ملفات .env — 6 أخطاء تؤدي إلى تسريب أسرارك على GitHub

تحديث في

الإفراط في تسريب ملفات .env ليس ناتجًا عن مهاجمين — بل ناتجًا عن مطورين أرسلوا ملفات قبل إعداد .gitignore، أو أرسلوا ملف .env.example مع قيم حية، أو أذنوا لمنصة بسيطة بجمع أسرار الخادم في ملفات JavaScript للعميل. إليك الـ 6 أخطاء التي تحدث فعلاً.

ملفات .env — 6 أخطاء تُعرض أسرارك على GitHub 1
إعلان · حذف؟

تقرير "حالة التوسع في الأسرار" لـ GitGuardian لعام 2023 وجد أكثر من 12 مليون سر في مخازن GitHub العامة. لم تُسَرَّق معظم هذه الأسرار — بل تم تحميلها من قبل مطورين يعتقدون أنهم قد عاملوها بشكل صحيح. هذه هي الأنماط المسؤولة.

1. إضافة ملف .gitignore بعد الالتزام الأول

.gitignore إذا كانت الملفات مُضمنة بالفعل في سجل Git، فإضافة ملف إلى لا تحدث أي تغيير. سيستمر Git في تتبعه وسيُسجل تغييرات عليه. الملفات من عدم التسجيل. بمجرد أن يُدرج ملف — حتى لفترة قصيرة — فإنه يُدرج في تاريخ git. إذا أنشأت .env، وقمت بتشغيل git add . && git commit، ثم أضفت .env ل .gitignore بعد ذلك، فإن الملف يظل في كل التحديثات التي تسبق هذا التغيير.

تحقق مما إذا كان موجودًا في التاريخ:

git log --all -- .env

إذا عادت التحديثات، فإن الأسرار موجودة في التاريخ. ابدأ بتحديث المفاتيح أولاً. ثم أزل الملف من التاريخ باستخدام git-filter-repo (البديل الموصى به لـ git filter-branch):

pip install git-filter-repo
git filter-repo --path .env --invert-paths

أعد تحميل جميع المواقع البعيدة واطلب من الزملاء إعادة التحميل. توجد التحديثات في كل تحميل تم إجراؤه قبل التنظيف — بما في ذلك أنظمة CI التلقائية التي أخذت المخزن.

2. نسخ ملف .env إلى .env.example دون حذف القيم

العملية القياسية: أنشئ .env بقيم حقيقية، ثم نسخه إلى .env.example لإظهار الزملاء ما هي المفاتيح التي يحتاجها المشروع. يقع الخطأ في النسخ.

cp .env .env.example تُنسخ كل شيء — المفاتيح و القيم. و .env.example مُفترض أن تُدرج. وهذا هو الهدف من ذلك. تُدرج القيم الحقيقية في .env.example بشكل مقصود.

❌ ما ينتهي به المطاف في git:

DATABASE_URL=postgres://admin:supersecretpassword@prod-db.example.com/appdb
STRIPE_SECRET_KEY=sk_live_51AbcDefGhiJklMnopQrstUvwx...
JWT_SECRET=my-actual-production-jwt-secret

✅ ما .env.example يجب أن يبدو عليه:

DATABASE_URL=postgres://user:password@localhost:5432/appdb
STRIPE_SECRET_KEY=sk_live_YOUR_KEY_HERE
JWT_SECRET=generate-a-random-secret-min-32-chars

يخلق .env.example بقيم مُستخدمة أولاً، ثم أدرجها، ثم نسخها إلى .env وأدخل القيم الحقيقية — لا بالعكس.

3. تسجيل متغيرات process.env في معالجات الأخطاء

يبدأ هذا كمُراقبة سريعة أثناء حدث، ولا يُزال أبدًا. أو يُستخدم في مُعالجات الأخطاء العامة التي تبدو غير خطرة.

// Classic debug line that makes it to production
console.log('Starting with config:', process.env);

// Generic error handler that dumps everything
app.use((err, req, res, next) => {
  logger.error({ config: process.env, error: err.message });
  res.status(500).json({ error: 'Internal server error' });
});

process.env يحتوي على كل المتغيرات التي تم تحميلها من dotenv، بالإضافة إلى المتغيرات النظامية. إرسال الكائن الكامل إلى مُسجل الأخطاء يعني أن المحتوى يُدخل إلى مُجمّع السجلات، وخدمة تتبع الأخطاء (مثل Sentry، Datadog، Rollbar)، وربما إلى رسائل إشعار الأخطاء أو وصلات ويب. تُرسل العديد من هذه الخدمات إلى تخزين خارجي بتحكمات وصول خاصة.

سجل فقط القيم المطلوبة لتشخيص المشكلة:

logger.error({
  nodeEnv: process.env.NODE_ENV,
  appVersion: process.env.APP_VERSION,
  error: err.message,
  stack: err.stack
});

4. دمج الأسرار في طبقات صورة Docker

أثنان من الأنماط التي تُدخل الأسرار بشكل دائم إلى تاريخ صورة Docker:

# Pattern 1: COPY bakes the entire .env into a layer
COPY .env .

# Pattern 2: ARG/ENV burns values into build metadata
ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL

حتى لو حذفت الملف في طبقة لاحقة (RUN rm .env)، فإن القيمة لا تزال قابلة للقراءة في تاريخ الصورة. يمكن لأي شخص لديه صلاحية التحميل أن ينفذ:

