Ví dụ TLS Python

Thông thường, máy khách SSL/TLS xác minh chứng chỉ của máy chủ. Máy chủ cũng có thể yêu cầu chứng chỉ đã ký từ máy khách. Chúng được gọi là Chứng chỉ ứng dụng khách. Điều này đảm bảo rằng không chỉ máy khách có thể tin tưởng máy chủ mà máy chủ cũng có thể tin tưởng máy khách

Theo truyền thống trong Python, bạn sẽ chuyển tham số ca_certs cho hàm ssl.wrap_socket[] trên máy chủ để kích hoạt chứng chỉ ứng dụng khách

# Client
ssl.wrap_socket[s, ca_certs="ssl/server.crt", cert_reqs=ssl.CERT_REQUIRED,
                certfile="ssl/client.crt", keyfile="ssl/client.key"]

# Server
ssl.wrap_socket[connection, server_side=True, certfile="ssl/server.crt",
                keyfile="ssl/server.key", ca_certs="ssl/client.crt"]

Kể từ Python v3. 4, thì càng an toàn và do đó, việc bọc một ổ cắm trong lớp SSL/TLS là tạo một phiên bản

context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
0 và gọi
context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
1. Tuy nhiên, phương thức
context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
1 không có tham số ca_certs. Không rõ ràng trực tiếp về cách kích hoạt yêu cầu chứng chỉ ứng dụng khách ở phía máy chủ

Tài liệu về

context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
4 có đề cập đến chứng chỉ ứng dụng khách

Mục đích. CLIENT_AUTH tải chứng chỉ CA để xác minh chứng chỉ ứng dụng khách ở phía máy chủ

Nhưng

context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
4 tải chuỗi Cơ quan cấp chứng chỉ đáng tin cậy mặc định của hệ thống để máy khách có thể xác minh chứng chỉ của máy chủ. Bạn thường không muốn sử dụng chúng cho chứng chỉ ứng dụng khách

Trong phần này, nó đề cập rằng bạn cần chỉ định

context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
6

Ở chế độ máy chủ, nếu bạn muốn xác thực ứng dụng khách của mình bằng lớp SSL [thay vì sử dụng cơ chế xác thực cấp cao hơn], bạn cũng sẽ phải chỉ định CERT_REQUIRED và kiểm tra tương tự chứng chỉ ứng dụng khách

Tôi đã không phát hiện ra cách chỉ định

context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
6 trong hàm tạo
context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
0 hoặc phương thức
context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
9. Hóa ra bạn phải đặt thuộc tính theo cách thủ công trên
context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]
0 trên máy chủ để kích hoạt xác minh chứng chỉ ứng dụng khách, như thế này

context = ssl.create_default_context[ssl.Purpose.CLIENT_AUTH]
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain[certfile=server_cert, keyfile=server_key]
context.load_verify_locations[cafile=client_certs]

Dưới đây là ví dụ đầy đủ về máy khách và máy chủ, cả hai đều xác thực chứng chỉ của nhau

Đối với ví dụ này, chúng tôi sẽ tạo chứng chỉ ứng dụng khách và máy chủ tự ký. Thông thường, bạn sẽ sử dụng chứng chỉ máy chủ từ Tổ chức phát hành chứng chỉ, chẳng hạn như Let's Encrypt và sẽ thiết lập Tổ chức phát hành chứng chỉ của riêng bạn để bạn có thể ký và thu hồi chứng chỉ ứng dụng khách

SSL được thiết kế để chống lại cuộc tấn công trung gian. An toàn không phải là điều dễ dàng. SSL có thể đảm bảo kết nối an toàn nếu nó được triển khai đúng cách [Bạn có nhớ sự cố chảy máu trái tim không?]. Ngay bây giờ, triển khai phổ biến nhất có thể vẫn là OpenSSL. Ssl trong stdlib của Python về cơ bản là một trình bao bọc xung quanh nó. Nó cung cấp một tập hợp nhỏ các hoạt động cấp rất cao. Để sử dụng nó, hiểu biết cơ bản về SSL là rất quan trọng

