HTTPS و گواهی، ساده
قفل کنار آدرس چه چیزی را تضمین میکند و چه چیزی را نه: سه وعدهی HTTPS (محرمانگی، درستی، هویت)، TLS و دستدادنش، گواهی دیجیتال و فیلدهایش، مرجع صدور (CA) و زنجیرهی اعتماد، ساختن یک گواهی خودامضا و یک CA آزمایشی با openssl، خطاهای self-signed و نام نادرست، دیدن چیزهایی که حتی با HTTPS لو میرود (اسم سایت)، و Let's Encrypt.
- حدود ۲۸ دقیقه مطالعه
- ۷ مثال اجراشده
- ۳ تمرین با جواب
- مقدماتی (از صفر مطلق)
- بهروزرسانی: ۲۹ مهر ۱۴۰۵
توی این درس یاد میگیری قفل کوچک کنار آدرس سایت دقیقاً چه معنایی دارد. HTTPS همان HTTP درس قبل است که داخل یک «تونل» رمزنگاریشده به اسم TLS حرکت میکند. سه چیزی را که HTTPS تضمین میکند (و چیزهایی را که تضمین نمیکند) میشناسی، با گواهی دیجیتال و مرجع صدور (CA) آشنا میشوی، خودت یک گواهی میسازی و یک سرور HTTPS روی کامپیوترت بالا میآوری، خطاهای معروف مرورگر را با چشم خودت میسازی و میفهمی، و در آخر یک CA کوچک آزمایشی درست میکنی تا ببینی «اعتماد» در اینترنت چطور کار میکند.
مسئله: HTTP مثل کارتپستال است
Section titled “مسئله: HTTP مثل کارتپستال است”در درس HTTP دیدی همهچیز متن ساده است: آدرس، کوکیها، فرمها، رمزی که تایپ میکنی. هر کس در مسیر (صاحب Wi-Fi کافه، هر روتری در راه، یا هر کسی که توانسته بین تو و سرور قرار بگیرد) همه را میخواند، و بدتر، میتواند عوض کند: یک تبلیغ در صفحه بگذارد، شمارهحساب را عوض کند، یا خودش را جای سایت جا بزند. HTTPS برای این سه مشکل است.
تشبیه: پاکت مهرومومشده با کارت شناسایی
Section titled “تشبیه: پاکت مهرومومشده با کارت شناسایی”HTTP یعنی کارتپستال: هر کس در مسیر میخواندش و میتواند رویش بنویسد. HTTPS یعنی:
- پاکت دربسته (محرمانگی): نامه داخل پاکتی است که فقط گیرنده میتواند باز کند.
- مهروموم (درستی): اگر کسی در راه پاکت را باز کند یا چیزی عوض کند، مهر میشکند و گیرنده میفهمد.
- کارت شناسایی (هویت): قبل از تحویل، طرف مقابل یک کارت شناسایی (گواهی) نشان میدهد که یک ادارهی معتبر (CA) صادر کرده و رویش نوشته «این واقعاً بانک ملت است». بدون این، ممکن است پاکت را با خیال راحت دست یک کلاهبردار بدهی؛ رمزنگاری کامل، با آدم اشتباه.
سومی را خیلیها فراموش میکنند و مهمترینش است.
TLS در چند جمله
Section titled “TLS در چند جمله”TLS (Transport Layer Security؛ نسخهی قدیمیاش SSL نام داشت و هنوز این اسم را زیاد میشنوی) قبل از اینکه حتی یک بایت HTTP رد و بدل شود، یک دستدادن (handshake) انجام میدهد:
ایدههای درس رمز و هش اینجا کنار هم مینشینند: سرور با کلید خصوصیاش امضا میکند تا ثابت کند صاحب گواهی است؛ دو طرف با یک روش ریاضی (Diffie-Hellman) یک کلید متقارن مشترک میسازند بدون اینکه خود کلید از شبکه رد شود؛ و بقیهی گفتوگو با رمزنگاری متقارن سریع (مثل AES) انجام میشود. هر پیام هم یک «مهر» (کد اعتبارسنجی) دارد که هر تغییری را لو میدهد.
گواهی دیجیتال چیست؟
Section titled “گواهی دیجیتال چیست؟”گواهی (certificate) یک فایل کوچک است که میگوید: «کلید عمومی X مال سایت shop.example.com است، از فلان تاریخ تا فلان تاریخ، و من، مرجع Y، این را امضا میکنم». فیلدهای اصلی:
| فیلد | معنی |
|---|---|
| Subject / SAN | گواهی برای کدام اسمها (Subject Alternative Name: فهرست دامنهها) |
| Issuer | چه کسی صادرش کرده (CA) |
| Validity | از کی تا کی معتبر است |
| Public key | کلید عمومی سرور |
| Signature | امضای CA روی همهی موارد بالا |
مرورگر از کجا به CA اعتماد میکند؟ سیستمعامل و مرورگر یک فهرست از پیش نصبشده از CAهای مورد اعتماد (گواهیهای ریشه) دارند. هر گواهی سایت باید با یک زنجیره از امضاها به یکی از این ریشهها برسد.
مثالهای عملی
Section titled “مثالهای عملی”همهی مثالها در پوشهی ~/tls و روی کامپیوتر خودت انجام میشوند:
mkdir -p ~/tls && cd ~/tlsمثال ۱: یک گواهی خودامضا
Section titled “مثال ۱: یک گواهی خودامضا”خودامضا (self-signed) یعنی گواهیای که هیچ CAی امضایش نکرده؛ خودش، خودش را تأیید کرده. مثل کارت شناسایی که خودت در خانه چاپ کردهای. برای آزمایش خوب است:
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \ -keyout server.key -out server.crt -days 30 \ -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"ls -l-----total 8-rw-rw-r-- 1 ali ali 607 Oct 22 00:33 server.crt-rw------- 1 ali ali 241 Oct 22 00:33 server.keyیک دستور بلند، ولی هر بخشش ساده است: req -x509 (یک گواهی بساز)، -newkey ec ... (یک جفت کلید جدید از نوع منحنی بیضوی، کوتاه و سریع)، -nodes (کلید خصوصی را با رمز قفل نکن، برای اینکه سرور بتواند بدون پرسیدن بخواندش)، ۳۰ روز اعتبار، برای اسم localhost. (\ آخر خط یعنی «دستور در خط بعد ادامه دارد».) دو فایل ساخته شد:
server.crtگواهی، شامل کلید عمومی: آزادانه به همه داده میشود.server.keyکلید خصوصی: مجوزش-rw-------است (فقط صاحبش میخواند؛ دورهی لینوکس، درس مجوزها). کسی که این را داشته باشد، میتواند خودش را جای سایت تو جا بزند.
داخل گواهی را بخوان:
openssl x509 -in server.crt -noout -subject -issuer -dates -ext subjectAltNamesubject=CN = localhostissuer=CN = localhostnotBefore=Oct 21 21:03:40 2026 GMTnotAfter=Nov 20 21:03:40 2026 GMTX509v3 Subject Alternative Name: DNS:localhostsubject و issuer یکیاند: همان «خودامضا». notBefore و notAfter بازهی اعتبار (به وقت گرینویچ). فایلها خودشان متناند (head -3 server.crt را امتحان کن): -----BEGIN CERTIFICATE----- و بعد بایتها با Base64 (درس قبل).
مثال ۲: یک سرور HTTPS
Section titled “مثال ۲: یک سرور HTTPS”همان وبسرور پایتون درس کلاینت و سرور، با چند خط اضافه برای TLS. فایل ~/tls/https_server.py:
import http.server, ssl
server = http.server.HTTPServer(("127.0.0.1", 8443), http.server.SimpleHTTPRequestHandler)ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)ctx.load_cert_chain("server.crt", "server.key")server.socket = ctx.wrap_socket(server.socket, server_side=True)print("serving https://localhost:8443 ...")server.serve_forever()echo "<h1>صفحهی امن</h1>" > index.htmlpython3 https_server.pyدر ترمینال دیگر (و در همان پوشه):
cd ~/tlscurl -sS https://localhost:8443/echo "exit=$?"curl: (60) SSL certificate problem: self-signed certificateMore details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could notestablish a secure connection to it. To learn more about this situation andhow to fix it, please visit the web page mentioned above.exit=60curl قبول نکرد. ارتباط را میشد رمزنگاری کرد، ولی هیچ CAی تأیید نکرده که این سرور واقعاً «localhost» است. مرورگر هم همین را با صفحهی قرمز «Your connection is not private» (کد NET::ERR_CERT_AUTHORITY_INVALID) نشان میدهد. این دقیقاً همان کاری است که باید بکند: بدون هویت، رمزنگاری بیمعنی است.
مثال ۳: دو راه برای «قبول کردن»
Section titled “مثال ۳: دو راه برای «قبول کردن»”راه غلط (ولی رایج در آزمایش): به curl بگو بررسی نکند:
curl -sSk https://localhost:8443/<h1>صفحهی امن</h1>-k (--insecure) یعنی «هر گواهیای قبول است». ارتباط رمزنگاریشده است، ولی ممکن است با هر کسی باشد؛ پاکت دربسته، تحویل به یک غریبه. برای آزمایش روی کامپیوتر خودت اشکالی ندارد؛ در اسکریپتها و برنامههای واقعی هرگز.
راه درست: به curl بگو دقیقاً به این گواهی اعتماد کند:
curl -sv --cacert server.crt https://localhost:8443/* Connected to localhost (127.0.0.1) port 8443* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):* TLSv1.3 (IN), TLS handshake, Certificate (11):* TLSv1.3 (IN), TLS handshake, CERT verify (15):* TLSv1.3 (IN), TLS handshake, Finished (20):* TLSv1.3 (OUT), TLS handshake, Finished (20):* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / id-ecPublicKey* Server certificate:* subject: CN=localhost* start date: Oct 21 21:03:40 2026 GMT* expire date: Nov 20 21:03:40 2026 GMT* subjectAltName: host "localhost" matched cert's "localhost"* issuer: CN=localhost* SSL certificate verify ok.> GET / HTTP/1.1> Host: localhost:8443...< HTTP/1.0 200 OK...<h1>صفحهی امن</h1>حالا دستدادن TLS را خط به خط میبینی، همان نمودار بالا: Client hello، Server hello، Certificate (سرور گواهیاش را فرستاد)، CERT verify (با کلید خصوصی امضا کرد که صاحبش است)، Finished. خط SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 میگوید: نسخهی TLS 1.3، رمزنگاری متقارن AES با کلید ۲۵۶ بیتی، و توافق کلید با روش X25519. بعد بررسی هویت: اسم جور است (host "localhost" matched) و SSL certificate verify ok. فقط بعد از همهی اینها، همان درخواست HTTP درس قبل (GET / HTTP/1.1) داخل تونل رفت.
مثال ۴: اسم اشتباه
Section titled “مثال ۴: اسم اشتباه”گواهی برای localhost است. با آدرس دیگری از همان سرور وصل شو:
curl -sS --cacert server.crt https://127.0.0.1:8443/curl: (60) SSL: no alternative certificate subject name matches target host name '127.0.0.1'More details here: https://curl.se/docs/sslcerts.html...همان سرور، همان گواهی، ولی گواهی میگوید «من مال localhost هستم» و تو 127.0.0.1 خواستهای. curl رد کرد. در مرورگر: NET::ERR_CERT_COMMON_NAME_INVALID. این بررسی چیزی است که جلوی جا زدن را میگیرد: مهاجمی که برای evil.com گواهی معتبر گرفته، نمیتواند با آن خودش را bank.ir جا بزند. رایجترین دلیل این خطا در دنیای واقعی: گواهی برای example.com گرفتهای و کاربر به www.example.com رفته (یا برعکس)؛ گواهی باید همهی اسمها را در SAN داشته باشد.
مرورگر برای سایت شرکت خطای «certificate is not valid for www.shop.ir» میدهد، ولی shop.ir درست باز میشود. مشکل چیست؟
جواب درست: گواهی فقط برای shop.ir صادر شده و www.shop.ir در فهرست اسمهایش نیست
هر اسم باید در SAN گواهی باشد؛ گواهی را برای هر دو اسم بگیر.
مثال ۵: چه چیزی با HTTPS هم لو میرود؟
Section titled “مثال ۵: چه چیزی با HTTPS هم لو میرود؟”با nc (درس پورت) خودت را جای سرور بگذار و ببین مرورگر/curl قبل از رمزنگاری چه میفرستد. در ترمینال سرور (اول سرور HTTPS را با Ctrl+C ببند):
nc -l 127.0.0.1 8444 > hello.binو در ترمینال دیگر یک درخواست HTTPS با یک کوکی و یک «شمارهکارت» در آدرس:
curl -sk -m 1 -H "Cookie: session=abc123" "https://localhost:8444/orders?card=6037991234567890"بعد ببین چه رسیده (strings متنهای قابلخواندن داخل یک فایل دودویی را پیدا میکند):
ls -l hello.binstrings -n 5 hello.bingrep -c card hello.bin-rw-rw-r-- 1 ali ali 517 Oct 22 00:34 hello.bin localhosthttp/1.10۵۱۷ بایت رسید: پیام Client hello. داخلش فقط دو چیز خواناست: اسم سایت (localhost) و پروتکلی که میخواهد (http/1.1). نه مسیر، نه card، نه کوکی؛ اینها فقط بعد از دستدادن و داخل تونل فرستاده میشوند. اسم سایت در فیلدی به اسم SNI (Server Name Indication) بدون رمزنگاری فرستاده میشود تا سروری که چند سایت دارد بداند کدام گواهی را نشان دهد. پس با HTTPS:
| در مسیر دیده میشود | در مسیر دیده نمیشود |
|---|---|
| IP سرور (درس شبکه) | مسیر و query (/orders?card=...) |
| اسم سایت (SNI) و سؤال DNS | هدرها و کوکیها |
| زمان و حجم داده | محتوای صفحه، فرمها، رمزها |
یعنی صاحب Wi-Fi میفهمد به کدام سایت رفتهای، ولی نمیفهمد آنجا چه کردی.
مثال ۶: یک CA کوچک، زنجیرهی اعتماد
Section titled “مثال ۶: یک CA کوچک، زنجیرهی اعتماد”در دنیای واقعی، سایت گواهی را از یک CA میگیرد و مرورگرها به CA اعتماد دارند، نه به تکتک سایتها. همین را در کوچک بساز. اول یک CA (یک کلید و یک گواهی خودامضا که نقش «ادارهی صدور» را دارد):
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \ -keyout ca.key -out ca.crt -days 365 -subj "/CN=LoopX Test CA"حالا سایت myshop.test یک کلید میسازد و یک درخواست گواهی (CSR) به CA میدهد. در CSR کلید عمومی و اسم هست، نه کلید خصوصی:
openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \ -keyout shop.key -out shop.csr -subj "/CN=myshop.test"printf "subjectAltName=DNS:myshop.test,DNS:www.myshop.test\n" > san.extCA درخواست را امضا میکند (در دنیای واقعی، قبلش بررسی میکند که درخواستکننده واقعاً صاحب دامنه است):
openssl x509 -req -in shop.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -out shop.crt -days 90 -extfile san.extopenssl x509 -in shop.crt -noout -subject -issuer -dates -ext subjectAltNameCertificate request self-signature oksubject=CN = myshop.testsubject=CN = myshop.testissuer=CN = LoopX Test CAnotBefore=Oct 21 21:04:08 2026 GMTnotAfter=Jan 19 21:04:08 2027 GMTX509v3 Subject Alternative Name: DNS:myshop.test, DNS:www.myshop.testحالا issuer با subject فرق دارد: گواهی سایت را CA امضا کرده. بررسی زنجیره:
openssl verify -CAfile ca.crt shop.crtopenssl verify -CAfile server.crt shop.crtshop.crt: OKCN = myshop.testerror 20 at 0 depth lookup: unable to get local issuer certificateerror shop.crt: verification failedبا CA درست: OK. با یک «CA» بیربط: رد. سرور را با گواهی جدید روشن کن. فایل https_server.py را کپی کن و فقط اسم فایلها و پورت را عوض کن:
sed -e "s/server.crt/shop.crt/; s/server.key/shop.key/; s/8443/8445/" https_server.py > shop_server.pypython3 shop_server.pyو از ترمینال دیگر (--resolve یعنی «فرض کن این اسم به این IP است»، مثل یک خط موقت /etc/hosts در درس DNS):
cd ~/tlscurl -sS --resolve myshop.test:8445:127.0.0.1 https://myshop.test:8445/curl -sS --cacert ca.crt --resolve myshop.test:8445:127.0.0.1 https://myshop.test:8445/curl -sS --cacert ca.crt --resolve www.myshop.test:8445:127.0.0.1 https://www.myshop.test:8445/curl: (60) SSL certificate problem: unable to get local issuer certificateMore details here: https://curl.se/docs/sslcerts.html...<h1>صفحهی امن</h1><h1>صفحهی امن</h1>بدون معرفی CA، curl رد کرد (این CA در فهرست سیستم نیست). با --cacert ca.crt، هر دو اسم کار کردند. نکتهی بزرگ: curl هرگز shop.crt را جداگانه «نمیشناخت»؛ فقط به CA اعتماد کرد و هر گواهیای که CA امضا کرده، خودبهخود قبول شد. اینترنت هم دقیقاً همینطور کار میکند، فقط با چند ده CA معروف بهجای LoopX Test CA:
ls /etc/ssl/certs | grep -E "ISRG|DigiCert_Global_Root_G2"DigiCert_Global_Root_G2.pemISRG_Root_X1.pemISRG_Root_X2.pemاینها چند تا از گواهیهای ریشهی نصبشده در Ubuntu هستند (ISRG همان سازمان Let’s Encrypt است). اگر کسی بتواند یک CA جعلی را به این فهرست روی کامپیوتر تو اضافه کند (مثلاً یک بدافزار، یا «نرمافزار امنیتی» یک شبکهی اداری)، میتواند برای هر سایتی گواهی بسازد و ارتباط HTTPS تو را باز کند. برای همین نصب «گواهی ریشه» که کسی برایت فرستاده، تصمیم بسیار جدی است.
مثال ۷: گواهی سایتهای واقعی و تاریخ انقضا
Section titled “مثال ۷: گواهی سایتهای واقعی و تاریخ انقضا”گواهی یک سایت واقعی را با openssl s_client (کلاینت TLS خام) بخوان:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \ | openssl x509 -noout -subject -issuer -datessubject=C = US, ST = California, L = Los Angeles, O = Internet Corporation for Assigned Names and Numbers, CN = *.example.comissuer=C = US, O = DigiCert Inc, CN = DigiCert Global G3 TLS ECC SHA384 2020 CA1notBefore=Jan 15 00:00:00 2026 GMTnotAfter=Jan 15 23:59:59 2027 GMT*.example.com یک گواهی wildcard است: برای هر زیردامنهی یکسطحی (www.example.com، api.example.com). صادرکننده یک CA میانی DigiCert است. گواهیها تاریخ انقضا دارند و این مدت مدام کوتاهتر شده: امروز حداکثر حدود یک سال، و گواهیهای رایگان Let’s Encrypt فقط ۹۰ روز (برای همین تمدیدشان باید خودکار باشد). برای اینکه بدانی گواهی تا یک ماه دیگر منقضی میشود یا نه:
openssl x509 -in shop.crt -noout -checkend $((100*86400)); echo "exit=$?"openssl x509 -in shop.crt -noout -checkend $((30*86400)); echo "exit=$?"Certificate will expireexit=1Certificate will not expireexit=0-checkend ثانیه میگوید «تا اینقدر دیگر منقضی میشود؟» ($((100*86400)) حساب ۱۰۰ روز به ثانیه است). گواهی ۹۰ روزه تا ۱۰۰ روز دیگر منقضی میشود ولی تا ۳۰ روز دیگر نه. از همین کد خروج در یک اسکریپت cron برای هشدار قبل از انقضا استفاده میشود.
پشت پرده: قفل چه چیزی را تضمین نمیکند
Section titled “پشت پرده: قفل چه چیزی را تضمین نمیکند”| HTTPS تضمین میکند | HTTPS تضمین نمیکند |
|---|---|
| کسی در مسیر محتوا را نمیخواند | که سایت صادق یا قانونی است |
| کسی در مسیر محتوا را عوض نمیکند | که سایت همان سایتی است که فکر میکنی (examp1e-bank.ir با رقم ۱ بهجای l هم میتواند گواهی معتبر داشته باشد) |
| طرف مقابل صاحب واقعی این اسم دامنه است | که سرور هک نشده یا دادهی تو را درست نگه میدارد |
| که کامپیوتر خودت بدافزار ندارد |
بیشتر سایتهای فیشینگ (سایت جعلی که شبیه بانک ساخته شده) امروز HTTPS و قفل دارند، چون گرفتن گواهی برای دامنهای که خودشان خریدهاند رایگان و خودکار است. قفل یعنی «ارتباط تو با این دامنه امن است»، نه «این دامنه قابلاعتماد است». همیشه اسم دامنه را نگاه کن.
برای سرورهای خودت، گرفتن گواهی واقعی با Let’s Encrypt رایگان و خودکار است: ابزار certbot ثابت میکند دامنه مال توست (مثلاً با گذاشتن یک فایل موقت روی سایت که Let’s Encrypt از اینترنت میخواندش)، گواهی را میگیرد، وبسرور را تنظیم میکند و هر دو ماه خودکار تمدید میکند. این کار را در دورهی nginx، درس HTTPS با Let’s Encrypt، قدمبهقدم انجام میدهی.
جدولهای مرجع
Section titled “جدولهای مرجع”| خطای مرورگر / curl | معنی |
|---|---|
ERR_CERT_AUTHORITY_INVALID / self-signed certificate / unable to get local issuer certificate |
گواهی به CA مورد اعتمادی نمیرسد |
ERR_CERT_COMMON_NAME_INVALID / no alternative certificate subject name matches |
گواهی برای اسم دیگری است |
ERR_CERT_DATE_INVALID / certificate has expired |
تاریخ گذشته (یا ساعت کامپیوتر تو غلط است!) |
ERR_SSL_PROTOCOL_ERROR |
سرور TLS درست حرف نمیزند (مثلاً پورت HTTP معمولی است) |
| دستور | کار |
|---|---|
openssl req -x509 -newkey ec ... -keyout k -out c |
گواهی خودامضا |
openssl x509 -in c -noout -subject -issuer -dates |
خواندن گواهی |
openssl verify -CAfile ca.crt c |
بررسی زنجیره |
openssl x509 -in c -noout -checkend N |
انقضا تا N ثانیهی دیگر؟ |
openssl s_client -connect host:443 -servername host |
دیدن گواهی یک سایت |
curl -v https://... |
دیدن دستدادن و بررسی |
curl --cacert ca.crt |
اعتماد به یک CA مشخص |
curl -k |
بیخیال بررسی (فقط آزمایش!) |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) -k و «Proceed anyway» بهعنوان راهحل
Section titled “۱) -k و «Proceed anyway» بهعنوان راهحل”خطای گواهی را با curl -k یا کلیک روی «ادامه» دور زدن، دقیقاً همان کاری است که مهاجم میخواهد. راهحل: علت را پیدا کن (اسم، تاریخ، CA)؛ برای سرورهای داخلی، CA داخلی و --cacert.
۲) گواهی برای یک اسم، سایت روی چند اسم
Section titled “۲) گواهی برای یک اسم، سایت روی چند اسم”راهحل: همهی اسمها (example.com، www.example.com) در SAN، یا wildcard.
۳) انقضای فراموششده
Section titled “۳) انقضای فراموششده”سایت یک صبح با خطای ERR_CERT_DATE_INVALID بالا میآید. راهحل: تمدید خودکار (certbot) و یک بررسی جدا با -checkend که هشدار بدهد.
۴) کلید خصوصی بیمحافظ
Section titled “۴) کلید خصوصی بیمحافظ”server.key با مجوز 644، در گیت، یا فرستاده با ایمیل. راهحل: مجوز 600، فقط روی سرور، و اگر لو رفت، باطل کردن گواهی و ساختن کلید جدید.
۵) «قفل دارد پس امن است»
Section titled “۵) «قفل دارد پس امن است»”راهحل: قفل فقط ارتباط را امن میکند؛ اسم دامنه را با دقت بخوان و رمز را فقط در سایتی بزن که خودت آدرسش را تایپ کردهای یا از قبل نشانهگذاری کردهای.
گواهی shop.crt مثال ۶ را بخوان و بگو: برای کدام اسمهاست، چه کسی صادرش کرده و کی منقضی میشود. بعد بگو آیا برای api.myshop.test هم معتبر است.
دیدن جواب
openssl x509 -in shop.crt -noout -subject -issuer -enddate -ext subjectAltNamesubject=CN = myshop.testissuer=CN = LoopX Test CAnotAfter=Jan 19 21:04:08 2027 GMTX509v3 Subject Alternative Name: DNS:myshop.test, DNS:www.myshop.testاسمها: myshop.test و www.myshop.test. صادرکننده: LoopX Test CA. انقضا: ۱۹ ژانویهی ۲۰۲۷. برای api.myshop.test معتبر نیست: در SAN نیست و wildcard هم نیست. curl برایش no alternative certificate subject name matches میداد.
یک گواهی جدید با همان CA بساز که برای هر زیردامنهی myshop.test معتبر باشد (wildcard) و با --resolve ثابت کن api.myshop.test با آن کار میکند. آیا myshop.test (بدون زیردامنه) هم کار میکند؟
دیدن جواب
openssl req -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \ -keyout wild.key -out wild.csr -subj "/CN=*.myshop.test"printf "subjectAltName=DNS:*.myshop.test\n" > wild.extopenssl x509 -req -in wild.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -out wild.crt -days 90 -extfile wild.extsed -e "s/server.crt/wild.crt/; s/server.key/wild.key/; s/8443/8446/" https_server.py > wild_server.pypython3 wild_server.pyدر ترمینال دیگر:
curl -sS --cacert ca.crt --resolve api.myshop.test:8446:127.0.0.1 https://api.myshop.test:8446/curl -sS --cacert ca.crt --resolve myshop.test:8446:127.0.0.1 https://myshop.test:8446/<h1>صفحهی امن</h1>curl: (60) SSL: no alternative certificate subject name matches target host name 'myshop.test'...*.myshop.test همهی زیردامنههای یکسطحی را میپوشاند (api، www، shop)، ولی نه خود myshop.test و نه دوسطحیها (a.b.myshop.test). برای همین گواهیهای واقعی معمولاً هر دو را دارند: DNS:myshop.test,DNS:*.myshop.test.
یک اسکریپت کوچک cert-check.sh بنویس که برای هر فایل گواهی که به آن داده میشود، اسم و تاریخ انقضا را چاپ کند و اگر کمتر از ۳۰ روز مانده بود، «هشدار» بنویسد و در آخر با کد ۱ خارج شود. روی server.crt (۳۰ روزه) و shop.crt (۹۰ روزه) امتحانش کن.
دیدن جواب
#!/usr/bin/env bashstatus=0for cert in "$@"; do name=$(openssl x509 -in "$cert" -noout -subject | sed 's/.*CN = //') end=$(openssl x509 -in "$cert" -noout -enddate | cut -d= -f2) if openssl x509 -in "$cert" -noout -checkend $((30*86400)) > /dev/null; then echo "OK $name (until $end)" else echo "WARNING $name expires soon: $end" status=1 fidoneexit $statuschmod +x cert-check.sh./cert-check.sh server.crt shop.crt; echo "exit=$?"WARNING localhost expires soon: Nov 20 21:03:40 2026 GMTOK myshop.test (until Jan 19 21:04:08 2027 GMT)exit=1server.crt دقیقاً ۳۰ روز اعتبار داشت و چند ثانیه از ساختش گذشته، پس «کمتر از ۳۰ روز» مانده. "$@" یعنی همهی آرگومانهای اسکریپت، sed و cut تکهی لازم را از خروجی جدا میکنند (همه را در دورههای لینوکس و bash مفصل میبینی). با cron (هر صبح) و یک ایمیل یا پیام، همین اسکریپت جلوی صبحهای «سایت با خطای گواهی بالا آمده» را میگیرد.
آزمونک
Section titled “آزمونک”HTTPS کدام سه چیز را تضمین میکند؟
جواب درست: محرمانگی، درستی (دستکارینشدن) و هویت سرور
هویت مهمترین و فراموششدهترین است.
گواهی دیجیتال چه چیزی را به هم گره میزند؟
جواب درست: یک کلید عمومی و یک اسم دامنه، با امضای یک CA
و بازهی اعتبار.
خطای self-signed certificate یعنی…
جواب درست: گواهی را هیچ CA مورد اعتمادی امضا نکرده
خودت برای خودت کارت چاپ کردهای.
با HTTPS، صاحب Wi-Fi کافه چه چیزی را میتواند ببیند؟
جواب درست: اسم سایت (SNI) و IP آن
محتوا، مسیر و هدرها داخل تونلاند.
یک سایت فیشینگ قفل سبز دارد. یعنی…
جواب درست: ارتباط تو با همان دامنهی جعلی رمزنگاری شده؛ دربارهی صداقتش چیزی نمیگوید
اسم دامنه را بخوان.
چرا curl -k در اسکریپتهای واقعی خطرناک است؟
جواب درست: بررسی هویت سرور را خاموش میکند و هر کسی میتواند جای سرور باشد
پاکت دربسته، تحویل به غریبه.
جمعبندی
Section titled “جمعبندی”- HTTPS = HTTP داخل TLS: محرمانگی، درستی و هویت سرور.
- TLS با کلید عمومی/خصوصی هویت را ثابت میکند و یک کلید متقارن موقت برای بقیهی گفتوگو میسازد.
- گواهی کلید عمومی را به اسم دامنه گره میزند و یک CA امضایش میکند؛ مرورگر زنجیره را تا یک ریشهی نصبشده بررسی میکند، بهعلاوهی اسم و تاریخ.
- خطاهای اصلی: CA ناشناس (خودامضا)، اسم نادرست، تاریخ گذشته. راهحل هیچکدام
-kنیست. - با HTTPS هم اسم سایت (SNI) و IP دیده میشوند؛ محتوا، مسیر و کوکی نه.
- قفل یعنی ارتباط امن با این دامنه، نه اینکه دامنه قابلاعتماد است. گواهی واقعی: Let’s Encrypt (دورهی nginx).
| دستور | کاری که میکند |
|---|---|
curl -v https://site | دیدن دستدادن TLS و گواهی |
openssl x509 -in c.crt -noout -subject -issuer -dates | خواندن گواهی |
openssl s_client -connect host:443 -servername host | گواهی یک سایت واقعی |
openssl verify -CAfile ca.crt c.crt | بررسی زنجیره |
openssl x509 -in c.crt -noout -checkend 2592000 | انقضا تا ۳۰ روز دیگر؟ |
curl --cacert ca.crt | اعتماد به CA مشخص |
curl -k | خاموش کردن بررسی (فقط آزمایش) |
/etc/ssl/certs | گواهیهای ریشهی سیستم |