docker history --no-trunc your-image:tag

ويمكنه استرداد القيم ARG المستخدمة في وقت البناء. الأسرار المُحددة في Docker BuildKit هي الأداة الصحيحة — فهي تُحمّل الأسرار أثناء البناء دون كتابتها في أي طبقة:

# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=db_url     DATABASE_URL=$(cat /run/secrets/db_url) ./setup.sh

لإدخال إعدادات التشغيل في وقت التشغيل، استخدم docker run -e أو environment: في Docker Compose، مع الإشارة إلى المتغيرات المُستمدة من الجهاز — لا قيم مُحددة، ولا COPY‘مُستندات سرية.

5. استخدام أسرار مُستخدمة ضعيفة تُرسل إلى البيئة الإنتاجية

JWT_SECRET=secret, SESSION_SECRET=keyboard cat, APP_KEY=changeme, ENCRYPTION_KEY=1234567890abcdefهذه تبدأ كمُستندات تطوير وغالبًا ما لا تُستبدَل. يحاول المهاجمون تجربة هذه السلاسل بطرق تجريبية — فهي موجودة في قوائم الكلمات لأنها تظهر في بحث GitHub.

يمكن تحليل JWT الموقعة بـ HS256 بمفتاح ضعيف باستخدام أدوات مثل c-jwt-cracker. يكفي تلقي مفتاح صالح واحد لتجربة المفتاح وصنع أذونات غير محدودة.

يجب أن تكون الأسرار عشوائية من حيث التشفير، وليست أقل من 32 بايت. أنشئها قبل الحاجة إليها — يُنتج مولد أسرار البيئة على IO Tools قيمًا عشوائية صحيحة لأساسيات .env (مفاتيح JWT، أسرار الجلسة، مفاتيح الواجهة) دون الحاجة إلى إعدادات. أنشئها من البداية؛ لا تستخدم مفتاحًا مُستندًا وخطط لـ "إصلاحه قبل الإنتاجية".

6. ممارسات متغيرات بيئة الإطار التي تُعرض الأسرار للعميل

تستخدم العديد من الإطارات الشهيرة تسمية متغيرات مسبقة لتحديد مدى إمكانية رؤيتها من قبل العميل أو الخادم. إذا أُخطئ هذا، فإن الأسرار تُرسل إلى ملفات JavaScript في كل متصفح يحمل تطبيقك — بشكل نصي.

  • Next.js: أي مسار يسمى NEXT_PUBLIC_المتغيرات المُسبقة بـ -prefixed تُدرج في جانب العميل. لكن الأسرار الخادمة تتسرب عند المرور عبر getServerSideProps props — أي قيمة تُعاد إلى props تُحول إلى HTML في الصفحة وتصبح قابلة للقراءة في المصدر.
  • Vite: المتغيرات المُسبقة بـ VITE_ تُدرج في ملف JavaScript للعميل. استخدام VITE_DATABASE_URL "للاستفادة من السهولة" هو خطأ يُمارسه المطورون فعليًا.
  • Create React App: الكل REACT_APP_ تُدرج المتغيرات في ملف العميل، دون استثناء. لا توجد عملية تشغيل خادم في CRA — كل ما يُحمّل يُرسل إلى المتصفح.

تحقق بعد البناء من خلال البحث في مجلد الناتج عن القيم المعروفة للسر:

grep -r "sk_live_" ./dist
grep -r "sk_live_" ./.next/static

إذا عادت أي نتائج، فإن الأسرار موجودة في كل علامة تبويب المتصفح. ابدأ بتحديثها فورًا وقم بفحص ما تم تضمينه أيضًا.

مُمارسة واحدة تستحق البناء من البداية

قبل إنشاء أي ملف يحتوي على أسرار، أنشئ .gitignore أولًا — وليس كمُستند متأخر. يجب أن يكون التحديث الأول في أي مخزن جديد هو .gitignore و .env.example بقيم مُستخدمة أولاً. سيُنتج منشئ .gitignore مُستند تجاهل مخصص للإطار في أقل من دقيقة.

الستة أخطاء المذكورة أعلاه جميعها يمكن تجنبها قبل كتابة أي كود. التحديث هو الحل الوحيد بعد أن تُظهر الأسرار — وهذا يعني التحديث في كل مكان: في مزود الخدمة، في كل بيئة تحتوي على نسخة، وفي كل نظام قد يحتفظ بالقيمة في السجلات.

هل تريد حذف الإعلانات؟ تخلص من الإعلانات اليوم

تثبيت ملحقاتنا

أضف أدوات IO إلى متصفحك المفضل للوصول الفوري والبحث بشكل أسرع

أضف لـ إضافة كروم أضف لـ امتداد الحافة أضف لـ إضافة فايرفوكس أضف لـ ملحق الأوبرا

وصلت لوحة النتائج!

لوحة النتائج هي طريقة ممتعة لتتبع ألعابك، يتم تخزين جميع البيانات في متصفحك. المزيد من الميزات قريبا!

إعلان · حذف؟
إعلان · حذف؟
إعلان · حذف؟

ركن الأخبار مع أبرز التقنيات

شارك

ساعدنا على الاستمرار في تقديم أدوات مجانية قيمة

اشتري لي قهوة
إعلان · حذف؟