[Ghi chú. Mã Python trong bài viết này sử dụng Python 3. 4]

Bạn có thực sự là bạn không?

Ngay cả trước khi bạn có thể giao tiếp trong một kênh bảo mật, vẫn có một câu hỏi cấp bách hơn về bảo mật. Tôi có thực sự đang nói chuyện với người mà tôi muốn nói chuyện không?

Khi thiết lập kết nối an toàn, ngay sau khi gửi lời chào cho khách hàng ban đầu, khách hàng sẽ mong đợi phía máy chủ trả lời lại một tin nhắn bao gồm chứng chỉ kỹ thuật số. Chứng chỉ kỹ thuật số về cơ bản là một cách chứng minh máy chủ thực sự là máy khách của máy chủ dự định kết nối với [hay đúng hơn là máy chủ tuyên bố đó là ai]

Hãy cùng xem chứng chỉ của một nhà cung cấp dịch vụ thanh toán [loại ISP mà bạn mong đợi có một kênh HTTPS]


import ssl
import urllib

# Alipay, a trusted payment provider
url = '//www.alipay.com'
addr = urllib.parse.urlsplit[url].hostname
port = 443
cert = ssl.get_server_certificate[[addr, port], ssl_version=1]
print[cert]

Đây là những gì nó cho thấy


-----BEGIN CERTIFICATE-----
MIIFPTCCBCWgAwIBAgIQNuTYtQBKE6idQDXmiii1UjANBgkqhkiG9w0BAQsFADB+
MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAd
BgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxLzAtBgNVBAMTJlN5bWFudGVj
IENsYXNzIDMgU2VjdXJlIFNlcnZlciBDQSAtIEc0MB4XDTE0MTIxNjAwMDAwMFoX
DTE1MTIyMTIzNTk1OVowgcsxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaSEVKSUFO
RzERMA8GA1UEBwwISEFOR1pIT1UxGzAZBgNVBAoMEkFsaXBheS5jb20gQ28uLEx0
ZDEeMBwGA1UECwwVT3BlcmF0aW9ucyBEZXBhcnRtZW50MUAwPgYDVQQLFDdPcmdh
bml6YXRpb24gYW5kIGRvbWFpbihzKSBhdXRoZW50aWNhdGVkIGJ5IGlUcnVzIENo
aW5hMRcwFQYDVQQDDA53d3cuYWxpcGF5LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAJ2lSCMIXh/7VIcNpsJnymDmWLiAWaxuudcb4T58SJJT00Am
0T2ZV9ZFsOMRrYJuyGpEFSh7oXxs6jwdB/JvK1kMrVOcT6LoI7kGktjsKXFhGhrl
KU6yz++2mcvcCb1UFeeGDaei6jTG3J3ymF+lv4o/e8ck5RVinSffgCvMVK6OFxUA
o185M7Dd1AlchfSLZknITF+jGLd4mbir8o/glS3TiiaRXdOBvHN4txWIAIvXfH0Z
oX7T1H9RsK/kY27jFWhz2QQVJi3la4nwMFOI31iCBVVdcIEaLAOqeZM9VjpQWqyh
cZc/vh5DEPh58MMuFyqyenRRSFIx1TDy5NiVUZ0CAwEAAaOCAWcwggFjMBkGA1Ud
EQQSMBCCDnd3dy5hbGlwYXkuY29tMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgWg
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBlBgNVHSAEXjBcMFoGCmCG
SAGG+EUBBzYwTDAjBggrBgEFBQcCARYXaHR0cHM6Ly9kLnN5bWNiLmNvbS9jcHMw
JQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5bWNiLmNvbS9ycGEwHwYDVR0jBBgw
FoAUX2DPYZBV34RDFIpgKrL1evRDGO8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDov
L3NzLnN5bWNiLmNvbS9zcy5jcmwwVwYIKwYBBQUHAQEESzBJMB8GCCsGAQUFBzAB
hhNodHRwOi8vc3Muc3ltY2QuY29tMCYGCCsGAQUFBzAChhpodHRwOi8vc3Muc3lt
Y2IuY29tL3NzLmNydDANBgkqhkiG9w0BAQsFAAOCAQEAhHl+N2K6QlSm4bJN33K5
26g+xfN1ix8ulo+4cYiTb80hN4IN6gRDOR0wYVCP940ioe+DoFrqnv38wOnsMOYO
30mQxU4fV4Pn8pSm0Mi905bTGIVeGUTkHWvpcYQt5xcyQmYmBpnsWt9Ycig5b8xJ
BCgX+HhlbmL69C9I0HkwPhgcYDFIjvC3RptT/VrkQtnODZ75UY6jvNJF7b4Le0Ul
1Clv4kpmK+W5Yz9z67DMTEw7E3lazcbvZASl0hEJJOMgC8e3fg1zcaBrQ2oSZDwe
aUeWXCzgzMrzDwO45Z6hiIhP4oflIQz2J5Urh48Ix8CVBc9zxNnYYW9tLmSh29yR
aw==
-----END CERTIFICATE-----

