ssl --- وحدة SSL/TLS¶
توفر هذه الوحدة الوصول إلى تسهيلات أمان طبقة النقل (المعروفة سابقاً وعلى نطاق واسع باسم "طبقة المقابس الآمنة") للتشفير ومصادقة الطرف لمقابس الشبكة، سواء في جانب العميل أو جانب الخادم.
نصيحة
جديد على TLS في الكاميرا؟ ابدأ بالبرنامج التعليمي العمل مع شهادات TLS. فهو يشرح خطوة بخطوة اختيار أنواع المفاتيح، وإنشاء الشهادات وتحويلها إلى صيغة DER التي تتطلبها الكاميرا، ونقلها إلى الجهاز، والتحقق من الخوادم والعملاء -- مع أمثلة عملية كاملة.
ملاحظة
لا تنفّذ MicroPython ssl.SSLError. تُثار حالات فشل SSL/TLS على شكل OSError بدلاً من ذلك.
أمثلة¶
عميل TLS يتحقق من شهادة الخادم مقابل شهادة جهة إصدار (CA) (بصيغة DER) مخزّنة على نظام الملفات:
import socket
import ssl
import ntptime
# CERT_REQUIRED checks the certificate's validity dates, so the clock
# must be set (see the certificates tutorial linked above).
ntptime.settime()
# Open a plain TCP connection.
addr = socket.getaddrinfo("example.com", 443)[0][-1]
sock = socket.socket()
sock.connect(addr)
# Wrap it for TLS and require a valid certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="ca.der")
ssock = ctx.wrap_socket(sock, server_hostname="example.com")
ssock.write(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
print(ssock.read())
ssock.close()
للحصول على اتصال سريع غير آمن (بدون التحقق من الشهادة) يمكن استخدام دالة الراحة ssl.wrap_socket() بدلاً من ذلك:
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
خادم TLS يقدّم شهادته الخاصة ومفتاحه الخاص (بصيغة DER):
import socket
import ssl
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(socket.getaddrinfo("0.0.0.0", 8443)[0][-1])
sock.listen(1)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key")
while True:
client, addr = sock.accept()
sclient = ctx.wrap_socket(client, server_side=True)
sclient.write(b"hello\n")
sclient.close()
الدوال¶
- ssl.wrap_socket(sock: Any, server_side: bool = False, key: bytes | None = None, cert: bytes | None = None, cert_reqs: int = CERT_NONE, cadata: bytes | None = None, server_hostname: str | None = None, do_handshake: bool = True) Any¶
تغلّف الـ sock المعطى وتُرجع كائن مقبس مغلّف جديد. تنفيذ هذه الدالة هو أولاً إنشاء
SSLContextثم استدعاء الطريقةSSLContext.wrap_socket()على كائن السياق ذاك. تُمرَّر الوسيطات sock وserver_side وserver_hostname دون تغيير إلى استدعاء الطريقة. وتُمرَّر الوسيطة do_handshake على شكل do_handshake_on_connect. أما الوسيطات المتبقية فلها السلوك التالي:تحدّد cert_reqs ما إذا كان على الطرف (الخادم أو العميل) تقديم شهادة صالحة. لاحظ أن
ssl.CERT_NONEوssl.CERT_OPTIONALلا تتحققان من أي شهادة؛ بلssl.CERT_REQUIREDوحدها هي التي تفعل ذلك.cadata هو كائن bytes يحتوي على سلسلة شهادات جهة الإصدار (CA) (بصيغة DER) التي ستتحقق من شهادة الطرف. لا يُدعم حالياً سوى شهادة واحدة مرمّزة بصيغة DER.
الأصناف¶
- class ssl.SSLContext(protocol: int, /)¶
ينشئ نسخة SSLContext جديدة. يجب أن تكون الوسيطة protocol واحدة من ثوابت
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
يحمّل مفتاحاً خاصاً والشهادة المقابلة له. certfile هو سلسلة نصية تحتوي على مسار ملف الشهادة. keyfile هو سلسلة نصية تحتوي على مسار ملف المفتاح الخاص.
الفرق عن CPython
امتداد MicroPython: يمكن أن يكون certfile وkeyfile كائني bytes بدلاً من سلاسل نصية، وفي هذه الحالة يُفسَّران على أنهما بيانات الشهادة/المفتاح الفعلية.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
يحمّل سلسلة شهادات جهة الإصدار (CA) التي ستتحقق من شهادة الطرف. cafile هو مسار ملف شهادات جهة الإصدار. cadata هو كائن bytes يحتوي على شهادات جهة الإصدار. يجب تقديم واحدة فقط من هاتين الوسيطتين.
- get_ciphers() List[str]¶
يحصل على قائمة الأنظمة المشفّرة المفعّلة، تُرجَع على شكل قائمة من السلاسل النصية.
- set_ciphers(ciphers: List[str]) None¶
يضبط الأنظمة المشفّرة المتاحة للمقابس المنشأة بهذا السياق. ينبغي أن تكون ciphers قائمة من السلاسل النصية بصيغة IANA cipher suite .
- wrap_socket(sock: Any, *, server_side: bool = False, do_handshake_on_connect: bool = True, server_hostname: str | None = None, client_id: bytes | None = None) Any¶
يأخذ stream على شكل sock (عادةً نسخة socket.socket من النوع
SOCK_STREAM)، ويُرجع نسخة من ssl.SSLSocket تغلّف التدفق الأساسي. يحتوي الكائن المُرجَع على طرق واجهة stream المعتادة مثلread()وwrite()وما إلى ذلك.تحدّد server_side ما إذا كان المقبس المغلّف في جانب الخادم أم جانب العميل. ينبغي إنشاء مقبس SSL لجانب الخادم من مقبس عادي مُرجَع من
accept()على مقبس خادم استماع غير SSL.تحدّد do_handshake_on_connect ما إذا كان المصافحة تُجرى كجزء من
wrap_socketأم تؤجَّل لتُجرى كجزء من عمليات القراءة أو الكتابة الأولية. بالنسبة للمقابس الحاجبة، يُعد إجراء المصافحة فوراً هو السلوك المعياري. أما بالنسبة للمقابس غير الحاجبة (أي عندما يكون الـ sock المُمرَّر إلىwrap_socketفي الوضع غير الحاجب) فينبغي عموماً تأجيل المصافحة لأنwrap_socketخلاف ذلك يحجب حتى اكتمالها.تُستخدم server_hostname كعميل، وتضبط اسم المضيف الذي يُتحقَّق منه مقابل شهادة الخادم المستلمة. كما تضبط الاسم لإشارة اسم الخادم (SNI)، مما يسمح للخادم بتقديم الشهادة الصحيحة.
client_id وسيطة امتداد خاصة بـ MicroPython تُستخدم فقط عند تنفيذ خادم DTLS. راجع دعم DTLS للتفاصيل.
تحذير
افتراضياً لا يُجرى أي تحقق من الشهادة (
ssl.CERT_NONE). للحصول على اتصال آمن يجب التحقق من شهادة الطرف بضبط cert_reqs /SSLContext.verify_modeإلىssl.CERT_REQUIRED؛ وإلا فإن الاتصال يكون عرضة لهجمات الوسيط (man-in-the-middle).تُرجع
wrap_socketفي CPython كائنSSLSocketيحتوي على طرق نمطية للمقابس، مثلsendوrecvوما إلى ذلك. أماwrap_socketفي MicroPython فتُرجع كائناً أكثر تشابهاً مع كائنSSLObjectفي CPython الذي لا يحتوي على طرق المقابس هذه.
- verify_mode¶
يضبط أو يحصل على سلوك التحقق من شهادات الطرف. يجب أن يكون واحداً من ثوابت
CERT_*.ملاحظة
يتطلب
ssl.CERT_REQUIREDضبط تاريخ/وقت الجهاز بشكل صحيح، مثلاً باستخدام mpremote rtc --set أوntptime، ويجب تحديدserver_hostnameعند العمل في جانب العميل.
دعم DTLS¶
الفرق عن CPython
هذا امتداد خاص بـ MicroPython.
تدعم هذه الوحدة DTLS في وضعي العميل والخادم عبر الثابتين PROTOCOL_DTLS_CLIENT وPROTOCOL_DTLS_SERVER اللذين يمكن استخدامهما كوسيطة protocol لـ SSLContext.
في هذه الحالة يُتوقع أن يتصرف المقبس الأساسي كمقبس مخطّط بيانات (أي مثل المقبس المفتوح بـ socket.socket مع socket.AF_INET كـ af وsocket.SOCK_DGRAM كـ type).
دعم خادم DTLS¶
تُضبط ميزة دعم خادم DTLS في MicroPython بـ "Hello Verify" كما يتطلب DTLS 1.2. وهذا شفاف لعملاء DTLS، لكن هناك اعتبارات ذات صلة عند تنفيذ خادم DTLS في MicroPython:
ينبغي أن يمرّر الخادم وسيطة إضافية client_id عند استدعاء
SSLContext.wrap_socket(). يجب أن يكون هذا المعرّف كائنbytes(أو ما يشبهه) يحمل معرّفاً خاصاً بالنقل يمثّل العميل.أبسط نهج هو تحويل المجموعة
(client_ip, client_port)المُرجَعة منsocket.recv_from()إلى سلسلة بايتات، أي:_, client_addr = sock.recvfrom(1, socket.MSG_PEEK) sock.connect(client_addr) # Connect back to the client sock = ssl_ctx.wrap_socket(sock, server_side=True, client_id=repr(client_addr).encode())
في المرة الأولى التي يتصل فيها عميل، سيفشل استدعاء الخادم لـ
wrap_socketبخطأOSErrorنصه "Hello Verify Required". وذلك لأن ملف تعريف "Hello Verify" الخاص بـ DTLS غير معروف بعد لدى العميل. وإذا اتصل العميل نفسه مرة ثانية فإنwrap_socketستنجح.ترتبط ملفات تعريف "Hello Verify" الخاصة بـ DTLS بكائن
SSLContext، لذا ينبغي استخدام كائنSSLContextنفسه لتغليف اتصال لاحق من العميل نفسه. يتضمن تنفيذ ملف التعريف مهلة زمنية وله استخدام ثابت للذاكرة بغض النظر عن عدد العملاء المتصلين، لذا لا بأس بإعادة استخدام كائنSSLContextنفسه طوال مدة حياة الخادم.
الثوابت¶
- ssl.CERT_NONE: int¶
قيمة مدعومة للوسيطة cert_reqs وللسمة
SSLContext.verify_mode. لا يُجرى أي تحقق من شهادة الطرف.
- ssl.CERT_OPTIONAL: int¶
قيمة مدعومة للوسيطة cert_reqs وللسمة
SSLContext.verify_mode. التحقق من الشهادة اختياري. لاحظ أن هذا يتصرف على OpenMV Cam مثلssl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
قيمة مدعومة للوسيطة cert_reqs وللسمة
SSLContext.verify_mode. يُطلب من الطرف تقديم شهادة صالحة.