تعاملات رمزگذاری شده مشتری-سرور از امنیت لایه حمل و نقل (TLS) برای محافظت از داده های برنامه شما استفاده می کند.
این مقاله بهترین شیوه های مربوط به بهترین شیوه های پروتکل شبکه ایمن و ملاحظات زیرساخت کلید عمومی (PKI) (PKI) را مورد بحث قرار می دهد. برای جزئیات بیشتر ، نمای کلی امنیت Android و همچنین نمای کلی مجوزها را بخوانید.
مفاهیم
سرور با گواهی TLS دارای یک کلید عمومی و یک کلید خصوصی منطبق است. سرور از رمزنگاری کلید عمومی برای امضای گواهینامه خود در هنگام دست دادن TLS استفاده می کند.
یک دست دادن ساده فقط ثابت می کند که سرور کلید خصوصی گواهی را می داند. برای رسیدگی به این وضعیت، به مشتری اجازه دهید به چندین گواهی اعتماد کند. سرور معینی غیرقابل اعتماد است اگر گواهی آن در مجموعه گواهی های مورد اعتماد سمت سرویس گیرنده ظاهر نشود.
با این حال، سرورها ممکن است از چرخش کلید برای تغییر کلید عمومی گواهی خود با یک کلید جدید استفاده کنند. تغییر پیکربندی سرور نیاز به به روز رسانی برنامه مشتری دارد. اگر سرور یک سرویس وب شخص ثالث است، مانند مرورگر وب یا برنامه ایمیل، تشخیص زمان به روز رسانی برنامه مشتری دشوارتر است.
سرورها معمولاً برای صدور گواهیها به گواهیهای مقامات گواهی (CA) متکی هستند که پیکربندی سمت سرویس گیرنده را در طول زمان پایدارتر نگه میدارد. یک CA یک گواهی سرور را با استفاده از کلید خصوصی خود امضا می کند . سپس مشتری می تواند بررسی کند که سرور دارای گواهینامه CA شناخته شده در پلتفرم است.
CAهای مورد اعتماد معمولاً در پلتفرم میزبان فهرست می شوند. Android 8.0 (سطح API 26) شامل بیش از 100 CA است که در هر نسخه به روز می شوند و بین دستگاه ها تغییر نمی کنند.
برنامه های سرویس گیرنده به مکانیزمی برای تأیید سرور نیاز دارند زیرا CA گواهینامه هایی را برای سرورهای متعدد ارائه می دهد. گواهی CA سرور را با استفاده از یک نام خاص، مانند gmail.com یا با استفاده از یک علامت عام، مانند *.google.com شناسایی می کند.
برای مشاهده اطلاعات گواهی سرور یک وب سایت، از دستور s_client
ابزار openssl
استفاده کنید که شماره پورت را ارسال می کند. به طور پیش فرض، HTTPS از پورت 443 استفاده می کند.
این دستور خروجی openssl s_client
به openssl x509
ارسال می کند که اطلاعات گواهی را در استاندارد X.509 فرمت می کند. دستور موضوع (نام سرور) و صادرکننده (CA) را درخواست می کند.
openssl s_client -connect WEBSITE-URL:443 | \ openssl x509 -noout -subject -issuer
یک مثال HTTPS
با فرض اینکه شما یک وب سرور با گواهی صادر شده توسط یک CA شناخته شده دارید، می توانید یک درخواست ایمن همانطور که در کد زیر نشان داده شده است ارائه دهید:
کاتلین
val url = URL("https://wikipedia.org") val urlConnection: URLConnection = url.openConnection() val inputStream: InputStream = urlConnection.getInputStream() copyInputStreamToOutputStream(inputStream, System.out)
جاوا
URL url = new URL("https://wikipedia.org"); URLConnection urlConnection = url.openConnection(); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);
برای سفارشی کردن درخواستهای HTTP، به HttpURLConnection
ارسال کنید. مستندات Android HttpURLConnection
شامل نمونههایی برای رسیدگی به هدرهای درخواست و پاسخ، انتشار محتوا، مدیریت کوکیها، استفاده از پراکسیها، ذخیره پاسخها و موارد دیگر است. چارچوب Android گواهی ها و نام میزبان را با استفاده از این API ها تأیید می کند.
در صورت امکان از این API ها استفاده کنید. بخش زیر به مسائل رایجی که نیاز به راه حل های متفاوتی دارند می پردازد.
مشکلات رایج در تأیید گواهیهای سرور
فرض کنید به جای بازگرداندن محتوا، getInputStream()
یک استثنا ایجاد می کند:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374) at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433) at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290) at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240) at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282) at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177) at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
این ممکن است به دلایل مختلفی رخ دهد، از جمله:
- CA که گواهی سرور را صادر کرد ناشناخته بود .
- گواهی سرور توسط یک CA امضا نشده است، اما خود امضا شده است .
- پیکربندی سرور فاقد یک CA میانی است .
در بخشهای زیر نحوه رسیدگی به این مشکلات و در عین حال ایمن نگه داشتن اتصال شما به سرور بحث میشود.
مرجع گواهی ناشناس
SSLHandshakeException
به این دلیل ایجاد می شود که سیستم به CA اعتماد ندارد. دلیل آن میتواند به این دلیل باشد که گواهینامهای از یک مرکز صدور گواهی جدید دارید که Android به آن اعتماد ندارد یا به این دلیل که برنامه شما در نسخه قبلی بدون CA کار میکند. از آنجا که خصوصی است، CA به ندرت شناخته می شود. اغلب، یک CA ناشناخته است زیرا یک CA عمومی نیست، بلکه خصوصی است که توسط سازمانی مانند دولت، شرکت یا مؤسسه آموزشی برای استفاده خود صادر شده است.
برای اعتماد به CA سفارشی بدون نیاز به تغییر کد برنامه، پیکربندی امنیت شبکه خود را تغییر دهید.
احتیاط: بسیاری از وب سایت ها یک راه حل جایگزین ضعیف را توصیف می کنند، که نصب TrustManager
است که هیچ کاری انجام نمی دهد. انجام این کار کاربران شما را در هنگام استفاده از یک هات اسپات عمومی Wi-Fi در برابر حملات آسیب پذیر می کند، زیرا مهاجم می تواند از ترفندهای DNS برای ارسال ترافیک کاربران شما از طریق پروکسی که وانمود می کند سرور شماست استفاده کند. سپس مهاجم می تواند رمز عبور و سایر داده های شخصی را ضبط کند. این کار به این دلیل کار میکند که مهاجم میتواند یک گواهی تولید کند، و بدون اینکه TrustManager
تأیید کند که گواهی از یک منبع قابل اعتماد است، نمیتوانید این نوع حمله را مسدود کنید. بنابراین این کار را حتی به طور موقت انجام ندهید. در عوض، برنامه خود را به صادرکننده گواهی سرور اعتماد کنید.
گواهی سرور خود امضا شده
دوم، SSLHandshakeException
ممکن است به دلیل یک گواهی خودامضا رخ دهد و سرور را CA خودش کند. این شبیه به یک مرجع گواهی ناشناخته است، بنابراین پیکربندی امنیت شبکه برنامه خود را تغییر دهید تا به گواهی های خود امضا شده اعتماد کنید.
مجوز گواهی میانی وجود ندارد
سوم، SSLHandshakeException
به دلیل از دست دادن CA میانی رخ می دهد. CA های عمومی به ندرت گواهی های سرور را امضا می کنند. در عوض، CA ریشه، CAهای میانی را علامت میزند.
برای کاهش خطر سازش، CA ها CA ریشه را آفلاین نگه می دارند. با این حال، سیستمعاملهایی مانند Android معمولاً مستقیماً فقط به CAهای ریشه اعتماد میکنند، و یک شکاف اعتماد کوتاه بین گواهی سرور - که توسط CA میانی امضا شده است - و تأییدکننده گواهی که CA ریشه را تشخیص میدهد، باقی میگذارند.
برای حذف این شکاف اعتماد، سرور زنجیرهای از گواهیها را از CA سرور از طریق هر واسطهای به یک CA ریشه قابل اعتماد در طول دست دادن TLS ارسال میکند.
به عنوان مثال، در اینجا زنجیره گواهی mail.google.com است که توسط دستور openssl
s_client
مشاهده می شود:
$ openssl s_client -connect mail.google.com:443 --- Certificate chain 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=mail.google.com i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority ---
این نشان میدهد که سرور یک گواهی برای mail.google.com صادر شده توسط Thawte SGC CA، که یک CA متوسط است، و یک گواهی دوم برای Thawte SGC CA صادر شده توسط Verisign CA، که CA اصلی است که توسط CA مورد اعتماد است، ارسال میکند. اندروید.
با این حال، ممکن است یک سرور طوری پیکربندی نشده باشد که شامل CA میانی لازم باشد. به عنوان مثال، در اینجا سروری وجود دارد که می تواند باعث ایجاد خطا در مرورگرهای اندروید و استثنا در برنامه های اندروید شود:
$ openssl s_client -connect egov.uscis.gov:443 --- Certificate chain 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3 ---
برخلاف CA ناشناخته یا گواهی سرور با امضای خود، اکثر مرورگرهای دسکتاپ هنگام برقراری ارتباط با این سرور خطایی ایجاد نمی کنند. مرورگرهای دسکتاپ، CA های میانی مورد اعتماد را در حافظه پنهان ذخیره می کنند. پس از یادگیری در مورد یک CA متوسط از یک سایت، یک مرورگر دیگر به آن در زنجیره گواهی نیاز نخواهد داشت.
برخی از سایت ها عمداً این کار را برای سرورهای وب ثانویه ارائه می دهند. برای صرفه جویی در پهنای باند، آنها ممکن است صفحه اصلی HTML خود را از سروری با زنجیره گواهی کامل ارائه دهند، اما تصاویر، CSS و جاوا اسکریپت خود را بدون CA ارائه دهند. متأسفانه، گاهی اوقات این سرورها می توانند سرویس وب را ارائه دهند که می خواهید از طریق برنامه Android خود به آن دسترسی پیدا کنید، که آنقدرها قابل تحمل نیست.
برای رفع این مشکل، سرور را طوری پیکربندی کنید که CA واسطه را در زنجیره سرور قرار دهد. اکثر CA دستورالعمل هایی را در مورد نحوه انجام این کار برای سرورهای وب رایج ارائه می دهند.
هشدار در مورد استفاده مستقیم از SSLSocket
تاکنون، نمونهها بر روی HTTPS با استفاده از HttpsURLConnection
تمرکز کردهاند. گاهی اوقات برنامه ها باید از TLS جدا از HTTPS استفاده کنند. به عنوان مثال، یک برنامه ایمیل ممکن است از انواع TLS SMTP، POP3 یا IMAP استفاده کند. در این موارد، برنامه میتواند مستقیماً از SSLSocket
استفاده کند، تقریباً به همان روشی که HttpsURLConnection
در داخل انجام میدهد.
تکنیکهایی که تاکنون برای رسیدگی به مسائل تأیید گواهی توضیح داده شد، برای SSLSocket
نیز اعمال میشود. در واقع، هنگام استفاده از TrustManager
سفارشی، آنچه به HttpsURLConnection
ارسال میشود یک SSLSocketFactory
است. بنابراین اگر نیاز به استفاده از TrustManager
سفارشی با SSLSocket
دارید، همان مراحل را دنبال کنید و از آن SSLSocketFactory
برای ایجاد SSLSocket
خود استفاده کنید.
احتیاط: SSLSocket
تأیید نام میزبان را انجام نمی دهد. این به برنامه شما بستگی دارد که تأیید نام میزبان خود را انجام دهد، ترجیحاً با فراخوانی getDefaultHostnameVerifier()
با نام میزبان مورد انتظار. همچنین، توجه داشته باشید که HostnameVerifier.verify()
استثنایی برای خطا ایجاد نمی کند. در عوض، یک نتیجه بولی برمی گرداند که باید صریحاً آن را بررسی کنید.
CAهای مسدود شده
TLS برای صدور گواهینامه فقط برای صاحبان تأیید شده سرورها و دامنه ها به CAها متکی است. در موارد نادر، CAها فریب میخورند یا در مورد Comodo یا DigiNotar ، نقض میشوند و در نتیجه گواهیهای نام میزبان برای شخصی غیر از صاحب سرور یا دامنه صادر میشود.
برای کاهش این خطر، اندروید این توانایی را دارد که گواهیهای خاص یا حتی کل CA را به فهرست انکار اضافه کند. در حالی که این لیست از لحاظ تاریخی در سیستم عامل ساخته شده بود، با شروع اندروید 4.2، این لیست را می توان از راه دور به روز کرد تا با مصالحه های آینده مقابله کند.
محدود کردن برنامه شما به گواهیهای خاص
احتیاط: پین کردن گواهی، عمل محدود کردن گواهینامه هایی که برای برنامه شما معتبر هستند به گواهی هایی که قبلاً مجوز داده اید، برای برنامه های Android توصیه نمی شود. تغییرات پیکربندی سرور آینده، مانند تغییر به CA دیگر، باعث میشود برنامههای دارای گواهیهای پینشده بدون دریافت بهروزرسانی نرمافزار مشتری، به سرور متصل شوند.
اگر میخواهید برنامهتان را محدود کنید تا فقط گواهیهایی را که شما مشخص کردهاید بپذیرد، بسیار مهم است که چندین پین پشتیبان، از جمله حداقل یک کلید که کاملاً تحت کنترل شماست، و یک دوره انقضا به اندازه کافی کوتاه برای جلوگیری از مشکلات سازگاری، اضافه کنید. پیکربندی امنیت شبکه، پین کردن را با این قابلیت ها فراهم می کند.
گواهی های مشتری
این مقاله بر استفاده از TLS برای ایمن سازی ارتباطات با سرورها تمرکز دارد. TLS همچنین از مفهوم گواهیهای کلاینت پشتیبانی میکند که به سرور اجازه میدهد هویت یک کلاینت را تأیید کند. در حالی که فراتر از محدوده این مقاله است، تکنیک های درگیر مشابه تعیین یک TrustManager
سفارشی هستند.
Nogotofail: یک ابزار تست امنیت ترافیک شبکه
Nogotofail ابزاری است که به شما یک راه آسان برای تأیید ایمن بودن برنامههایتان در برابر آسیبپذیریها و پیکربندیهای نادرست TLS/SSL میدهد. این ابزاری خودکار، قدرتمند و مقیاسپذیر برای آزمایش مسائل امنیتی شبکه در هر دستگاهی است که ترافیک شبکهاش میتواند از طریق آن عبور کند.
Nogotofail برای سه مورد استفاده اصلی مفید است:
- یافتن باگ ها و آسیب پذیری ها
- تأیید اصلاحات و مشاهده رگرسیون.
- درک اینکه چه برنامهها و دستگاههایی چه ترافیکی ایجاد میکنند.
Nogotofail برای Android، iOS، Linux، Windows، ChromeOS، macOS و در واقع هر دستگاهی که برای اتصال به اینترنت استفاده می کنید کار می کند. یک کلاینت برای پیکربندی تنظیمات و دریافت اعلان ها در اندروید و لینوکس در دسترس است و خود موتور حمله می تواند به عنوان روتر، سرور VPN یا پروکسی مستقر شود.
می توانید در پروژه منبع باز Nogotofail به ابزار دسترسی پیدا کنید.
به روز رسانی SSL و TLS
اندروید 10
برخی از مرورگرها، مانند Google Chrome، زمانی که سرور TLS پیام درخواست گواهی را به عنوان بخشی از یک دست دادن TLS ارسال میکند، به کاربران اجازه میدهند گواهی را انتخاب کنند. از Android 10، اشیاء KeyChain هنگام فراخوانی KeyChain.choosePrivateKeyAlias()
به صادرکنندگان و پارامترهای مشخصات کلیدی احترام میگذارند تا یک درخواست انتخاب گواهی را به کاربران نشان دهند. به طور خاص، این درخواست حاوی گزینههایی نیست که با مشخصات سرور مطابقت ندارند.
اگر گواهیهای قابل انتخاب توسط کاربر در دسترس نباشد، مانند زمانی که هیچ گواهینامهای با مشخصات سرور مطابقت ندارد یا دستگاهی هیچ گواهینامهای نصب نکرده است، اعلان انتخاب گواهی اصلا ظاهر نمیشود.
علاوه بر این، در Android 10 یا بالاتر، نیازی به قفل صفحه دستگاه برای وارد کردن کلیدها یا گواهیهای CA به یک شی KeyChain نیست.
TLS 1.3 به طور پیش فرض فعال است
در Android 10 و بالاتر، TLS 1.3 به طور پیش فرض برای همه اتصالات TLS فعال است. در اینجا چند جزئیات مهم در مورد اجرای TLS 1.3 ما آورده شده است:
- مجموعه رمز TLS 1.3 را نمی توان سفارشی کرد. مجموعه رمزهای پشتیبانی شده TLS 1.3 همیشه در صورت فعال بودن TLS 1.3 فعال می شوند. هرگونه تلاش برای غیرفعال کردن آنها با فراخوانی
setEnabledCipherSuites()
نادیده گرفته می شود. - هنگامی که TLS 1.3 مورد مذاکره قرار می گیرد، اشیاء
HandshakeCompletedListener
قبل از افزودن جلسات به حافظه پنهان جلسه فراخوانی می شوند. (در TLS 1.2 و سایر نسخه های قبلی، این اشیاء پس از اضافه شدن جلسات به حافظه پنهان جلسه فراخوانی می شوند.) - در برخی شرایط که نمونههای SSLEngine یک
SSLHandshakeException
در نسخههای قبلی Android پرتاب میکنند، این نمونهها یکSSLProtocolException
را به جای آن در Android 10 و بالاتر میفرستند. - حالت 0-RTT پشتیبانی نمی شود.
در صورت تمایل، می توانید با تماس با SSLContext.getInstance("TLSv1.2")
یک SSLContext با TLS 1.3 غیرفعال دریافت کنید. همچنین می توانید با فراخوانی setEnabledProtocols()
بر روی یک شی مناسب، نسخه های پروتکل را بر اساس هر اتصال فعال یا غیرفعال کنید.
گواهینامه های امضا شده با SHA-1 در TLS قابل اعتماد نیستند
در Android 10، گواهیهایی که از الگوریتم هش SHA-1 استفاده میکنند، در اتصالات TLS قابل اعتماد نیستند. Root CA از سال 2016 چنین گواهینامه هایی را صادر نکرده است و دیگر در کروم یا سایر مرورگرهای اصلی قابل اعتماد نیستند.
اگر اتصال به سایتی باشد که گواهی را با استفاده از SHA-1 ارائه می دهد، هرگونه تلاش برای اتصال با شکست مواجه می شود.
تغییرات و بهبود رفتار KeyChain
برخی از مرورگرها، مانند Google Chrome، زمانی که سرور TLS پیام درخواست گواهی را به عنوان بخشی از یک دست دادن TLS ارسال میکند، به کاربران اجازه میدهند گواهی را انتخاب کنند. از Android 10، اشیاء KeyChain
هنگام فراخوانی KeyChain.choosePrivateKeyAlias()
به صادرکنندگان و پارامترهای مشخصات کلیدی احترام میگذارند تا یک درخواست انتخاب گواهی را به کاربران نشان دهند. به طور خاص، این اعلان حاوی گزینههایی نیست که با مشخصات سرور مطابقت ندارند.
اگر گواهیهای قابل انتخاب توسط کاربر در دسترس نباشد، مانند زمانی که هیچ گواهینامهای با مشخصات سرور مطابقت ندارد یا دستگاهی هیچ گواهینامهای نصب نکرده است، اعلان انتخاب گواهی اصلا ظاهر نمیشود.
علاوه بر این، در Android 10 یا بالاتر، نیازی به قفل صفحه دستگاه برای وارد کردن کلیدها یا گواهیهای CA به یک شی KeyChain نیست.
سایر تغییرات TLS و رمزنگاری
چندین تغییر جزئی در کتابخانههای TLS و رمزنگاری وجود دارد که در Android 10 اعمال میشوند:
- رمزهای AES/GCM/NoPadding و ChaCha20/Poly1305/NoPadding اندازههای بافر دقیقتری را از
getOutputSize()
برمیگردانند. - مجموعه رمز TLS_FALLBACK_SCSV از تلاشهای اتصال با حداکثر پروتکل TLS 1.2 یا بالاتر حذف شده است. به دلیل پیشرفتهایی که در اجرای سرور TLS انجام شده است، توصیه نمیکنیم که به عقبگرد خارجی TLS بپردازید. درعوض، توصیه میکنیم به مذاکره نسخه TLS تکیه کنید.
- ChaCha20-Poly1305 نام مستعار ChaCha20/Poly1305/NoPadding است.
- نامهای میزبان با نقاط انتهایی، نام میزبان SNI معتبر در نظر گرفته نمیشوند.
- پسوند supported_signature_algorithms در CertificateRequest هنگام انتخاب کلید امضا برای پاسخهای گواهی رعایت میشود.
- کلیدهای امضای غیرشفاف، مانند کلیدهای Android Keystore، می توانند با امضاهای RSA-PSS در TLS استفاده شوند.
اتصال HTTPS تغییر می کند
اگر برنامهای که اندروید 10 را اجرا میکند به setSSLSocketFactory()
تهی شود، یک IllegalArgumentException
رخ میدهد. در نسخههای قبلی، ارسال null به setSSLSocketFactory()
تأثیری مشابه ارسال در کارخانه پیشفرض فعلی داشت.
اندروید 11
سوکت های SSL به طور پیش فرض از موتور Conscrypt SSL استفاده می کنند
پیاده سازی پیش فرض SSLSocket اندروید بر اساس Conscrypt
است. از اندروید 11، این پیاده سازی به صورت داخلی بر روی SSLEngine Conscrypt ساخته شده است.