Đây là khóa được định dạng PEM, mã hóa base64 x509 ASN. 1 phím. Giải mã nó cho phép chúng ta đọc nó


from OpenSSL import crypto # pip install pyopenssl

cert = crypto.load_certificate[crypto.FILETYPE_PEM, cert]

Rất nhiều thông tin bây giờ có thể được đọc


# The signer of the server's certificate
issuer = cert.get_issuer[]
print[issuer.get_components[]]

[[b'C', b'US'], [b'O', b'Symantec Corporation'], [b'OU', b'Symantec Trust Network'], [b'CN', b'Symantec Class 3 Secure Server CA - G4']]


# The company's business information
subject = cert.get_subject[]
components = dict[subject.get_components[]] # convert to dict
print[components]


{b'C': b'CN',
 b'CN': b'www.alipay.com',
 b'L': b'HANGZHOU',
 b'O': b'Alipay.com Co.,Ltd',
 b'OU': b'Organization and domain[s] authenticated by iTrus China',
 b'ST': b'ZHEJIANG'}

Chứng chỉ kỹ thuật số là một cách hiệu quả để đảm bảo một người là người mà người đó tuyên bố là. Cách thức hoạt động là có cơ quan cấp chứng chỉ [CA] xác nhận danh tính của một người bằng cách ký chứng chỉ kỹ thuật số của người đó. Nói cách khác, niềm tin được ủy thác cho các CA này [Lòng tin thực sự là một điều thú vị của xã hội loài người chúng ta]. Trên thực tế, CA sẽ thực hiện một số cuộc điều tra trong thế giới thực về yêu cầu nhận dạng bằng cách yêu cầu tất cả các loại chứng chỉ kinh doanh và các dạng bằng chứng cứng khác. Sau khi xác nhận danh tính, CA sẽ ký điện tử chứng chỉ [Chứng chỉ là sự kết hợp của một số thứ, khóa chung, tên máy chủ mà tổ chức điều hành hoạt động kinh doanh trực tuyến của mình và siêu dữ liệu khác như tên công ty. ] để tất cả thông tin KHÔNG bị rối sau này [e. g. , bạn không thể mang chứng chỉ đã ký này về nhà rồi thay đổi tên máy chủ và sử dụng nó với tên máy chủ mới và làm điều gì đó không tốt với nó, vì khi đó chứng chỉ của bạn sẽ không hợp lệ và khách hàng sẽ không tin tưởng vào phía kết nối của bạn]

Khi nhận được chứng chỉ kỹ thuật số, phía máy khách của kết nối sẽ, trong số những thứ khác, kiểm tra tính hợp lệ của chứng chỉ, một số kiểm tra được

  1. Là nó hết hạn?
  2. Tên máy chủ có được chứng nhận mà một máy khách đang kết nối không?
  3. Chứng chỉ này đã từng bị sửa đổi chưa [bằng cách ký nó bằng cùng một thuật toán mà CA sử dụng và so sánh hai chữ ký, vâng, vì vậy bạn cần khóa công khai của CA, bạn hoàn toàn đúng. Một tập hợp các khóa công khai này của CA được công khai và thường đi kèm với hệ điều hành của bạn hoặc chúng có thể được truy xuất ở những nơi như tại đây]
  4. CA đã ký chứng chỉ của bạn có phải là CA mà tôi tin tưởng không?

