مرورگر صفحهٔ قرمز نشان میدهد و کاربر پشت آن گیر کرده. اگر فقط «خطای SSL» میبینید و نمیدانید کدامیک از دهها حالت ممکن است، این متن برای همین لحظه نوشته شده. اول باید کد خطا را بخوانید، چون هر کد یک علت متفاوت دارد و درمانشان یکی نیست.
اول کد خطا را بخوانید، بعد دست به سرور ببرید
مرورگر هیچوقت دروغ نمیگوید، ولی خیلی خلاصه میگوید. در Chrome با زدن دکمهٔ «Advanced» یا در Firefox با «Advanced» یک خط کد میبینید. این کدها را حفظ کنید:
ERR_CERT_DATE_INVALIDیعنی گواهی منقضی شده یا ساعت سرور غلط است.ERR_CERT_COMMON_NAME_INVALIDیعنی نام دامنه با گواهی نمیخواند.ERR_CERT_AUTHORITY_INVALIDیعنی زنجیرهٔ گواهی ناقص است یا گواهی self-signed نصب شده.ERR_CERT_REVOKEDیعنی صادرکننده گواهی را باطل کرده.ERR_SSL_PROTOCOL_ERRORیعنی اصلاً TLS برقرار نشده، نه اینکه گواهی بد باشد.
تفاوت این دو دسته مهم است. سه مورد اول دربارهٔ خود گواهی است؛ مورد آخر دربارهٔ تنظیمات وبسرور. اگر این را قاطی کنید، ساعتها وقت تلف میکنید.
انقضای بیسروصدا؛ شایعترین خطای SSL در سایتهای ایرانی
گواهیهای Let's Encrypt نود روزه هستند و تمدید خودکارشان گاهی بیصدا میشکند. سایت شما تا دیروز سالم بود، امروز صبح همهچیز قرمز است. علت معمولاً یکی از اینهاست: کرونجاب تمدید اجرا نشده، پورت 80 بسته شده و چالش HTTP-01 رد شده، یا رکورد DNS عوض شده و اعتبارسنجی شکسته.
برای دیدن تاریخ انقضا از خود سرور:
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates -subject -issuer
خروجی notAfter را ببینید. اگر تاریخ گذشته، مشکل همین است. اگر تاریخ درست است ولی مرورگر همچنان خطا میدهد، بروید سراغ زنجیره.
ساعت سرور را چک کنید
یک بار دیدم سروری که ساعتش ۴۰ دقیقه عقب بود، گواهی سالم را منقضی میدید. با timedatectl status بررسی کنید و اگر System clock synchronized: no بود، systemd-timesyncd را راه بیندازید. این خطا در لاگها هیچ اثری ندارد و فقط در مرورگر ظاهر میشود.
زنجیرهٔ ناقص؛ خطایی که فقط در بعضی مرورگرها میآید
این حالت آزاردهنده است. سایت روی لپتاپ شما باز میشود، ولی مشتری با موبایل اندروید خطا میگیرد. علت: شما فایل fullchain.pem را نصب نکردهاید و فقط گواهی سرور را گذاشتهاید. مرورگرهای دسکتاپ گواهی میانی را از کش خودشان برمیدارند؛ کلاینتهای تازه این کش را ندارند.
برای دیدن زنجیره:
openssl s_client -connect example.com:443 -showcerts
اگر در خروجی فقط یک -----BEGIN CERTIFICATE----- دیدید، زنجیره ناقص است. در Nginx باید ssl_certificate به فایل fullchain اشاره کند، نه به فایل cert:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
در Apache هم SSLCertificateChainFile را جدا تنظیم کنید. اگر از پنل استفاده میکنید، دنبال گزینهٔ «نصب زنجیره» یا «CA Bundle» بگردید و محتوای chain.pem را همانجا بچسبانید.
عدم تطابق نام؛ وقتی گواهی برای دامنهٔ دیگری صادر شده
خطای ERR_CERT_COMMON_NAME_INVALID سه علت رایج دارد. اول، گواهی فقط برای example.com صادر شده ولی کاربر www.example.com را باز میکند. دوم، از گواهی wildcard استفاده کردهاید که فقط یک سطح زیردامنه را پوشش میدهد؛ *.example.com شامل shop.example.com میشود ولی a.shop.example.com را نمیگیرد. سوم، دامنه را عوض کردهاید و گواهی قدیمی روی سرور مانده.
راهحل درست، صدور گواهی با هر دو نام است. با certbot:
certbot certonly --nginx -d example.com -d www.example.com
اگر تعداد زیردامنهها زیاد است، wildcard بگیرید ولی حواستان باشد که به اعتبارسنجی DNS-01 نیاز دارد و باید رکورد TXT موقت اضافه کنید. اینجا اشتباه میکنند: رکورد TXT را اضافه میکنند، اعتبارسنجی موفق میشود، بعد رکورد را پاک نمیکنند و ماه بعد که تمدید میخواهد اجرا شود، شکست میخورد چون مقدار TXT عوض شده. نشانهاش این است که تمدید دستی کار میکند ولی خودکار نه.
خطای SSL در سمت سرور؛ جایی که مرورگر چیزی نمیگوید
گاهی سایت با HTTPS بالا نمیآید و مرورگر فقط timeout میدهد. اینجا باید از سمت سرور نگاه کنید. با curl -vI https://example.com ببینید handshake کجا متوقف میشود. اگر پیام no shared cipher دیدید، یعنی سرور فقط TLS 1.3 را قبول میکند و کلاینت قدیمی چیزی برای توافق ندارد.
پیکربندی متعادل برای Nginx:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
هزینهاش را بگویم: اگر TLS 1.0 و 1.1 را کامل ببندید، کاربرانی با اندروید ۴ یا ویندوز XP دیگر وصل نمیشوند. برای سایتهای داخلی با مخاطب قدیمی، این تصمیم سخت است. من در سایتهای عمومی TLS 1.2 را کف میگذارم؛ فقط اگر لاگ نشان داد بخش قابلتوجهی از ترافیک از کلاینتهای قدیمی میآید، 1.1 را هم باز میکنم. برای اکثر سایتهای امروزی، بستن نسخههای قدیمی درستتر است.
ابزارهایی که وقت شما را ذخیره میکنند
قبل از هر تغییری، از بیرون سرور تست بگیرید. ابزارهای بررسی DNS و شبکه به شما نشان میدهند رکورد A و AAAA به کدام IP اشاره میکنند؛ اگر AAAA به سروری اشاره کند که گواهی ندارد، مرورگرهای IPv6 خطا میگیرند و شما در لاگ IPv4 چیزی نمیبینید. این تلهٔ رایجی است.
برای پایش مداوم، یک اسکریپت ساده بنویسید که هر ساعت تاریخ انقضا را چک کند و اگر کمتر از ۱۴ روز بود هشدار بدهد. منتظر مرورگر نمانید. اگر سایت شما پشت WordPress است، بخشی از این خطاها از افزونههایی میآید که خودشان درخواست ناامن میسازند؛ در امنیت وردپرس؛ از wp-config تا افزونههایی که خودشان حفرهاند این موارد فهرست شده.
اگر سرور شما پشت لایهٔ محافظت DDoS است، مطمئن شوید گواهی روی همان لبه نصب شده، نه فقط روی سرور مبدأ. در غیر این صورت کاربر گواهی لبه را میبیند و اگر آنجا گواهی معتبر نباشد، خطا همانجا رخ میدهد. محافظت در برابر DDoS بدون گواهی درست، فقط یک لایهٔ اضافه است که مشکل تازه میسازد.
چه زمانی مشکل از شما نیست
اگر گواهی خودتان سالم است و همهچیز درست تنظیم شده ولی کاربران همچنان خطا میگیرند، احتمالاً مشکل سمت صادرکننده یا شبکهٔ کاربر است. اول وضعیت لحظهای سرویسها را ببینید. اگر سرویس سالم بود، از کاربر بخواهید کش مرورگر را پاک کند و ساعت دستگاهش را چک کند. گواهیهای ریشهای در بعضی دستگاههای قدیمی منقضی شدهاند و هیچ کاری از سمت شما برنمیآید جز تغییر صادرکننده.
برای سایتهایی که ترافیک حساس دارند، یک لایهٔ اضافه مثل VPN سازمانی برای دسترسی امن کارکنان منطقی است، ولی این ربطی به خطای SSL کاربر عادی ندارد. اشتباه نکنید که این دو را یکی ببینید.
پرسشهای پرتکرار
چرا گواهی SSL سایت من خودبهخود منقضی شد؟
چون تمدید خودکار اجرا نشده. Let's Encrypt گواهی نود روزه میدهد و اگر کرونجاب تمدید به هر دلیلی شکست بخورد، هیچ هشداری نمیبینید تا لحظهای که مرورگر خطا بدهد. با certbot renew --dry-run مسیر تمدید را تست کنید و خروجی را در لاگ ذخیره کنید.
تفاوت ERR_CERT_DATE_INVALID و ERR_CERT_AUTHORITY_INVALID چیست؟
اولی یعنی گواهی منقضی شده یا ساعت سرور غلط است. دومی یعنی مرورگر نمیتواند گواهی را به یک مرجع معتبر برساند، که معمولاً به معنی زنجیرهٔ ناقص یا گواهی self-signed است. درمان این دو کاملاً متفاوت است.
آیا میتوانم خطای SSL را با غیرفعال کردن بررسی در مرورگر رد کنم؟
فقط برای تست روی سیستم خودتان. این کار امنیت را کامل حذف میکند و اگر روی سرور یا در کد اپلیکیشن انجام شود، حملهٔ man-in-the-middle را ممکن میکند. هرگز در محیط تولید این کار را نکنید.
چرا سایت روی موبایل خطای SSL میدهد ولی روی لپتاپ نه؟
تقریباً همیشه به خاطر زنجیرهٔ ناقص است. مرورگر دسکتاپ گواهی میانی را از کش قبلی دارد، ولی دستگاه تازهای که قبلاً آن دامنه را ندیده، زنجیره را کامل نمیبیند و خطا میدهد. فایل fullchain را نصب کنید.
قدم بعدی ساده است: همین حالا openssl s_client را روی دامنه اجرا کنید و تاریخ انقضا و تعداد گواهیهای زنجیره را ببینید. اگر کمتر از ۱۴ روز مانده یا زنجیره یکتایی است، همان لحظه درستش کنید. بقیهٔ خطاها بعد از این دو مورد، تقریباً همیشه به تنظیمات وبسرور برمیگردند.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!