خوادم الويب والوكلاء العكسيون

إنهاء TLS وإعداد HTTPS

18 دقيقة الدرس 5 من 28

إنهاء TLS وإعداد HTTPS

تشغيل بروتوكول HTTP النصي في بيئة الإنتاج ليس مجرد خيار في الإعداد — بل هو مسؤولية أمنية خطيرة. TLS هو الطبقة التشفيرية التي تُوثّق خادمك للعملاء وتشفّر كل بايت يُرسَل عبر الشبكة. في بنية DevOps الحديثة، يعمل Nginx دائمًا تقريبًا نقطةَ إنهاء TLS: يفكّ تشفير حركة مرور HTTPS الواردة، ثم يُحيل طلبات HTTP غير المشفّرة إلى الخلفيات التطبيقية العاملة على localhost أو شبكة داخلية لا تحتاج إلى تشفير إضافي.

لن يقتصر هذا الدرس على "أضف سطر ssl_certificate وانتهى." ستتعلم كيف تحصل على الشهادات، وتكتب كتلة TLS احترافية في Nginx، وتُطبّق إعادة التوجيه، وتُعدّ HSTS، وتضبط مجموعات التشفير، وتتجنب أعطال الإنتاج الشائعة التي تُضعف الأمان بصمت أو تتسبب في أخطاء اتصال العميل في الساعة الثالثة صباحًا.

كيف يعمل إنهاء TLS

TLS termination request flow Client Browser / curl HTTPS :443 encrypted Nginx TLS Termination • Decrypts TLS • Validates cert • Forwards plain HTTP HTTP :8080 plaintext (LAN) App Backend Node / Gunicorn / PHP-FPM App Backend Node / Gunicorn / PHP-FPM Certificate Store cert.pem + key.pem
ينتهي TLS عند Nginx؛ تستقبل الخلفيات حركة مرور غير مشفّرة عبر مسار داخلي موثوق.