Mô-đun ssl trong Python 3. 4 cung cấp hầu hết tất cả các API thực hiện các kiểm tra trên

Làm thế nào chúng ta sẽ nói chuyện an toàn bây giờ?

Sau khi xác minh chứng chỉ của máy chủ là đáng tin cậy, hai bên kết nối sẽ đàm phán một khóa bí mật được gọi là bí mật chính sẽ được sử dụng để mã hóa đối xứng tất cả lưu lượng truy cập trong tương lai. Điều quan trọng cần lưu ý là trong quá trình đàm phán khóa này, bản thân khóa được mã hóa bằng khóa chung của máy chủ và máy chủ sẽ giải mã nó bằng khóa riêng phù hợp của nó [thực tế là nó có thể giải mã nó chứng minh thêm danh tính của nó]. Lý do nó sử dụng một khóa để mã hóa đối xứng lưu lượng truy cập trong tương lai thay vì tiếp tục sử dụng mã hóa khóa chung là vì mã hóa bất đối xứng rất tốn kém về mặt tính toán. Vì vậy, đây là trường hợp của hệ thống mật mã lai, trong đó quá trình bắt tay là đóng gói khóa và lưu lượng truy cập trong tương lai là đóng gói dữ liệu

Đặt nó hoàn toàn trong HTTPS

Đây là phiên bản nguyên thủy về cách bạn có thể mở trang HTTPS bằng Python


from collections import namedtuple
from http.client import HTTPResponse
import cgi
import socket
import ssl
import urllib

# certifi is a pkg whose sole purpose is distributing Mozilla's CA bundle
import certifi # pip install certifi
ca_certs = certifi.where[]

def http[url, secure=443, encoding='utf-8']:
    components = urllib.parse.urlsplit[url]
    path = '%s' % components.path if components.path else '/'
    HTTPS = [components.scheme == 'https']
    addr = components.hostname
    port = secure if HTTPS else 80
    CRLF = '\r\n\r\n'
    
    Page = namedtuple['Page', ['code', 'headers', 'body']]
    
    def handshake[sock]:
        # this will trigger the handshake
        # if sock is already connected
        new_sock = ssl.wrap_socket[sock,
               ciphers="HIGH:-aNULL:-eNULL:-PSK:RC4-SHA:RC4-MD5",
               ssl_version=ssl.PROTOCOL_TLSv1,
               cert_reqs=ssl.CERT_REQUIRED, # I will verify your certificate
               ca_certs=ca_certs # using a list of trusted CA's certificates
               ]
        return new_sock
    
    def make_header[]:
        headers = [
            'GET %s HTTP/1.1' % [path],
            'Host: %s' % addr,
            'User-Agent: Mozilla/4.0 [compatible; MSIE 7.0; Windows NT 5.1]',
            'Charset: %s' % encoding
        ]
        header = '\n'.join[headers]
        header += CRLF
        return bytes[header, encoding=encoding]
    
    def parse_response[resp]:
        '''
        resp
            http.client.HTTPResponse, not closed
        '''
        resp.begin[]
        code = resp.getcode[]
        # assume status 200
        headers = dict[resp.getheaders[]]
        def get_charset[]:
            ctt_type = headers.get['Content-Type']
            return cgi.parse_header[ctt_type][1].get['charset', encoding]
        body = resp.read[]
        body = str[body, encoding=get_charset[]]
        return Page[code, headers, body]
    
    def request[header]:
        sock = socket.socket[socket.AF_INET, socket.SOCK_STREAM]
        sock.setsockopt[socket.SOL_SOCKET, socket.SO_REUSEADDR, 1]
        sock.connect[[addr, port]]
        try:
            if  sock = handshake[sock]
            sock.sendall[header]
            # receive it
            resp = HTTPResponse[sock]
            return parse_response[resp]
        finally:
            sock.shutdown[1]
            sock.close[]    
    
    return request[make_header[]]


