1. Giới thiệu.
Đoạn code Python này là một script được sử dụng để kiểm tra và in thông tin cơ bản về chứng chỉ SSL/TLS của một danh sách các máy chủ.
Script này được thiết kế để kiểm tra và in thông tin cơ bản về chứng chỉ SSL/TLS của các máy chủ được chỉ định.
Có thể sử dụng để đảm bảo rằng các máy chủ đang sử dụng chứng chỉ hợp lệ và cấu hình đúng cách.
2. Kiến Thức Cần Thiết.
- SSL/TLS và Chứng Chỉ SSL/TLS:
- SSL (Secure Sockets Layer) và TLS (Transport Layer Security) là các giao thức bảo mật được sử dụng để bảo vệ dữ liệu truyền tải qua mạng.
- Chứng chỉ SSL/TLS là các chứng chỉ số liệu số được sử dụng để xác nhận danh tính của máy chủ và bảo vệ sự toàn vẹn và bí mật của thông tin giao tiếp.
- Python Thư Viện:
OpenSSL
: Thư viện Python cung cấp giao diện để tương tác với SSL/TLS.cryptography
: Thư viện để xử lý và thao tác với chứng chỉ SSL/TLS.idna
: Thư viện để mã hóa và giải mã tên miền (Internationalized Domain Names in Applications).
- Socket Programming:
- Sử dụng module
socket
để thiết lập và quản lý kết nối mạng.
- Sử dụng module
- Concurrent Programming:
- Sử dụng
concurrent.futures.ThreadPoolExecutor
để thực hiện đồng thời nhiều nhiệm vụ, cải thiện hiệu suất.
- Sử dụng
from OpenSSL import SSL
from cryptography import x509
from cryptography.x509.oid import NameOID
import idna
from socket import socket
from collections import namedtuple
HostInfo = namedtuple(field_names='cert hostname peername', typename='HostInfo')
HOSTS = [('wiki.hoanghd.com', 443)]
def verify_cert(cert, hostname):
cert.has_expired()
def get_certificate(hostname, port):
hostname_idna = idna.encode(hostname)
sock = socket()
sock.connect((hostname, port))
peername = sock.getpeername()
ctx = SSL.Context(SSL.SSLv23_METHOD) # most compatible
ctx.check_hostname = False
ctx.verify_mode = SSL.VERIFY_NONE
sock_ssl = SSL.Connection(ctx, sock)
sock_ssl.set_connect_state()
sock_ssl.set_tlsext_host_name(hostname_idna)
sock_ssl.do_handshake()
cert = sock_ssl.get_peer_certificate()
crypto_cert = cert.to_cryptography()
sock_ssl.close()
sock.close()
return HostInfo(cert=crypto_cert, peername=peername, hostname=hostname)
def get_alt_names(cert):
try:
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
return ext.value.get_values_for_type(x509.DNSName)
except x509.ExtensionNotFound:
return None
def get_common_name(cert):
try:
names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
return names[0].value
except x509.ExtensionNotFound:
return None
def get_issuer(cert):
try:
names = cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)
return names[0].value
except x509.ExtensionNotFound:
return None
def print_basic_info(hostinfo):
s = '''» {hostname} « … {peername}
\tcommonName: {commonname}
\tSAN: {SAN}
\tissuer: {issuer}
\tnotBefore: {notbefore}
\tnotAfter: {notafter}
'''.format(
hostname=hostinfo.hostname,
peername=hostinfo.peername,
commonname=get_common_name(hostinfo.cert),
SAN=get_alt_names(hostinfo.cert),
issuer=get_issuer(hostinfo.cert),
notbefore=hostinfo.cert.not_valid_before,
notafter=hostinfo.cert.not_valid_after
)
print(s)
def check_it_out(hostname, port):
hostinfo = get_certificate(hostname, port)
print_basic_info(hostinfo)
import concurrent.futures
if __name__ == '__main__':
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
for hostinfo in e.map(lambda x: get_certificate(x[0], x[1]), HOSTS):
print_basic_info(hostinfo)
Kết quả.
» wiki.hoanghd.com « … ('113.161.201.128', 443)
commonName: wiki.hoanghd.com
SAN: ['wiki.hoanghd.com']
issuer: R3
notBefore: 2023-10-11 01:20:31
notAfter: 2024-01-09 01:20:30
3. Cách hoạt động.
Import các thư viện: Code sử dụng các thư viện cần thiết để làm việc với chứng chỉ SSL/TLS và kết nối mạng. OpenSSL
được sử dụng cho các chức năng liên quan đến SSL, cryptography
được sử dụng để thao tác với chứng chỉ và idna
được sử dụng để mã hóa tên miền.
from OpenSSL import SSL
from cryptography import x509
from cryptography.x509.oid import NameOID
import idna
Named Tuple: Định nghĩa một named tuple (HostInfo
) để lưu trữ thông tin về chứng chỉ SSL của máy chủ, tên miền và thông tin peername của socket.
from collections import namedtuple
HostInfo = namedtuple(field_names='cert hostname peername', typename='HostInfo')
Danh sách các máy chủ: Chỉ định một danh sách các máy chủ cần kiểm tra, mỗi mục trong danh sách là một tuple chứa tên miền và cổng.
HOSTS = [
('wiki.hoanghd.com', 443)
]
Hàm xác minh chứng chỉ: Hàm này được thiết kế để thực hiện xác minh bổ sung trên chứng chỉ SSL, nó kiểm tra xem chứng chỉ đã hết hạn hay chưa.
def verify_cert(cert, hostname):
# Hàm để xác minh chứng chỉ SSL
# (Chưa được triển khai đầy đủ trong mã nguồn)
Hàm lấy chứng chỉ: Hàm này kết nối đến máy chủ và cổng được chỉ định, lấy chứng chỉ SSL, chuyển đổi nó thành chứng chỉ cryptography và trả về một named tuple HostInfo
.
def get_certificate(hostname, port):
# Hàm để thiết lập kết nối đến máy chủ và lấy chứng chỉ SSL của nó
# ...
Các hàm thông tin chứng chỉ: Các hàm này trích xuất thông tin cụ thể từ chứng chỉ, chẳng hạn như Subject Alternative Names, Common Name và thông tin nhà cung cấp.
def get_alt_names(cert):
# Hàm để lấy Subject Alternative Names (SAN) từ một chứng chỉ
# ...
def get_common_name(cert):
# Hàm để lấy Common Name (CN) từ một chứng chỉ
# ...
def get_issuer(cert):
# Hàm để lấy thông tin người phát hành từ một chứng chỉ
# ...
Hàm in thông tin cơ bản: Hàm này nhận một tuple HostInfo
và in thông tin về chứng chỉ, bao gồm tên phổ biến, SAN, nhà cung cấp, notBefore và notAfter.
def print_basic_info(hostinfo):
# Hàm để in thông tin cơ bản về chứng chỉ SSL của máy chủ
# ...
Hàm kiểm tra và in thông tin: Hàm này gọi get_certificate
và print_basic_info
để kiểm tra và in thông tin chứng chỉ SSL cho một máy chủ và cổng cụ thể.
def check_it_out(hostname, port):
# Hàm để kiểm tra và in thông tin cơ bản cho một máy chủ
# ...
ThreadPoolExecutor cho Thực hiện đồng thời: Script sử dụng ThreadPoolExecutor
để thực hiện hàm get_certificate
đối với nhiều máy chủ đồng thời. Kết quả sau đó được chuyển đến print_basic_info
để in thông tin chứng chỉ cơ bản.
import concurrent.futures
if __name__ == '__main__':
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as e:
for hostinfo in e.map(lambda x: get_certificate(x[0], x[1]), HOSTS):
print_basic_info(hostinfo)
5. Bổ sung thêm thời gian hết hạn SSL.
Để thêm thông tin về số ngày hết hạn của chứng chỉ SSL/TLS vào đoạn mã, bạn có thể sử dụng thuộc tính not_valid_after
của đối tượng chứng chỉ. Dưới đây là cách bạn có thể cập nhật mã để in thêm thông tin này:
Thêm import cho module datetime
:
from datetime import datetime
Cập nhật hàm print_basic_info
để bao gồm thông tin về ngày hết hạn:
def print_basic_info(hostinfo):
not_before = hostinfo.cert.not_valid_before
not_after = hostinfo.cert.not_valid_after
s = '''» {hostname} « … {peername}
\tcommonName: {commonname}
\tSAN: {SAN}
\tissuer: {issuer}
\tnotBefore: {notbefore}
\tnotAfter: {notafter}
\tdaysUntilExpiration: {days_until_expiration}
'''.format(
hostname=hostinfo.hostname,
peername=hostinfo.peername,
commonname=get_common_name(hostinfo.cert),
SAN=get_alt_names(hostinfo.cert),
issuer=get_issuer(hostinfo.cert),
notbefore=not_before,
notafter=not_after,
days_until_expiration=(not_after - datetime.utcnow()).days
)
print(s)
Trong đoạn code cập nhật trên, not_valid_before
và not_valid_after
là các thuộc tính của đối tượng chứng chỉ và days_until_expiration
tính số ngày còn lại cho đến khi chứng chỉ hết hạn.
Sau đó, khi bạn gọi print_basic_info(hostinfo)
, thông tin về ngày hết hạn và số ngày còn lại sẽ được hiển thị như dưới.
» wiki.hoanghd.com « … ('113.161.201.128', 443)
commonName: wiki.hoanghd.com
SAN: ['wiki.hoanghd.com']
issuer: R3
notBefore: 2023-10-11 01:20:31
notAfter: 2024-01-09 01:20:30
daysUntilExpiration: 17
4. Kết Luận.
Đoạn code giúp tự động hóa quá trình kiểm tra chứng chỉ SSL/TLS cho nhiều máy chủ một cách hiệu quả.
Đây là công cụ hữu ích trong việc duyệt qua các máy chủ và đảm bảo tính an toàn của kết nối SSL/TLS.
Chú ý rằng đoạn code này có thể cần được mở rộng thêm để thực hiện các kiểm tra bảo mật chi tiết hơn tùy thuộc vào yêu cầu cụ thể của ứng dụng.