الحصول على الشهادات باستخدام Certbot (Let's Encrypt)

تُصدر Let's Encrypt شهادات DV (التحقق من النطاق) مجانية صالحة لمدة 90 يومًا وتثق بها جميع المتصفحات الرئيسية. يُؤتمت certbot عملية الإصدار والتجديد. للبدء على خادم جديد:

# تثبيت certbot وإضافة Nginx على Debian/Ubuntu sudo apt install -y certbot python3-certbot-nginx # إصدار الشهادة والسماح لـ certbot بتعديل إعداد Nginx تلقائيًا sudo certbot --nginx -d example.com -d www.example.com # التحقق من عمل التجديد التلقائي (يعمل مرتين يوميًا) sudo certbot renew --dry-run # حالة مؤقت التجديد sudo systemctl status certbot.timer
نصيحة إنتاجية — شهادات Wildcard: للنطاقات الفرعية الكثيرة، استخدم شهادة wildcard (*.example.com) عبر تحدي DNS-01 بدلًا من HTTP-01. يتطلب ذلك إضافة مزود DNS (مثل certbot-dns-cloudflare) لكنه يُلغي الحاجة إلى خادم HTTP متاح للعموم أثناء التجديد، وهو أمر بالغ الأهمية للخدمات الداخلية. احفظ شهادات Wildcard مركزيًا ووزّعها على جميع عقد Nginx عبر مدير أسرار أو أداة شهادات مخصصة مثل step-ca.

كتلة TLS احترافية في Nginx

لا تقبل الإعداد الأدنى الذي "يعمل فقط". لكل إعداد أدناه سبب واضح ومحدد.

# /etc/nginx/sites-available/example.com # 1. إعادة توجيه HTTP إلى HTTPS دون استثناء server { listen 80; listen [::]:80; server_name example.com www.example.com; # إعادة توجيه دائمة (301) حتى تُخزّنها المتصفحات وتتوقف عن إرسال HTTP return 301 https://$host$request_uri; } # 2. كتلة خادم HTTPS server { listen 443 ssl; listen [::]:443 ssl; http2 on; # تعدد إرسال HTTP/2 (صياغة Nginx >= 1.25.1) server_name example.com www.example.com; # --- مسارات الشهادة (تُدار بواسطة certbot) --- ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # --- إصدارات البروتوكول: TLS 1.2 كحد أدنى، TLS 1.3 مُفضّل --- ssl_protocols TLSv1.2 TLSv1.3; # --- مجموعات التشفير: ذات السرية التامة للأمام فقط --- ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:' 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:' 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:' 'DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; ssl_prefer_server_ciphers off; # TLS 1.3 يتجاهل هذا؛ الإعداد الجيد لـ 1.2 # --- استئناف الجلسة: تقليل تكلفة المصافحة --- ssl_session_cache shared:SSL:10m; # حوالي 40,000 جلسة مشتركة بين العمال ssl_session_timeout 1d; ssl_session_tickets off; # التذاكر تُضعف السرية التامة؛ أوقفها # --- OCSP Stapling: تقديم حالة إلغاء الشهادة بشكل مضمّن --- ssl_stapling on; ssl_stapling_verify on; resolver 1.1.1.1 8.8.8.8 valid=300s; resolver_timeout 5s; # --- HSTS: أخبر المتصفحات باستخدام HTTPS دائمًا لمدة عام --- add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # --- رؤوس أمان إضافية --- add_header X-Frame-Options DENY always; add_header X-Content-Type-Options nosniff always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; root /var/www/example.com/public; index index.html; location / { try_files $uri $uri/ =404; } }
لماذا ssl_session_tickets off؟ تُشفّر تذاكر الجلسة حالتها باستخدام مفتاح جانب الخادم. إذا اختُرق هذا المفتاح يومًا، يستطيع المهاجم فكّ تشفير كل حركة المرور السابقة (لا سرية تامة للأمام). تعطيل التذاكر يجبر Nginx على استخدام ذاكرة التخزين المؤقت للجلسة الداخلية، التي تدور بشكل طبيعي. معظم إعدادات الإنتاج في Google وCloudflare وMozilla تُعطّل التذاكر لهذا السبب.

HTTP Strict Transport Security (HSTS)

HSTS هو رأس استجابة HTTP يأمر المتصفحات بعدم الاتصال بنطاقك عبر HTTP غير المشفّر مطلقًا — حتى للطلب الأول بعد انتهاء max-age. بمجرد أن يرى المتصفح رأس HSTS، سيُعيد توجيه http:// داخليًا إلى https:// قبل إرسال أي بيانات على السلك، مما يُلغي نافذة هجمات SSL-stripping.

  • max-age=31536000 — سنة واحدة؛ يتذكر المتصفح هذا لمدة 365 يومًا بعد آخر استجابة.
  • includeSubDomains — يُمدّد السياسة لكل نطاق فرعي. أضفه فقط بعد التأكد من أن كل نطاق فرعي يملك شهادة سارية.
  • preload — يُدرج نطاقك في قوائم HSTS المحملة مسبقًا في المتصفحات (Chrome وFirefox وSafari). قدّم الطلب على hstspreload.org. هذا إجراء شبه دائم — قد يستغرق الإزالة أشهرًا.
قائمة HSTS preload باب في اتجاه واحد. إذا أضفت preload وقدّمت طلبًا للقائمة ثم احتجت لاحقًا للعودة إلى HTTP (مثلًا لنطاق تجريبي)، سترفض المتصفحات الاتصال لمدة تصل إلى عام. لا تُفعّل preload إلا على النطاقات التي أنت متأكد تمامًا من أنها ستخدم HTTPS إلى الأبد.

التحقق من إعداد TLS

بعد تطبيق الإعداد، اختبر قبل افتراض الصحة. فحصان أساسيان:

# 1. فحص صياغة إعداد Nginx — شغّله دائمًا قبل إعادة التحميل sudo nginx -t # 2. إعادة التحميل (دون توقف) بعد الاختبار النظيف sudo systemctl reload nginx # 3. اختبار مصافحة TLS من سطر الأوامر openssl s_client -connect example.com:443 -servername example.com < /dev/null # 4. التحقق من البروتوكول ومجموعة التشفير المُتفَق عليها curl -vI https://example.com 2>&1 | grep -E "SSL|TLSv|cipher" # 5. تقييم الإعداد خارجيًا (من جهاز المطوّر) # زر: https://www.ssllabs.com/ssltest/analyze.html?d=example.com # الهدف: تقييم A+. أسباب شائعة لخسارة النقاط: # - TLS 1.0/1.1 لا يزال مُفعّلًا → أزله من ssl_protocols # - تذاكر الجلسة مُفعّلة → أضف ssl_session_tickets off # - HSTS غائب → أضف الرأس # - OCSP stapling لا يعمل → افحص resolver و ssl_stapling_verify

أنماط الفشل الشائعة

  • تحذيرات المحتوى المختلط — يُحمَّل HTML عبر HTTPS لكنه يتضمن عناوين موارد http://. يحجبها المتصفح أو يُنبّه عنها. الحل: تأكد من أن جميع عناوين الأصول تستخدم https:// أو // المستقلة عن البروتوكول. أعدّ إطار تطبيقك لتوليد عناوين HTTPS خلف proxy (APP_URL=https://...، أو URL::forceScheme('https') في Laravel).
  • سلسلة الشهادة غير مكتملة — يجب أن تُرسل الخوادم السلسلة الكاملة (الورقة + الوسيطات). استخدم fullchain.pem لا cert.pem. عملاء الجوال بالذات يفشلون بصمت عند غياب الشهادة الوسيطة.
  • انتهاء صلاحية الشهادة — تنتهي شهادات Let's Encrypt بعد 90 يومًا. يُجدّد مؤقت systemd لـ Certbot في اليوم 60، لكن إذا توقف المؤقت (إعادة تشغيل النظام مع فشل الخدمات)، لديك 60 يومًا لاكتشاف المشكلة. راقب الانتهاء: certbot certificates أو مراقب خارجي مثل فحص SSL في UptimeRobot.
  • خطأ في server_name — يخدم Nginx أول كتلة server مطابقة إذا لم يتطابق server_name، مما قد يُقدّم شهادة خاطئة. ضع دائمًا خادمًا افتراضيًا صريحًا أو تحقق من ترتيب الكتل.
أتمتة مراقبة الشهادات في CI/CD: أضف خطوة ما بعد النشر تُشغّل openssl s_client -connect $HOST:443 < /dev/null 2>&1 | openssl x509 -noout -dates وتُنبّه إذا كان notAfter خلال 30 يومًا. يكتشف هذا فشل التجديد قبل أن يفعل المستخدمون.