Tất nhiên, lập trình viên Python trong thế giới thực sẽ không làm điều này. Thay vào đó, một phiên bản hấp dẫn hơn sẽ là thế này


import requests # pip install requests
resp = requests.get[url]

Các mối quan tâm bảo mật khác

Cái bắt tay được giải thích ở trên đã bỏ qua nhiều thứ khác

Ngẫu nhiên

Một trong số đó là một vài byte ngẫu nhiên được trao đổi giữa máy khách và máy chủ, được sử dụng để lấy ra, trong số nhiều thứ khác, khóa chính được giải thích ở trên. Vì vậy, tính gần đúng của nó với tính ngẫu nhiên thực sự quan trọng. Trong nhiều ngôn ngữ lập trình, bạn có thể thêm trình tạo số ngẫu nhiên của mình với nhiều nguồn hơn. Trong một số WAS hiện đại, trình tạo số ngẫu nhiên có thể có hơn 100 nguồn

Phát lại cuộc tấn công

Điều gì sẽ xảy ra nếu ai đó ghi lại bất cứ điều gì đang diễn ra giữa bạn và máy chủ [e. g. , bộ định tuyến của bạn có thể, ISP của bạn có thể] và sau đó phát lại? . SSL tự xây dựng lớp bảo vệ này theo thiết kế. ID phiên tắt một lần được tạo để bảo vệ bạn. Vì vậy, khi kẻ tấn công phát lại phiên của bạn, máy chủ sẽ từ chối nói chuyện với anh ta vì đối với cuộc trò chuyện mới, một phiên mới được sử dụng và lưu lượng truy cập được phát lại bằng ID phiên trước đó là không đáng tin cậy. Tuy nhiên, đây là một bức tranh lớn hơn với ID phiên. Với những gì đã giải thích ở trên về việc bắt tay có nhiều chi phí hoạt động do thuật toán khóa chung, một ứng dụng web hiệu quả sẽ gặp khó khăn trong việc đánh đổi giữa bảo mật và hiệu suất. Vì vậy, các kỹ thuật mới được khám phá để tránh bắt tay hoàn toàn bằng cách tiếp tục phiên trước đó. Đây là một bài viết xuất sắc giải thích kỹ thuật này

Làm cách nào để sử dụng TLS trong Python?

Bây giờ chúng ta đã biết cách xác minh tính hợp lệ của TLS/SSL trên trang web, hãy xem cách chúng ta có thể sử dụng nó trên máy chủ web Python. .
Tạo khóa RSA riêng
Tạo yêu cầu ký chứng chỉ [CSR] bằng khóa riêng
Ký yêu cầu CSR để tạo chứng chỉ

Yêu cầu Python có sử dụng TLS không?

Yêu cầu sử dụng mô-đun ssl của thư viện chuẩn Python bên trong - mô-đun này hỗ trợ các phiên bản SSL và TLS khác nhau . Bạn có thể yêu cầu sử dụng một giao thức cụ thể [như TLSv1] bằng cách tạo HTTPAdapter tùy chỉnh phiên bản PoolManager được tạo.

Yêu cầu Python sử dụng phiên bản TLS nào?

Trên Python 3. 6. 10 với các yêu cầu được cài đặt pyopenssl nên sử dụng TLS cao nhất hiện có, 1. 3 [từ pyopenssl ], không phải 1. 2 [từ ssl ].

Làm cách nào để tạo kết nối SSL trong Python?

Tạo ổ cắm .
nhập ổ cắm nhập tên máy chủ ssl = 'www. con trăn. bối cảnh tổ chức = ssl. create_default_context[] với ổ cắm. .
tên máy chủ = 'www. con trăn. org' # PROTOCOL_TLS_CLIENT yêu cầu chuỗi chứng chỉ hợp lệ và ngữ cảnh tên máy chủ = ssl. SSLContext[ssl. .
bối cảnh = ssl. SSLContext[ssl

Chủ Đề