GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing python.github.io — ssl --- پوششی TLS/SSL برای اشیای سوکت
|
کلاینت / سرور |
SSLv2 |
SSLv3 |
TLS [3] |
TLSv1 |
TLSv1.1 |
TLSv1.2 |
SSLv2 |
بله |
خیر |
خیر [1] |
خیر |
خیر |
خیر |
SSLv3 |
خیر |
بله |
خیر [2] |
خیر |
خیر |
خیر |
TLS (SSLv23) [3] |
خیر [1] |
خیر [2] |
بله |
بله |
بله |
بله |
TLSv1 |
خیر |
خیر |
بله |
بله |
خیر |
خیر |
TLSv1.1 |
خیر |
خیر |
بله |
خیر |
بله |
خیر |
TLSv1.2 |
خیر |
خیر |
بله |
خیر |
خیر |
بله |
پانویسها
همچنین ببینید
create_default_context() به ماژول ssl اجازه میدهد تنظیمات امنیتی را برای منظوری مشخص انتخاب کند.
تغییر یافته در نسخهی 3.6: زمینه با مقادیر پیشفرض امن ایجاد میشود. گزینههای OP_NO_COMPRESSION، OP_CIPHER_SERVER_PREFERENCE، OP_SINGLE_DH_USE، OP_SINGLE_ECDH_USE، OP_NO_SSLv2 و OP_NO_SSLv3 (بهجز PROTOCOL_SSLv3) بهطور پیشفرض تنظیم شدهاند. فهرست اولیهی مجموعهرمزها فقط شامل رمزهای HIGH است و هیچ رمز NULL و هیچ رمز MD5 ندارد.
منسوخ شده از نسخهی 3.10: SSLContext بدون آرگومان پروتکل منسوخ شده است. کلاس زمینه در آینده به پروتکل PROTOCOL_TLS_CLIENT یا PROTOCOL_TLS_SERVER نیاز خواهد داشت.
تغییر یافته در نسخهی 3.10: مجموعههای رمزنگاری پیشفرض اکنون فقط شامل رمزهای امن AES و ChaCha20 با رازداری پیشرو (forward secrecy) و سطح امنیت ۲ هستند. کلیدهای RSA و DH با کمتر از ۲۰۴۸ بیت و کلیدهای ECC با کمتر از ۲۲۴ بیت ممنوع هستند. PROTOCOL_TLS، PROTOCOL_TLS_CLIENT و PROTOCOL_TLS_SERVER از TLS 1.2 بهعنوان حداقل نسخهی TLS استفاده میکنند.
نکته
SSLContext پس از آنکه توسط یک اتصال استفاده شد، تنها تغییر محدودی را میپذیرد. افزودن گواهیهای جدید به مخزن اعتماد داخلی مجاز است، اما تغییر رمزها، تنظیمات صحتسنجی، یا گواهیهای mTLS ممکن است منجر به رفتار غیرمنتظره شود.
نکته
SSLContext برای اشتراکگذاری و استفاده توسط چندین اتصال طراحی شده است. بنابراین، تا زمانی که پس از استفاده توسط یک اتصال پیکربندی مجدد نشود، ایمن از نظر نخی است.
اشیای SSLContext دارای متدها و ویژگیهای زیر هستند:
آمار مربوط به تعداد گواهیهای X.509 بارگذاریشده، تعداد گواهیهای X.509 علامتگذاریشده بهعنوان گواهی CA و فهرستهای ابطال گواهی را بهصورت دیکشنری دریافت کنید.
مثالی برای یک زمینه با یک گواهی CA و یک گواهی دیگر:
>>> context.cert_store_stats() {'crl': 0, 'x509_ca': 1, 'x509': 2}
اضافه شده در نسخهی 3.4.
یک کلید خصوصی و گواهی مربوطه را بارگذاری میکند. رشتهی certfile باید مسیر یک پرونده واحد در قالب PEM حاوی گواهی و همچنین هر تعداد گواهی CA مورد نیاز برای احراز اصالت گواهی باشد. رشتهی keyfile، در صورت وجود، باید به پروندهای حاوی کلید خصوصی اشاره کند. در غیر این صورت، کلید خصوصی نیز از certfile گرفته میشود. برای اطلاعات بیشتر دربارهی نحوهی ذخیرهی گواهی در certfile، مبحث گواهیها را ببینید.
آرگومان password میتواند تابعی باشد که برای دریافت گذرواژه جهت رمزگشایی کلید خصوصی فراخوانی میشود. این تابع فقط در صورتی فراخوانی خواهد شد که کلید خصوصی رمزنگاریشده باشد و به یک گذرواژه نیاز باشد. این تابع بدون آرگومان فراخوانی میشود و باید یک رشته، bytes یا bytearray برگرداند. اگر مقدار بازگشتی یک رشته باشد، پیش از استفاده از آن برای رمزگشایی کلید، بهصورت UTF-8 کدگذاری خواهد شد. بهعنوان جایگزین، میتوان یک مقدار رشته، bytes یا bytearray را مستقیماً بهعنوان آرگومان password ارائه کرد. اگر کلید خصوصی رمزنگارینشده باشد و نیازی به گذرواژه نباشد، این مقدار نادیده گرفته خواهد شد.
اگر آرگومان password تعیین نشده باشد و به یک گذرواژه نیاز باشد، از سازوکار توکار درخواست گذرواژه در OpenSSL برای درخواست تعاملی گذرواژه از کاربر استفاده میشود.
اگر کلید خصوصی با گواهی مطابقت نداشته باشد، یک SSLError پرتاب میشود.
تغییر یافته در نسخهی 3.3: آرگومان اختیاری جدید password.
بارگذاری مجموعهای از گواهیهای پیشفرض «مرجع صدور گواهی» (CA) از مکانهای پیشفرض. در ویندوز، گواهیهای CA را از مخزنهای سیستمی CA و ROOT بارگذاری میکند. در تمام سیستمها، SSLContext.set_default_verify_paths() را فراخوانی میکند. در آینده، این متد ممکن است گواهیهای CA را از مکانهای دیگر نیز بارگذاری کند.
پرچم purpose مشخص میکند که چه نوع گواهیهای CA بارگذاری شوند. تنظیم پیشفرض Purpose.SERVER_AUTH گواهیهایی را بارگذاری میکند که برای احراز هویت سرور وب TLS (سوکتهای سمت کلاینت) علامتگذاری شدهاند و مورد اعتماد هستند. Purpose.CLIENT_AUTH گواهیهای CA را برای تأیید گواهی کلاینت در سمت سرور بارگذاری میکند.
اضافه شده در نسخهی 3.4.
مجموعهای از گواهیهای «مرجع صدور گواهی» (CA) را بارگذاری میکند که برای اعتبارسنجی گواهیهای سایر همتایان، هنگامی که verify_mode مقداری غیر از CERT_NONE باشد، استفاده میشوند. حداقل یکی از cafile یا capath باید مشخص شود.
این متد همچنین میتواند فهرستهای لغو گواهی (CRL) را در قالب PEM یا DER بارگذاری کند. برای استفاده از CRLها، باید SSLContext.verify_flags بهدرستی پیکربندی شود.
رشتهی cafile، در صورت وجود، مسیر یک پرونده حاوی گواهیهای CA بههمپیوسته در قالب PEM است. برای اطلاعات بیشتر دربارهی نحوهی چیدمان گواهیها در این پرونده، بحث گواهیها را ببینید.
رشتهی capath، در صورت وجود، مسیر پوشهای است که شامل چندین گواهی CA در قالب PEM است و از چیدمان خاص OpenSSL پیروی میکند.
شیء cadata، در صورت وجود، یا یک رشتهی ASCII از یک یا چند گواهی کدگذاریشده با PEM است یا یک bytes-like object از گواهیهای کدگذاریشده با DER. مانند capath، سطرهای اضافی اطراف گواهیهای کدگذاریشده با PEM نادیده گرفته میشوند، اما حداقل یک گواهی باید موجود باشد.
تغییر یافته در نسخهی 3.4: آرگومان اختیاری جدید cadata
فهرستی از گواهیهای بارگذاریشدهی «مرجع صدور گواهی» (CA) را دریافت کنید. اگر پارامتر binary_form برابر False باشد، هر آیتم فهرست یک دیکشنری مانند خروجی SSLSocket.getpeercert() است. در غیر این صورت، متد فهرستی از گواهیهای کدگذاریشده با DER را برمیگرداند. فهرست برگرداندهشده شامل گواهیهای capath نمیشود، مگر اینکه گواهیای توسط یک اتصال SSL درخواست و بارگذاری شده باشد.
نکته
گواهیهای موجود در یک پوشه capath بارگذاری نمیشوند، مگر اینکه حداقل یک بار استفاده شده باشند.
اضافه شده در نسخهی 3.4.
فهرستی از رمزنگارهای فعال را دریافت کنید. این فهرست به ترتیب اولویت رمزنگارها مرتب شده است. SSLContext.set_ciphers() را ببینید.
مثال:
>>> ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) >>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA') >>> ctx.get_ciphers() [{'aead': True, 'alg_bits': 256, 'auth': 'auth-rsa', 'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA ' 'Enc=AESGCM(256) Mac=AEAD', 'digest': None, 'id': 50380848, 'kea': 'kx-ecdhe', 'name': 'ECDHE-RSA-AES256-GCM-SHA384', 'protocol': 'TLSv1.2', 'strength_bits': 256, 'symmetric': 'aes-256-gcm'}, {'aead': True, 'alg_bits': 128, 'auth': 'auth-rsa', 'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA ' 'Enc=AESGCM(128) Mac=AEAD', 'digest': None, 'id': 50380847, 'kea': 'kx-ecdhe', 'name': 'ECDHE-RSA-AES128-GCM-SHA256', 'protocol': 'TLSv1.2', 'strength_bits': 128, 'symmetric': 'aes-128-gcm'}]
اضافه شده در نسخهی 3.6.
فهرستی از گروههای پیادهسازیشده برای توافق کلید را با در نظر گرفتن مقادیر کنونی minimum_version و maximum_version مربوط به TLS دریافت کنید. برای نمونه:
>>> ctx = ssl.create_default_context() >>> ctx.minimum_version = ssl.TLSVersion.TLSv1_3 >>> ctx.maximum_version = ssl.TLSVersion.TLSv1_3 >>> ctx.get_groups() ['secp256r1', 'secp384r1', 'secp521r1', 'x25519', 'x448', ...]
به طور پیشفرض، این متد فقط نامهای ترجیحی IANA را برای گروههای موجود برمیگرداند. با این حال، اگر پارامتر include_aliases روی True تنظیم شود، این متد هرگونه نام مستعار مرتبط مانند نامهای منحنی ECDH پشتیبانیشده در نسخههای قدیمیتر OpenSSL را نیز برخواهد گرداند.
اضافه شده در نسخهی 3.15.
بارگذاری مجموعهای از گواهیهای پیشفرض «مرجع صدور گواهی» (CA) از یک مسیر سامانه فایلبندی که هنگام ساخت کتابخانه OpenSSL تعریف شده است. متأسفانه، راه آسانی برای اطلاع از موفقیت این متد وجود ندارد: اگر هیچ گواهیای یافت نشود، هیچ خطایی بازگردانده نمیشود. با این حال، وقتی کتابخانه OpenSSL بهعنوان بخشی از سیستمعامل ارائه شده باشد، احتمالاً بهدرستی پیکربندی شده است.
رمزهای مجاز را برای سوکتهای ایجادشده با این زمینه هنگام اتصال با استفاده از TLS 1.2 و نسخههای پیشین تنظیم کنید. آرگومان ciphers باید رشتهای در قالب OpenSSL cipher list format باشد. برای تنظیم رمزهای مجاز TLS 1.3، از SSLContext.set_ciphersuites() استفاده کنید.
اگر هیچ رمز (cipher) قابل انتخابی وجود نداشته باشد (به این دلیل که گزینههای زمان کامپایل یا پیکربندیهای دیگر استفاده از تمام رمزهای مشخصشده را ممنوع کردهاند)، یک SSLError بالا برده میشود.
نکته
هنگام اتصال، متد SSLSocket.cipher() سوکتهای SSL جزئیاتی را دربارهی رمز مذاکرهشده بازخواهد گرداند.
رمزهای مجاز را برای سوکتهای ایجادشده با این زمینه هنگام اتصال با استفاده از TLS 1.3 تنظیم کنید. آرگومان ciphersuites باید رشتهای جدا شده با دو نقطه از نام رمزهای TLS 1.3 باشد. اگر هیچ رمزی انتخاب نشود (بهدلیل اینکه گزینههای زمان کامپایل یا پیکربندیهای دیگر استفاده از تمام رمزهای مشخصشده را ممنوع میکنند)، یک SSLError بالا آورده خواهد شد.
نکته
هنگام اتصال، متد SSLSocket.cipher() سوکتهای SSL جزئیاتی را دربارهی رمز مذاکرهشده بازخواهد گرداند.
اضافه شده در نسخهی 3.15.
گروههای مجاز برای توافق کلید را برای سوکتهای ایجادشده با این زمینه تنظیم کنید. این مقدار باید یک رشته در قالب OpenSSL group list format باشد.
نکته
هنگام اتصال، متد SSLSocket.group() سوکتهای SSL گروه استفادهشده برای توافق کلید روی آن اتصال را بازخواهد گرداند.
اضافه شده در نسخهی 3.15.
الگوریتمهای امضای مجاز برای احراز هویت کلاینت مبتنی بر گواهی را تنظیم کنید. این مقدار باید یک رشته در قالب OpenSSL client sigalgs list format باشد.
نکته
هنگام اتصال، متد SSLSocket.client_sigalg() سوکتهای SSL الگوریتم امضای استفادهشده برای انجام احراز هویت کلاینت مبتنی بر گواهی روی آن اتصال را بازخواهد گرداند.
اضافه شده در نسخهی 3.15.
الگوریتمهای امضای مجاز برای سرور جهت تکمیل دستدهی TLS را تنظیم کنید. این مقدار باید یک رشته در قالب OpenSSL sigalgs list format باشد.
نکته
هنگام اتصال، متد SSLSocket.server_sigalg() سوکتهای SSL الگوریتم امضای استفادهشده توسط سرور برای تکمیل دستدهی TLS روی آن اتصال را بازخواهد گرداند.
اضافه شده در نسخهی 3.15.
مشخص کنید که سوکت باید در طول دستدهی SSL/TLS کدام پروتکلها را اعلام کند. این باید فهرستی از رشتههای ASCII، مانند ['http/1.1', 'spdy/2']، مرتبشده بر اساس ترجیح باشد. انتخاب یک پروتکل در طول دستدهی انجام خواهد شد و مطابق RFC 7301 صورت خواهد گرفت. پس از یک دستدهی موفق، متد SSLSocket.selected_alpn_protocol() پروتکل توافقشده را برمیگرداند.
اگر HAS_ALPN برابر False باشد، این متد NotImplementedError را پرتاب خواهد کرد.
اضافه شده در نسخهی 3.5.
مشخص کنید که سوکت باید در حین دستدهی SSL/TLS کدام پروتکلها را اعلام کند. این باید فهرستی از رشتهها باشد، مانند ['http/1.1', 'spdy/2']، که به ترتیب اولویت مرتبشده است. انتخاب یک پروتکل در حین دستدهی انجام خواهد شد و مطابق Application Layer Protocol Negotiation صورت خواهد گرفت. پس از یک دستدهی موفق، متد SSLSocket.selected_npn_protocol() پروتکل توافقشده را برمیگرداند.
اگر HAS_NPN برابر False باشد، این متد NotImplementedError را پرتاب میکند.
اضافه شده در نسخهی 3.3.
منسوخ شده از نسخهی 3.10: NPN با ALPN جایگزین شده است
یک تابع کالبک را ثبت کنید که پس از دریافت پیام دستدهی TLS Client Hello توسط سرور SSL/TLS، هنگامی که کلاینت TLS نشانگر نام سرور (Server Name Indication) را مشخص کرده باشد، فراخوانی میشود. سازوکار نشانگر نام سرور در بخش ۳ از RFC 6066 با عنوان Server Name Indication مشخص شده است.
برای هر SSLContext تنها میتوان یک کالبک تنظیم کرد. اگر sni_callback روی None تنظیم شود، کالبک غیرفعال میشود. فراخوانی مجدد این تابع، کالبک ثبتشدهی پیشین را غیرفعال خواهد کرد.
تابع کالبک با ۳ آرگومان فراخوانی میشود؛ اولی ssl.SSLSocket است، دومی رشتهای است که نام سرور مورد نظر کلاینت برای برقراری ارتباط را نشان میدهد (یا اگر TLS Client Hello حاوی نام سرور نباشد، None) و آرگومان سوم SSLContext اصلی است. آرگومان نام سرور متن است. برای IDN، نام سرور یک IDN A-label است ("xn--pythn-mua.org").
یک کاربرد رایج این کالبک، تغییر ویژگی SSLSocket.context مربوط به ssl.SSLSocket به شیء جدیدی از نوع SSLContext است که نشاندهندهی زنجیرهی گواهیای است که با نام سرور مطابقت دارد.
اگر کالبک یک زمینهی جدید به SSLSocket.context اختصاص دهد، هرگونه پیام ClientHello بعدی روی همان اتصال (برای مثال پس از یک TLS 1.3 HelloRetryRequest) به sni_callback زمینهی جدید فرستاده میشود، اگر زمینهی مذکور آن را داشته باشد؛ کالبک اصلی دیگر برای آن اتصال فراخوانی نمیشود.
به دلیل مرحلهی اولیهی مذاکرهی اتصال TLS، تنها متدها و ویژگیهای محدودی قابل استفاده هستند، مانند SSLSocket.selected_alpn_protocol() و SSLSocket.context. متدهای SSLSocket.getpeercert()، SSLSocket.get_verified_chain()، SSLSocket.get_unverified_chain()، SSLSocket.cipher() و SSLSocket.compression() نیاز دارند که اتصال TLS از TLS Client Hello فراتر رفته باشد؛ بنابراین نه مقادیر معناداری برمیگردانند و نه میتوان آنها را بهطور امن فراخوانی کرد.
تابع sni_callback باید None را برگرداند تا اجازه دهد مذاکره TLS ادامه یابد. اگر نیاز به شکست TLS باشد، میتوان یک ثابت ALERT_DESCRIPTION_* را برگرداند. مقادیر بازگشتی دیگر باعث خطای مهلک TLS با ALERT_DESCRIPTION_INTERNAL_ERROR خواهند شد.
اگر استثنایی از تابع sni_callback پرتاب شود، اتصال TLS با یک پیام هشدار وخیم TLS ALERT_DESCRIPTION_HANDSHAKE_FAILURE خاتمه مییابد.
اگر OPENSSL_NO_TLSEXT هنگام ساخت کتابخانه OpenSSL تعریفشده باشد، این متد NotImplementedError را پرتاب خواهد کرد.
اضافه شده در نسخهی 3.7.
تغییر یافته در نسخهی 3.15: پس از اینکه کالبک یک SSLSocket.context جدید اختصاص داد، پیامهای ClientHello بعدی روی اتصال به sni_callback زمینهی جدید فرستاده میشوند.
این یک API قدیمی است که برای سازگاری با نسخههای قبلی حفظ شده است. در صورت امکان، باید بهجای آن از sni_callback استفاده کنید. server_name_callback دادهشده مشابه sni_callback است، با این تفاوت که وقتی نام میزبان سرور یک IDN کدگذاریشده با IDN باشد، server_name_callback یک U-label کدگشاییشده را دریافت میکند ("pythön.org").
اگر در کدگشایی نام سرور خطایی رخ دهد، اتصال TLS با یک پیام هشدار مهلک TLS ALERT_DESCRIPTION_INTERNAL_ERROR به کلاینت خاتمه خواهد یافت.
اضافه شده در نسخهی 3.4.
پارامترهای تولید کلید برای تبادل کلید Diffie-Hellman (DH) را بارگذاری کنید. استفاده از تبادل کلید DH، رازداری پیشرو (forward secrecy) را به بهای مصرف منابع محاسباتی (هم در سمت سرور و هم در سمت کلاینت) بهبود میبخشد. پارامتر dhfile باید مسیر پروندهای حاوی پارامترهای DH در قالب PEM باشد.
این تنظیم به سوکتهای کلاینت اعمال نمیشود. همچنین میتوانید از گزینه OP_SINGLE_DH_USE برای بهبود بیشتر امنیت استفاده کنید.
اضافه شده در نسخهی 3.3.
نام منحنی را برای تبادل کلید دیفی-هلمن مبتنی بر منحنی بیضوی (ECDH) تنظیم کنید. ECDH بهطور قابلتوجهی سریعتر از DH معمولی است، در حالی که میتوان گفت به همان اندازه امن است. پارامتر curve_name باید رشتهای باشد که یک منحنی بیضوی شناختهشده را توصیف میکند، برای مثال prime256v1 برای منحنیای که بهطور گسترده پشتیبانی میشود.
این تنظیم برای سوکتهای کلاینت اعمال نمیشود. همچنین میتوانید از گزینه OP_SINGLE_ECDH_USE برای بهبود بیشتر امنیت استفاده کنید.
اگر HAS_ECDH برابر False باشد، این متد در دسترس نیست.
اضافه شده در نسخهی 3.3.
همچنین ببینید
Vincent Bernat.
سوکت پایتون موجود sock را میپیچد و نمونهای از SSLContext.sslsocket_class (پیشفرض SSLSocket) را برمیگرداند. سوکت SSL برگرداندهشده به زمینه، تنظیمات و گواهیهای آن وابسته است. sock باید یک سوکت SOCK_STREAM باشد؛ سایر انواع سوکت پشتیبانی نمیشوند.
پارامتر server_side یک بولی است که مشخص میکند آیا رفتار سمت سرور یا رفتار سمت کلاینت از این سوکت مورد انتظار است.
برای سوکتهای سمت کلاینت، ساخت زمینه با تأخیر انجام میشود؛ اگر سوکت زیرین هنوز متصل نشده باشد، ساخت زمینه پس از فراخوانی connect() روی سوکت انجام خواهد شد. برای سوکتهای سمت سرور، اگر سوکت همتای راه دوری نداشته باشد، فرض میشود که یک سوکت شنود است، و پیچیدن SSL در سمت سرور بهطور خودکار روی اتصالهای کلاینت پذیرفتهشده از طریق متد accept() انجام میشود. این متد ممکن است SSLError را پرتاب کند.
در اتصالهای کلاینت، پارامتر اختیاری server_hostname نام میزبان سرویسی را که به آن متصل میشویم مشخص میکند. این به یک سرور واحد امکان میدهد تا چندین سرویس مبتنی بر SSL با گواهیهای متمایز را میزبانی کند، بسیار مشابه میزبانهای مجازی HTTP. مشخص کردن server_hostname در صورتی که server_side برابر true باشد، باعث پرتاب ValueError میشود.
پارامتر do_handshake_on_connect مشخص میکند که آیا دستدهی SSL (SSL handshake) بهطور خودکار پس از انجام socket.connect() انجام شود، یا اینکه برنامه کاربردی آن را بهصورت صریح با فراخوانی متد SSLSocket.do_handshake() فراخوانی خواهد کرد. فراخوانی صریح SSLSocket.do_handshake() کنترل رفتار مسدودسازی ورودی/خروجی سوکت درگیر در دستدهی را به برنامه میدهد.
پارامتر suppress_ragged_eofs مشخص میکند که متد SSLSocket.recv() باید EOF غیرمنتظره از سوی دیگر اتصال را چگونه اعلام کند. اگر بهصورت True (پیشفرض) مشخص شده باشد، در پاسخ به خطاهای EOF غیرمنتظره که از سوکت زیرین پرتاب میشوند، یک EOF معمولی (یک شیء bytes خالی) را برمیگرداند؛ اگر False باشد، استثناها را به فراخواننده پرتاب خواهد کرد.
session، session را ببینید.
برای دربرگرفتن یک SSLSocket در یک SSLSocket دیگر، از SSLContext.wrap_bio() استفاده کنید.
تغییر یافته در نسخهی 3.5: همیشه اجازه دهید server_hostname ارسال شود، حتی اگر OpenSSL فاقد SNI باشد.
تغییر یافته در نسخهی 3.6: آرگومان session افزوده شد.
تغییر یافته در نسخهی 3.7: این متد بهجای SSLSocket سختکدشده، نمونهای از SSLContext.sslsocket_class را برمیگرداند.
نوع بازگشتی SSLContext.wrap_socket()، بهطور پیشفرض SSLSocket است. میتوان این ویژگی را در نمونههای SSLContext مقداردهی کرد تا یک زیرکلاس سفارشی از SSLSocket بازگردانده شود.
اضافه شده در نسخهی 3.7.
اشیای BIO incoming و outgoing را دربرمیگیرد و نمونهای از SSLContext.sslobject_class (پیشفرض SSLObject) برمیگرداند. روالهای SSL دادههای ورودی را از BIO ورودی میخوانند و دادهها را در BIO خروجی مینویسند.
پارامترهای server_side، server_hostname و session معنایی مشابه با SSLContext.wrap_socket() دارند و به همان شکل اعتبارسنجی میشوند: بهویژه هنگامی که check_hostname فعال است اما هیچ server_hostnameی داده نشده است، یک ValueError بالا آورده میشود، زیرا هیچ نامی برای مطابقت دادن با گواهی همتا وجود نخواهد داشت.
تغییر یافته در نسخهی 3.6: آرگومان session افزوده شد.
تغییر یافته در نسخهی 3.7: این متد بهجای SSLObject سختکد، نمونهای از SSLContext.sslobject_class را برمیگرداند.
تغییر یافته در نسخهی 3.15: پارامترهای server_side، server_hostname و session اکنون مانند روشی که SSLContext.wrap_socket() آنها را اعتبارسنجی میکند، اعتبارسنجی میشوند. پیشتر، یک زمینه با check_hostname فعال و بدون server_hostname پذیرفته میشد و زنجیرهی گواهی را تأیید میکرد اما هرگز هویت همتا را بررسی نمیکرد.
نوع بازگشتی SSLContext.wrap_bio()، بهطور پیشفرض SSLObject است. میتوان این ویژگی را در نمونهای از کلاس بازنویسی کرد تا یک زیرکلاس سفارشی از SSLObject بازگردانده شود.
اضافه شده در نسخهی 3.7.
آمار نشستهای SSL ایجادشده یا مدیریتشده توسط این زمینه را دریافت کنید. یک دیکشنری بازگردانده میشود که نام هر قطعهای از اطلاعات را به مقدار عددی آن نگاشت میکند. برای مثال، در اینجا تعداد کل برخوردها و عدمبرخوردها در نهانگاه نشست از زمان ایجاد زمینه آمده است:
>>> stats = context.session_stats() >>> stats['hits'], stats['misses'] (0, 0)
اینکه آیا باید نام میزبان گواهی همتا در SSLSocket.do_handshake() تطبیق داده شود یا خیر. verify_mode زمینه باید روی CERT_OPTIONAL یا CERT_REQUIRED تنظیم شده باشد و شما باید برای تطبیق نام میزبان، server_hostname را به wrap_socket() ارسال کنید. فعالسازی بررسی نام میزبان، verify_mode را بهطور خودکار از CERT_NONE به CERT_REQUIRED تنظیم میکند. تا زمانی که بررسی نام میزبان فعال است، نمیتوان آن را دوباره روی CERT_NONE تنظیم کرد. پروتکل PROTOCOL_TLS_CLIENT بررسی نام میزبان را بهصورت پیشفرض فعال میکند. در پروتکلهای دیگر، بررسی نام میزبان باید بهصورت صریح فعال شود.
مثال:
import socket, ssl context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) context.verify_mode = ssl.CERT_REQUIRED context.check_hostname = True context.load_default_certs() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssl_sock = context.wrap_socket(s, server_hostname='www.verisign.com') ssl_sock.connect(('www.verisign.com', 443))
اضافه شده در نسخهی 3.4.
تغییر یافته در نسخهی 3.7: اکنون اگر بررسی نام میزبان فعال باشد و verify_mode برابر با CERT_NONE باشد، verify_mode بهطور خودکار به CERT_REQUIRED تغییر میکند. پیشتر، همین عملیات با یک ValueError شکست میخورد.
کلیدهای TLS را هر زمان که مادهی کلید (key material) تولید یا دریافت شود، در یک پرونده گزارش کلید (keylog file) مینویسد. پرونده گزارش کلید فقط برای اهداف اشکالزدایی طراحی شده است. قالب پرونده توسط NSS مشخص شده است و توسط بسیاری از تحلیلگرهای ترافیک مانند Wireshark استفاده میشود. پرونده گزارش در حالت فقط-افزودنی (append-only) باز میشود. نوشتنها بین نخها همگامسازی میشوند، اما بین فرآیندها همگامسازی نمیشوند.
اضافه شده در نسخهی 3.8.
یک عضو enum از TLSVersion که نشاندهندهی بالاترین نسخهی پشتیبانیشدهی TLS است. مقدار آن بهطور پیشفرض برابر با TLSVersion.MAXIMUM_SUPPORTED است. این ویژگی برای پروتکلهایی غیر از PROTOCOL_TLS، PROTOCOL_TLS_CLIENT و PROTOCOL_TLS_SERVER فقطخواندنی است.
ویژگیهای maximum_version، minimum_version و SSLContext.options همگی بر نسخههای پشتیبانیشدهی SSL و TLS زمینه اثر میگذارند. پیادهسازی از ترکیبهای نامعتبر جلوگیری نمیکند. برای مثال، زمینهای که در options آن OP_NO_TLSv1_2 وجود دارد و maximum_version آن روی TLSVersion.TLSv1_2 تنظیم شده است، نمیتواند یک اتصال TLS 1.2 برقرار کند.
اضافه شده در نسخهی 3.7.
مانند SSLContext.maximum_version است، با این تفاوت که پایینترین نسخه پشتیبانیشده یا TLSVersion.MINIMUM_SUPPORTED است.
اضافه شده در نسخهی 3.7.
تعداد بلیتهای نشست (session tickets) TLS 1.3 برای یک زمینه PROTOCOL_TLS_SERVER را کنترل کنید. این تنظیم تأثیری بر اتصالات TLS 1.0 تا 1.2 ندارد.
اضافه شده در نسخهی 3.8.
عدد صحیحی که مجموعهی گزینههای SSL فعال در این زمینه را نشان میدهد. مقدار پیشفرض OP_ALL است، اما میتوانید گزینههای دیگری مانند OP_NO_SSLv2 را با OR کردن آنها با یکدیگر مشخص کنید.
تغییر یافته در نسخهی 3.6: SSLContext.options پرچمهای Options را برمیگرداند:
>>> ssl.create_default_context().options <Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|OP_NO_COMPRESSION: 2197947391>
منسوخ شده از نسخهی 3.7: همهی گزینههای OP_NO_SSL* و OP_NO_TLS* از پایتون 3.7 منسوخ شدهاند. در عوض از SSLContext.minimum_version و SSLContext.maximum_version استفاده کنید.
احراز هویت کلاینت پس از دستدهی (post-handshake) در TLS 1.3 را فعال کنید. احراز هویت پس از دستدهی بهطور پیشفرض غیرفعال است و یک سرور تنها میتواند در حین دستدهی اولیه، یک گواهی کلاینت TLS را درخواست کند. هنگامی که فعال باشد، یک سرور میتواند در هر زمانی پس از دستدهی، یک گواهی کلاینت TLS را درخواست کند.
هنگامی که روی سوکتهای سمت کلاینت فعال شود، کلاینت به سرور علامت میدهد که از احراز هویت پس از دستدهی (post-handshake authentication) پشتیبانی میکند.
هنگامی که در سوکتهای سمت سرور فعال باشد، SSLContext.verify_mode نیز باید روی CERT_OPTIONAL یا CERT_REQUIRED تنظیم شود. تبادل واقعی گواهی کلاینت تا زمانی که SSLSocket.verify_client_post_handshake() فراخوانی شود و مقداری ورودی/خروجی انجام شود، به تعویق میافتد.
اضافه شده در نسخهی 3.8.
نسخهی پروتکل انتخابشده هنگام ساخت زمینه. این ویژگی فقطخواندنی است.
اینکه آیا check_hostname در نبود افزونهی نام جایگزین موضوع، برای تأیید نام مشترک موضوع گواهی بهعنوان حالت جایگزین عمل میکند یا خیر (پیشفرض: true).
اضافه شده در نسخهی 3.7.
تغییر یافته در نسخهی 3.10: این پرچم در OpenSSL پیش از نسخه 1.1.1l تاثیری نداشت. پایتون 3.8.9، 3.9.3 و 3.10 شامل راهحلهای موقتی برای نسخههای پیشین هستند.
یک عدد صحیح که سطح امنیت زمینه را نشان میدهد. این ویژگی فقطخواندنی است.
اضافه شده در نسخهی 3.10.
پرچمها برای عملیات تأیید صحت گواهی. میتوانید پرچمهایی مانند VERIFY_CRL_CHECK_LEAF را با OR کردن آنها با یکدیگر تنظیم کنید. بهطور پیشفرض، OpenSSL نه به فهرستهای ابطال گواهی (CRLs) نیاز دارد و نه آنها را تأیید صحت میکند.
اضافه شده در نسخهی 3.4.
تغییر یافته در نسخهی 3.6: SSLContext.verify_flags پرچمهای VerifyFlags را برمیگرداند:
>>> ssl.create_default_context().verify_flags <VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>
اینکه آیا باید برای تأیید گواهیهای سایر همتایان تلاش شود و در صورت شکست تأیید، چگونه رفتار شود. این ویژگی باید یکی از CERT_NONE، CERT_OPTIONAL یا CERT_REQUIRED باشد.
تغییر یافته در نسخهی 3.6: SSLContext.verify_mode یک enum از VerifyMode برمیگرداند:
>>> ssl.create_default_context().verify_mode <VerifyMode.CERT_REQUIRED: 2>
احراز هویت TLS-PSK (کلید از پیش اشتراکگذاشتهشده) را روی یک اتصال سمت کلاینت فعال میکند.
بهطور کلی، احراز هویت مبتنی بر گواهی باید بر این روش ترجیح داده شود.
پارامتر callback یک شیء فراخوانیپذیر با امضای زیر است: def callback(hint: str | None) -> tuple[str | None, bytes]. پارامتر hint یک راهنمای هویت اختیاری است که توسط سرور ارسال میشود. مقدار بازگشتی یک تاپل بهشکل (client-identity, psk) است. client-identity یک رشته اختیاری است که ممکن است سرور از آن برای انتخاب یک PSK متناظر برای کلاینت استفاده کند. این رشته باید هنگام کدگذاری با UTF-8 کمتر یا مساوی 256 هشتبیتی باشد. PSK یک bytes-like object است که کلید از پیش اشتراکگذاریشده را نشان میدهد. برای رد کردن اتصال، یک PSK با طول صفر برگردانید.
با تنظیم callback به None، هر کالبک موجود حذف میشود.
نکته
هنگام استفاده از TLS 1.3:
پارامتر hint همیشه None است.
client-identity باید یک رشته غیرخالی باشد.
the PSK is only offered to the server, which may decline it and
authenticate with a certificate instead. That certificate is checked
according to verify_mode, so a client relying on
the PSK to authenticate the server should not set it to
CERT_NONE, or should check SSLSocket.session_reused,
which is true if the PSK was used. A PSK handshake does not need any
CA certificates to be loaded when using CERT_REQUIRED.
a zero length PSK does not reject the connection, it only means that no PSK is offered. An exception raised by the callback has the same effect.
the PSK is used with the SHA-256 handshake digest, so a server can only accept it with a cipher suite that uses SHA-256.
نمونه استفاده:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) context.check_hostname = False context.verify_mode = ssl.CERT_NONE context.maximum_version = ssl.TLSVersion.TLSv1_2 context.set_ciphers('PSK') # A simple lambda: psk = bytes.fromhex('c0ffee') context.set_psk_client_callback(lambda hint: (None, psk)) # A table using the hint from the server: psk_table = { 'ServerId_1': bytes.fromhex('c0ffee'), 'ServerId_2': bytes.fromhex('facade') } def callback(hint): return 'ClientId_1', psk_table.get(hint, b'') context.set_psk_client_callback(callback)
این متد در صورتی که HAS_PSK برابر False باشد، NotImplementedError را پرتاب خواهد کرد.
اضافه شده در نسخهی 3.13.
احراز هویت TLS-PSK (کلید از پیش اشتراکگذاشتهشده) را در یک اتصال سمت سرور فعال میکند.
بهطور کلی، احراز هویت مبتنی بر گواهی باید بر این روش ترجیح داده شود.
پارامتر callback یک شیء فراخوانیپذیر با امضای def callback(identity: str | None) -> bytes است. پارامتر identity یک هویت اختیاری ارسالشده از سوی کلاینت است که میتوان از آن برای انتخاب یک PSK متناظر استفاده کرد. مقدار بازگشتی یک bytes-like object است که کلید از پیش به اشتراک گذاشتهشده را نشان میدهد. برای رد کردن اتصال، یک PSK با طول صفر برگردانید.
با تنظیم callback به None، هر کالبک موجود حذف میشود.
پارامتر identity_hint یک رشتهی راهنمای هویت اختیاری است که به کلاینت ارسال میشود. این رشته باید هنگام کدگذاری با UTF-8 کمتر یا مساوی 256 هشتبیتی باشد.
نکته
هنگام استفاده از TLS 1.3:
هنگام استفاده از TLS 1.3، پارامتر identity_hint به کلاینت ارسال نمیشود.
a zero length PSK, or an exception raised by the callback, only
declines the client's PSK. If a certificate was loaded with
load_cert_chain(), the handshake continues with
certificate authentication instead of failing, so a server relying on
the PSK to authenticate clients should not load one, or should check
SSLSocket.session_reused, which is true if the PSK was used.
the PSK can only be accepted with a cipher suite that uses SHA-256. When a certificate is loaded, OpenSSL's default TLS 1.3 cipher suite order selects a SHA-384 suite instead, so the PSK is declined even if it matches.
نمونه استفاده:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.maximum_version = ssl.TLSVersion.TLSv1_2 context.set_ciphers('PSK') # A simple lambda: psk = bytes.fromhex('c0ffee') context.set_psk_server_callback(lambda identity: psk) # A table using the identity of the client: psk_table = { 'ClientId_1': bytes.fromhex('c0ffee'), 'ClientId_2': bytes.fromhex('facade') } def callback(identity): return psk_table.get(identity, b'') context.set_psk_server_callback(callback, 'ServerId_1')
این متد در صورتی که HAS_PSK برابر False باشد، NotImplementedError را پرتاب خواهد کرد.
اضافه شده در نسخهی 3.13.
گواهیها بهطور کلی بخشی از یک سیستم کلید عمومی / کلید خصوصی هستند. در این سیستم، به هر اصل (principal) (که ممکن است یک ماشین، یک شخص، یا یک سازمان باشد) یک کلید رمزنگاری دوبخشی یکتا اختصاص داده میشود. یک بخش از کلید عمومی است و کلید عمومی نامیده میشود؛ بخش دیگر مخفی نگه داشته میشود و کلید خصوصی نامیده میشود. این دو بخش با هم مرتبط هستند، به این معنا که اگر پیامی را با یکی از بخشها رمزنگاری کنید، میتوانید آن را با بخش دیگر رمزگشایی کنید، و فقط با بخش دیگر.
یک گواهی حاوی اطلاعاتی درباره دو طرف (principal) است. این گواهی حاوی نام یک موضوع و کلید عمومی آن موضوع است. همچنین حاوی اظهار طرف دوم، یعنی صادرکننده، است مبنی بر اینکه موضوع همان هویتی است که ادعا میکند، و اینکه این کلید واقعاً کلید عمومی موضوع است. اظهار اکسپورتکننده با کلید خصوصی اکسپورتکننده امضا شده است، که تنها صادرکننده آن را میداند. با این حال، هر کسی میتواند با یافتن کلید عمومی صادرکننده، رمزگشایی اظهار با آن کلید، و مقایسه آن با سایر اطلاعات موجود در گواهی، صحت اظهار صادرکننده را تأیید کند. گواهی همچنین حاوی اطلاعاتی درباره بازه زمانی اعتبار آن است. این موضوع بهصورت دو فیلد با نامهای "notBefore" و "notAfter" بیان میشود.
در کاربرد گواهیها در پایتون، یک کلاینت یا سرور میتواند از یک گواهی برای اثبات هویت خود استفاده کند. همچنین میتوان از طرف دیگر یک اتصال شبکه خواست که گواهیای ارائه دهد، و آن گواهی میتواند تا حد رضایت کلاینت یا سروری که چنین اعتبارسنجیای را لازم میداند، اعتبارسنجی شود. میتوان تلاش اتصال را طوری تنظیم کرد که در صورت ناموفق بودن اعتبارسنجی، استثنایی پرتاب کند. اعتبارسنجی بهطور خودکار توسط چارچوب OpenSSL زیرین انجام میشود؛ برنامه نیازی ندارد نگران سازوکار آن باشد. اما برنامه معمولاً نیاز دارد مجموعههایی از گواهیها را فراهم کند تا این فرایند انجام شود.
پایتون از پروندهها برای نگهداری گواهیها استفاده میکند. آنها باید با قالب «PEM» باشند (به RFC 1422 مراجعه کنید)، که یک قالب کدگذاریشده به base-64 است و با یک خط سرآیند و یک خط پایانی احاطه شده است:
-----BEGIN CERTIFICATE----- ... (certificate in base64 PEM encoding) ... -----END CERTIFICATE-----
پروندههای پایتون که حاوی گواهیها هستند، میتوانند حاوی دنبالهای از گواهیها باشند که گاهی به آن زنجیره گواهی میگویند. این زنجیره باید با گواهی مشخص برای هویتی که کلاینت یا سرور «است» آغاز شود، سپس با گواهی برای صادرکننده آن گواهی، و سپس با گواهی برای صادرکننده آن گواهی، و به همین ترتیب تا بالای زنجیره ادامه یابد تا به گواهیای برسید که خودامضا است، یعنی گواهیای که موضوع و صادرکننده یکسانی دارد و گاهی به آن گواهی ریشه میگویند. گواهیها باید فقط در پرونده گواهی به یکدیگر الحاق شوند. برای مثال، فرض کنید یک زنجیره سهگواهی داشتیم، از گواهی سرور ما تا گواهی مرجع صدور گواهی که گواهی سرور ما را امضا کرده است، تا گواهی ریشه سازمانی که گواهی مرجع صدور گواهی را صادر کرده است:
-----BEGIN CERTIFICATE----- ... (گواهی برای سرور شما)... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- ... (گواهی برای CA)... -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- ... (گواهی ریشه برای صادرکننده CA)... -----END CERTIFICATE-----
اگر میخواهید اعتبارسنجی گواهی طرف دیگر اتصال را الزامی کنید، باید یک پرونده «گواهیهای CA» فراهم کنید که حاوی زنجیرههای گواهی برای هر صادرکنندهای باشد که مایل به اعتماد به آن هستید. باز هم، این پرونده فقط شامل این زنجیرهها است که بههمپیوسته شدهاند. برای اعتبارسنجی، پایتون از اولین زنجیرهای استفاده میکند که در پرونده یافت شود و مطابقت داشته باشد. میتوان با فراخوانی SSLContext.load_default_certs() از پرونده گواهیهای پلتفرم استفاده کرد؛ این کار بهصورت خودکار با create_default_context() انجام میشود.
اغلب کلید خصوصی در همان پرونده گواهی ذخیره میشود؛ در این صورت، فقط باید پارامتر certfile را به SSLContext.load_cert_chain() ارسال کنید. اگر کلید خصوصی همراه گواهی ذخیره شده باشد، باید قبل از اولین گواهی در زنجیره گواهی قرار گیرد:
-----BEGIN RSA PRIVATE KEY----- ... (کلید خصوصی با کدگذاری base64) ... -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- ... (گواهی با کدگذاری base64 PEM) ... -----END CERTIFICATE-----
اگر میخواهید سروری بسازید که خدمات اتصال رمزگذاریشده با SSL را ارائه دهد، باید برای آن خدمت یک گواهی تهیه کنید. روشهای زیادی برای تهیه گواهیهای مناسب وجود دارد، مانند خرید یک گواهی از یک مرجع صدور گواهی. یک روش رایج دیگر، تولید یک گواهی خودامضا است. سادهترین راه برای انجام این کار، استفاده از بسته OpenSSL، با موردی مانند زیر است:
% openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem Generating a 1024 bit RSA private key .......++++++ .............................++++++ writing new private key to 'cert.pem' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:MyState Locality Name (eg, city) []:Some City Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc. Organizational Unit Name (eg, section) []:My Group Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com Email Address []:ops@myserver.mygroup.myorganization.com %
عیب یک گواهی خودامضا این است که خودش گواهی ریشهی خود محسوب میشود و هیچکس دیگری آن را در نهانگاه گواهیهای ریشهی شناختهشده (و مورد اعتماد) خود نخواهد داشت.
برای بررسی وجود پشتیبانی SSL در یک نصب پایتون، کد کاربر باید از الگوی زیر استفاده کند:
try: import ssl except ImportError: pass else: ... # do something that requires SSL support
این مثال یک زمینهی SSL (SSL context) با تنظیمات امنیتی توصیهشده برای سوکتهای کلاینت، از جمله تأیید خودکار گواهی، ایجاد میکند:
>>> context = ssl.create_default_context()
اگر ترجیح میدهید تنظیمات امنیتی را خودتان تنظیم کنید، میتوانید یک زمینه را از پایه ایجاد کنید (اما مراقب باشید که ممکن است تنظیمات را بهدرستی انجام ندهید):
>>> context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) >>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
(این قطعهکد فرض میکند که سیستمعامل شما بستهای از تمام گواهیهای CA را در /etc/ssl/certs/ca-bundle.crt قرار میدهد؛ در غیر این صورت، با خطا مواجه میشوید و باید محل را تنظیم کنید)
پروتکل PROTOCOL_TLS_CLIENT زمینه را برای اعتبارسنجی گواهی و تأیید نام میزبان پیکربندی میکند. verify_mode به CERT_REQUIRED تنظیم شده است و check_hostname به True تنظیم شده است. همهی پروتکلهای دیگر زمینههای SSL را با پیشفرضهای ناامن ایجاد میکنند.
وقتی از زمینه برای اتصال به یک سرور استفاده میکنید، CERT_REQUIRED و check_hostname گواهی سرور را اعتبارسنجی میکنند: این اعتبارسنجی تضمین میکند که گواهی سرور با یکی از گواهیهای CA امضا شده باشد، صحت امضا را بررسی میکند و سایر ویژگیها مانند اعتبار و هویت نام میزبان را تأیید میکند:
>>> conn = context.wrap_socket(socket.socket(socket.AF_INET), ... server_hostname="www.python.org") >>> conn.connect(("www.python.org", 443))
سپس میتوانید گواهی را دریافت کنید:
>>> cert = conn.getpeercert()
بازرسی دیداری نشان میدهد که گواهی، سرویس موردنظر (یعنی میزبان HTTPS www.python.org) را شناسایی میکند:
>>> pprint.pprint(cert) {'OCSP': ('http://ocsp.digicert.com',), 'caIssuers': ('http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt',), 'crlDistributionPoints': ('http://crl3.digicert.com/sha2-ev-server-g1.crl', 'http://crl4.digicert.com/sha2-ev-server-g1.crl'), 'issuer': ((('countryName', 'US'),), (('organizationName', 'DigiCert Inc'),), (('organizationalUnitName', 'www.digicert.com'),), (('commonName', 'DigiCert SHA2 Extended Validation Server CA'),)), 'notAfter': 'Sep 9 12:00:00 2016 GMT', 'notBefore': 'Sep 5 00:00:00 2014 GMT', 'serialNumber': '01BB6F00122B177F36CAB49CEA8B6B26', 'subject': ((('businessCategory', 'Private Organization'),), (('1.3.6.1.4.1.311.60.2.1.3', 'US'),), (('1.3.6.1.4.1.311.60.2.1.2', 'Delaware'),), (('serialNumber', '3359300'),), (('streetAddress', '16 Allen Rd'),), (('postalCode', '03894-4801'),), (('countryName', 'US'),), (('stateOrProvinceName', 'NH'),), (('localityName', 'Wolfeboro'),), (('organizationName', 'Python Software Foundation'),), (('commonName', 'www.python.org'),)), 'subjectAltName': (('DNS', 'www.python.org'), ('DNS', 'python.org'), ('DNS', 'pypi.org'), ('DNS', 'docs.python.org'), ('DNS', 'testpypi.org'), ('DNS', 'bugs.python.org'), ('DNS', 'wiki.python.org'), ('DNS', 'hg.python.org'), ('DNS', 'mail.python.org'), ('DNS', 'packaging.python.org'), ('DNS', 'pythonhosted.org'), ('DNS', 'www.pythonhosted.org'), ('DNS', 'test.pythonhosted.org'), ('DNS', 'us.pycon.org'), ('DNS', 'id.python.org')), 'version': 3}
اکنون که کانال SSL برقرار شد و گواهی تأیید شد، میتوانید ارتباط با سرور را آغاز کنید:
>>> conn.sendall(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n") >>> pprint.pprint(conn.recv(1024).split(b"\r\n")) [b'HTTP/1.1 200 OK', b'Date: Sat, 18 Oct 2014 18:27:20 GMT', b'Server: nginx', b'Content-Type: text/html; charset=utf-8', b'X-Frame-Options: SAMEORIGIN', b'Content-Length: 45679', b'Accept-Ranges: bytes', b'Via: 1.1 varnish', b'Age: 2188', b'X-Served-By: cache-lcy1134-LCY', b'X-Cache: HIT', b'X-Cache-Hits: 11', b'Vary: Cookie', b'Strict-Transport-Security: max-age=63072000; includeSubDomains', b'Connection: close', b'', b'']
بحث ملاحظات امنیتی را در زیر ببینید.
برای راهاندازی سرور، معمولاً باید یک گواهی سرور و یک کلید خصوصی داشته باشید، که هر کدام در یک پرونده قرار دارند. ابتدا یک زمینه حاوی کلید و گواهی ایجاد میکنید، تا کلاینتها بتوانند اصالت شما را بررسی کنند. سپس یک سوکت باز میکنید، آن را به یک پورت پیوند میدهید، متد listen() را روی آن فراخوانی میکنید و منتظر اتصال کلاینتها میمانید:
import socket, ssl context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile") bindsocket = socket.socket() bindsocket.bind(('myaddr.example.com', 10023)) bindsocket.listen(5)
هنگامی که یک کلاینت متصل شد، accept() را روی سوکت فراخوانی کنید تا سوکت جدید را از طرف مقابل دریافت کنید و از متد SSLContext.wrap_socket() زمینه برای ایجاد یک سوکت SSL در سمت سرور برای اتصال استفاده کنید:
while True: newsocket, fromaddr = bindsocket.accept() connstream = context.wrap_socket(newsocket, server_side=True) try: deal_with_client(connstream) finally: connstream.shutdown(socket.SHUT_RDWR) connstream.close()
سپس دادهها را از connstream میخوانید و تا زمانی که کار شما با کلاینت تمام نشده است (یا کار کلاینت با شما تمام نشده است)، با آنها کاری انجام میدهید:
def deal_with_client(connstream): data = connstream.recv(1024) # empty data means the client is finished with us while data: if not do_something(connstream, data): # we'll assume do_something returns False # when we're finished with client break data = connstream.recv(1024) # finished with client
و به گوشدادن برای اتصالهای جدید کلاینت بازگردید (البته، یک سرور واقعی احتمالاً هر اتصال کلاینت را در یک نخ جداگانه مدیریت میکند، یا سوکتها را در حالت غیرمسدودکننده قرار میدهد و از یک حلقه رویداد استفاده میکند).
سوکتهای SSL در حالت غیرمسدودکننده (non-blocking) کمی متفاوت از سوکتهای معمولی رفتار میکنند. بنابراین، هنگام کار با سوکتهای غیرمسدودکننده، باید به چند نکته توجه داشته باشید:
بیشتر متدهای SSLSocket در صورتی که یک عملیات ورودی/خروجی باعث مسدود شدن شود، بهجای BlockingIOError، یا SSLWantWriteError یا SSLWantReadError را پرتاب میکنند. اگر یک عملیات خواندن روی سوکت زیرین لازم باشد، SSLWantReadError پرتاب میشود، و برای یک عملیات نوشتن روی سوکت زیرین، SSLWantWriteError پرتاب میشود. توجه داشته باشید که تلاش برای نوشتن در یک سوکت SSL ممکن است ابتدا به خواندن از سوکت زیرین نیاز داشته باشد، و تلاش برای خواندن از سوکت SSL ممکن است به یک نوشتن قبلی روی سوکت زیرین نیاز داشته باشد.
تغییر یافته در نسخهی 3.5: در نسخههای پیشین پایتون، متد SSLSocket.send() بهجای پرتاب کردن SSLWantWriteError یا SSLWantReadError، صفر را برمیگرداند.
فراخوانی select() به شما میگوید که سوکت در سطح سیستمعامل قابل خواندن (یا نوشتن) است، اما این به آن معنا نیست که داده کافی در لایهی بالایی SSL وجود دارد. برای مثال، ممکن است تنها بخشی از یک فریم SSL رسیده باشد. بنابراین، باید آماده باشید تا شکستهای SSLSocket.recv() و SSLSocket.send() را مدیریت کنید و پس از فراخوانی دیگری از select() دوباره تلاش کنید.
در مقابل، از آنجا که لایه SSL چارچوببندی خاص خود را دارد، ممکن است یک سوکت SSL هنوز دادهای برای خواندن در دسترس داشته باشد، بدون آنکه select() از آن آگاه باشد. بنابراین، ابتدا باید SSLSocket.recv() را فراخوانی کنید تا هر دادهای که ممکن است در دسترس باشد تخلیه شود، و سپس تنها در صورت لزوم، در یک فراخوانی select() مسدود شوید.
(البته، ملاحظات مشابهی هنگام استفاده از سایر اولیهها مانند poll() یا موارد موجود در ماژول selectors اعمال میشود)
خود دستدهی SSL (SSL handshake) غیرمسدودکننده خواهد بود: متد SSLSocket.do_handshake() باید تا زمانی که با موفقیت بازگشت کند، دوباره فراخوانی شود. در اینجا خلاصهای با استفاده از select() برای منتظر ماندن تا آماده شدن سوکت آمده است:
while True: try: sock.do_handshake() break except ssl.SSLWantReadError: select.select([sock], [], []) except ssl.SSLWantWriteError: select.select([], [sock], [])
همچنین ببینید
ماژول asyncio از سوکتهای SSL غیرمسدود پشتیبانی میکند و API جریانها سطح بالاتری را فراهم میکند. این ماژول رویدادها را با استفاده از ماژول selectors پایش میکند و استثناهای SSLWantWriteError، SSLWantReadError و BlockingIOError را مدیریت میکند. همچنین دستدهی SSL را بهصورت ناهمگام اجرا میکند.
اضافه شده در نسخهی 3.5.
از زمان معرفی ماژول SSL در Python 2.6، کلاس SSLSocket دو حوزهی عملکردی مرتبط اما متمایز را ارائه کرده است:
مدیریت پروتکل SSL
ورودی/خروجی شبکه
API ورودی/خروجی شبکه دقیقاً مشابه API ارائهشده توسط socket.socket است که SSLSocket نیز از آن ارثبری میکند. این موضوع امکان استفاده از یک سوکت SSL بهعنوان جایگزین مستقیم (drop-in replacement) برای یک سوکت معمولی را فراهم میکند و افزودن پشتیبانی SSL به یک برنامه موجود را بسیار آسان میسازد.
ترکیب مدیریت پروتکل SSL و ورودی/خروجی شبکه معمولاً بهخوبی کار میکند، اما در برخی موارد اینطور نیست. یک نمونه، چارچوبهای ورودی/خروجی ناهمگام هستند که میخواهند از مدل متفاوتی برای تسهیم ورودی/خروجی (IO multiplexing) نسبت به مدل «select/poll روی توصیفگر پرونده» (مبتنی بر آمادگی) استفاده کنند؛ مدلی که توسط socket.socket و روالهای داخلی ورودی/خروجی سوکت OpenSSL فرض میشود. این موضوع بیشتر برای سکوهایی مانند ویندوز مرتبط است که این مدل در آنها کارآمد نیست. برای این منظور، گونهای از SSLSocket با محدودهای محدودتر به نام SSLObject ارائه شده است.
یک نسخه با محدودهی کاهشیافته از SSLSocket که نشاندهندهی یک نمونه از پروتکل SSL است و فاقد هرگونه متد ورودی/خروجی شبکه است. این کلاس معمولاً توسط نویسندگان چارچوبهایی استفاده میشود که میخواهند ورودی/خروجی ناهمگام برای SSL را از طریق بافرهای حافظه پیادهسازی کنند.
این کلاس یک رابط را بر فراز یک شیء SSL سطح پایین، که OpenSSL آن را پیادهسازی کرده است، پیادهسازی میکند. این شیء وضعیت یک اتصال SSL را ثبت میکند، اما خود هیچگونه ورودی/خروجی (IO) شبکهای فراهم نمیکند. ورودی/خروجی باید از طریق اشیای "BIO" جداگانه انجام شود که لایهی انتزاعی ورودی/خروجی OpenSSL هستند.
این کلاس سازندهی عمومی ندارد. یک نمونهی SSLObject باید با استفاده از متد wrap_bio() ایجاد شود. این متد نمونهی SSLObject را ایجاد میکند و آن را به یک جفت BIO متصل میکند. BIO ورودی برای انتقال داده از پایتون به نمونهی پروتکل SSL استفاده میشود، در حالی که BIO خروجی برای انتقال داده در جهت معکوس استفاده میشود.
متدهای زیر در دسترس هستند:
در مقایسه با SSLSocket، این شیء فاقد ویژگیهای زیر است:
هر شکلی از ورودی/خروجی شبکه؛ recv() و send() فقط از بافرهای زیربنایی MemoryBIO میخوانند و در آنها مینویسند.
هیچ سازوکاری برای do_handshake_on_connect وجود ندارد. شما باید همیشه do_handshake() را بهصورت دستی فراخوانی کنید تا دستدهی آغاز شود.
هیچ مدیریتی برای suppress_ragged_eofs وجود ندارد. تمام شرایط پایان پرونده که پروتکل را نقض میکنند، از طریق استثنا SSLEOFError گزارش میشوند.
فراخوانی متد unwrap() چیزی برنمیگرداند، برخلاف یک سوکت SSL که در آن سوکت زیرین را برمیگرداند.
کالبک server_name_callback که به SSLContext.set_servername_callback() ارسال میشود، بهعنوان اولین پارامتر خود، یک نمونه SSLObject را بهجای یک نمونه SSLSocket دریافت خواهد کرد.
چند نکته مرتبط با استفاده از SSLObject:
تمام عملیات ورودی/خروجی روی یک SSLObject به صورت غیرمسدود است. این بدان معناست که برای مثال read() در صورتی استثنای SSLWantReadError را پرتاب میکند که به داده بیشتری نسبت به آنچه BIO ورودی در دسترس دارد نیاز داشته باشد.
تغییر یافته در نسخهی 3.7: نمونههای SSLObject باید با wrap_bio() ایجاد شوند. در نسخههای پیشین، امکان ایجاد مستقیم نمونهها وجود داشت. این کار هرگز مستند نشده بود و بهطور رسمی پشتیبانی نمیشد.
یک SSLObject با استفاده از بافرهای حافظه با دنیای بیرون ارتباط برقرار میکند. کلاس MemoryBIO یک بافر حافظه فراهم میکند که میتواند برای این منظور استفاده شود. این کلاس یک شیء BIO حافظهی OpenSSL (ورودی/خروجی پایه) را پوشش میدهد:
یک بافر حافظه (memory buffer) که میتوان از آن برای انتقال داده بین پایتون و یک نمونه از پروتکل SSL استفاده کرد.
تعداد بایتهای موجود در بافر حافظه را در حال حاضر برمیگرداند.
یک بولی که نشان میدهد آیا BIO حافظهای (memory BIO) در موقعیت پایان پرونده قرار دارد یا خیر.
حداکثر n بایت از بافر حافظه بخوانید. اگر n مشخص نشده باشد یا منفی باشد، تمام بایتها برگردانده میشوند.
بایتهای buf را در BIO حافظهای (memory BIO) بنویسید. آرگومان buf باید شیءای باشد که از پروتکل بافر پشتیبانی میکند.
مقدار بازگشتی تعداد بایتهای نوشتهشده است، که همیشه برابر با طول buf است.
اضافه شده در نسخهی 3.6.
برای استفاده سمت کلاینت، اگر الزامات خاصی برای سیاست امنیتی خود ندارید، اکیداً توصیه میشود که از تابع create_default_context() برای ایجاد زمینهی SSL خود استفاده کنید. این تابع گواهیهای CA مورد اعتماد سیستم را بارگذاری میکند، اعتبارسنجی گواهی و بررسی نام میزبان را فعال میکند و تلاش میکند تنظیمات پروتکل و رمز نسبتاً امن را انتخاب کند.
برای مثال، به این صورت میتوانید از کلاس smtplib.SMTP برای ایجاد یک اتصال مورد اعتماد و امن به یک سرور SMTP استفاده کنید:
>>> import ssl, smtplib >>> smtp = smtplib.SMTP("mail.python.org", port=587) >>> context = ssl.create_default_context() >>> smtp.starttls(context=context) (220, b'2.0.0 Ready to start TLS')
اگر برای اتصال به یک گواهی کلاینت نیاز باشد، میتوان آن را با SSLContext.load_cert_chain() افزود.
در مقابل، اگر خودتان زمینهی SSL را با فراخوانی سازندهی SSLContext ایجاد کنید، نه اعتبارسنجی گواهی و نه بررسی نام میزبان بهطور پیشفرض فعال نخواهند بود. اگر این کار را انجام میدهید، لطفاً برای دستیابی به سطح امنیت مناسب، پاراگرافهای زیر را بخوانید.
هنگام فراخوانی مستقیم سازندهی SSLContext، CERT_NONE پیشفرض است. از آنجا که این حالت طرف مقابل را احراز هویت نمیکند، میتواند ناامن باشد، بهویژه در حالت کلاینت که در بیشتر موارد میخواهید از اصالت سروری که با آن در ارتباط هستید اطمینان حاصل کنید. بنابراین، در حالت کلاینت، بهشدت توصیه میشود از CERT_REQUIRED استفاده کنید. با این حال، این مورد بهتنهایی کافی نیست؛ همچنین باید بررسی کنید که گواهیی سرور، که میتوان آن را با فراخوانی SSLSocket.getpeercert() دریافت کرد، با سرویس موردنظر مطابقت دارد. در بسیاری از پروتکلها و کاربردها، سرویس را میتوان با نام میزبان شناسایی کرد. این بررسی رایج، هنگامی که SSLContext.check_hostname فعال باشد، بهصورت خودکار انجام میشود.
تغییر یافته در نسخهی 3.7: تطبیق نام میزبان اکنون توسط OpenSSL انجام میشود. پایتون دیگر از match_hostname() استفاده نمیکند.
در حالت سرور، اگر میخواهید کلاینتهای خود را با استفاده از لایه SSL احراز هویت کنید (بهجای استفاده از یک مکانیزم احراز هویت سطح بالاتر)، همچنین باید CERT_REQUIRED را تعیین کنید و بهطور مشابه گواهی کلاینت را بررسی نمایید.
نسخههای 2 و 3 SSL ناامن تلقی میشوند و بنابراین استفاده از آنها خطرناک است. اگر میخواهید حداکثر سازگاری میان کلاینتها و سرورها برقرار باشد، توصیه میشود از PROTOCOL_TLS_CLIENT یا PROTOCOL_TLS_SERVER بهعنوان نسخهی پروتکل استفاده کنید. SSLv2 و SSLv3 بهطور پیشفرض غیرفعال هستند.
>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) >>> client_context.minimum_version = ssl.TLSVersion.TLSv1_2 >>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3
زمینه کلاینت SSL ایجادشده در بالا، فقط اتصالهای TLSv1.2 و TLSv1.3 (اگر توسط سیستم شما پشتیبانی شود) به یک سرور را مجاز میکند. PROTOCOL_TLS_CLIENT بهطور پیشفرض شامل اعتبارسنجی گواهی و بررسیهای نام میزبان میشود. شما باید گواهیها را در زمینه بارگذاری کنید.
اگر الزامات امنیتی پیشرفته دارید، تنظیم دقیق رمزهایی که هنگام مذاکرهی یک نشست SSL فعال میشوند از طریق متد SSLContext.set_ciphers() امکانپذیر است. از Python 3.2.3 به بعد، ماژول ssl برخی از رمزهای ضعیف را بهطور پیشفرض غیرفعال میکند، اما ممکن است بخواهید انتخاب رمز را بیشتر محدود کنید. حتماً مستندات OpenSSL را دربارهی قالب فهرست رمز بخوانید. اگر میخواهید بررسی کنید کدام رمزها با یک فهرست رمز مشخص فعال میشوند، از SSLContext.get_ciphers() یا دستور openssl ciphers روی سیستم خود استفاده کنید.
اگر از این ماژول بهعنوان بخشی از یک برنامه چندفرایندی استفاده میکنید (برای نمونه، با استفاده از ماژولهای multiprocessing یا concurrent.futures)، توجه داشته باشید که مولد اعداد تصادفی درونی OpenSSL فرایندهای forkشده را بهدرستی مدیریت نمیکند. اگر برنامهها از هر قابلیت SSL همراه با os.fork() استفاده کنند، باید وضعیت PRNG فرایند والد را تغییر دهند. هر فراخوانی موفق RAND_add() یا RAND_bytes() کافی است.
اضافه شده در نسخهی 3.7.
پروتکل TLS 1.3 نسبت به نسخهی قبلی TLS/SSL کمی متفاوت عمل میکند. برخی ویژگیهای جدید TLS 1.3 هنوز در دسترس نیستند.
پروتکل TLS 1.3 از مجموعه رمزهای مجزایی استفاده میکند. تمام مجموعههای رمز AES-GCM و ChaCha20 بهطور پیشفرض فعال هستند. برای محدود کردن رمزهای مجاز TLS 1.3، باید متد SSLContext.set_ciphersuites() فراخوانی شود، نه SSLContext.set_ciphers() که فقط روی رمزهای موجود در نسخههای قدیمیتر TLS تأثیر میگذارد. متد SSLContext.get_ciphers() اطلاعاتی دربارهی رمزها برای هر دو نسخهی TLS 1.3 و نسخههای پیشین برمیگرداند و متد SSLSocket.cipher() پس از برقراری اتصال، اطلاعاتی را دربارهی رمز توافقشده برای هر دو نسخهی TLS 1.3 و نسخههای پیشین برمیگرداند.
تیکتهای نشست (session tickets) دیگر بهعنوان بخشی از دستدهی اولیه ارسال نمیشوند و بهشکل متفاوتی مدیریت میشوند. SSLSocket.session و SSLSession با TLS 1.3 سازگار نیستند.
گواهیهای سمت کلاینت نیز دیگر در حین دستدهی اولیه تأیید نمیشوند. یک سرور میتواند در هر زمان یک گواهی درخواست کند. کلاینتها درخواستهای گواهی را هنگامی که دادههای کاربردی را ارسال میکنند یا از سرور دریافت میکنند، پردازش میکنند.
ویژگیهای TLS 1.3 مانند دادهی زودهنگام، درخواست گواهی کلاینت با تأخیر TLS و بازنشانی کلید هنوز پشتیبانی نمیشوند.
همچنین ببینید
socket.socketمستندات کلاس زیربنایی socket
مقدمهای از مستندات Apache HTTP Server
استیو کنت
Donald E. Eastlake, Jeffrey I. Schiller, Steve Crocker
دیوید کوپر و دیگران
تیم دیرکس و اریک رسکورلا.
Donald E. Eastlake
IANA
IETF
Mozilla