Der HTTP-Server von Python ist praktisch, um mit JavaScript-Code in einem Browser zu experimentieren. HTTPS wird für die Progressive Web App (PWA) benötigt, die seit 2016 im Mittelpunkt der JavaScript-Community steht. Da es jedoch nur einen Artikel über das Ändern des SSL-Moduls auf Pythons HTTPS-Server und der Python 3-Serie gab. Ich beschloss, zusammenzufassen, was ich untersucht habe.
Angenommen, Python 3.6 und OpenSSL 1.0.2 und neuere Versionen. Dem SSL-Modul der Python 3-Serie wurden verschiedene Methoden und Konstanten hinzugefügt, und der Beispielcode funktioniert möglicherweise nicht mit Python 3.6 und früheren Versionen.
Sie können die Version von OpenSSL aus der Konstante von ssl herausfinden.
>>> import ssl
>>> ssl.OPENSSL_VERSION
Wenn Sie den Server, der die HTML-Datei bereitstellt, über die Befehlszeile starten möchten, führen Sie den folgenden Befehl aus:
python3 -m http.server 8000
Wenn Sie CGI verwenden möchten, geben Sie die Option "--cgi" an.
python3 -m http.server --cgi 8000
Das Skript, das Sie ausführen möchten, muss sich in / cgi-bin
oder / htbin
befinden.
#!/usr/bin/env python3
print("Content-Type: text/plain; charset=utf-8;\r\n")
print("hello world")
Sie können ein selbstsigniertes Zertifikat und einen privaten Schlüssel mit dem folgenden Einzeiler generieren.
# https://stackoverflow.com/a/41366949/531320
openssl req -x509 -newkey rsa:4096 -sha256 \
-nodes -keyout server.key -out server.crt \
-subj "/CN=example.com" -days 3650
Zusätzlich zum Modul "http.server" müssen Sie das Modul "ssl" laden. Erstellen Sie ein "SSLContext" -Objekt, geben Sie verschiedene Einstellungen an und verwenden Sie dann "SSLContext.wrap_socket", um ein "SSLSocket" -Objekt zu erstellen.
server.py
from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl
def run(host, port, ctx, handler):
server = HTTPServer((host, port), handler)
server.socket = ctx.wrap_socket(server.socket)
print('Server Starts - %s:%s' % (host, port))
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()
print('Server Stops - %s:%s' % (host, port))
if __name__ == '__main__':
host = 'localhost'
port = 8000
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain('server.crt', keyfile='server.key')
ctx.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
handler = SimpleHTTPRequestHandler
run(host, port, ctx, handler)
ssl.OP_NO_TLSv1
und ssl.OP_NO_TLSv1_1
bedeuten, TLS 1.0 bzw. TLS 1.1 zu deaktivieren.
Verwenden Sie für CGI "CGIHTTPRequestHandler" anstelle von "SimpleHTTPRequestHandler".
server.py
from http.server import HTTPServer, CGIHTTPRequestHandler
import ssl
def run(host, port, ctx, handler):
server = HTTPServer((host, port), handler)
server.socket = ctx.wrap_socket(server.socket)
print('Server Starts - %s:%s' % (host, port))
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()
print('Server Stops - %s:%s' % (host, port))
if __name__ == '__main__':
host = 'localhost'
port = 8000
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain('server.crt', keyfile='server.key')
ctx.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
handler = CGIHTTPRequestHandler
handler.cgi_directories = ['/cgi-bin', '/htbin']
# https://stackoverflow.com/a/27303995/531320
handler.have_fork=False
run(host, port, ctx, handler)
Unter macOS und Unix funktioniert der Server nicht ohne das Hinzufügen von "handler.have_fork = False".
Der Vorteil von CGI ist, dass es Programme in anderen Sprachen als Python ausführen kann. Der Node.js-Code sieht folgendermaßen aus:
node.cgi
#!/usr/bin/env node
console.log("Content-type: text/plain; charset=utf-8\n");
console.log("Hello World");
Mit dem Modul "wsgiref" können Sie einen WSGI-fähigen Server einrichten.
server.py
from wsgiref.simple_server import make_server
from pathlib import Path
import ssl
def simple_app(env, start_response):
info = env['PATH_INFO'][1:]
if info == '':
info = 'index.html'
root = Path.cwd()
path = root.joinpath(info).resolve()
if root in path.parents and path.is_file():
status = '200 OK'
body = path.read_bytes()
suffix = path.suffix
content_type = {
'.html': 'text/html',
'.txt': 'text/plain',
'.json': 'application/json'
}.get(suffix, 'text/plain')
else :
body = b'404 Not Found'
status = '404 Not Found'
content_type = 'text/plain'
headers = [
('Content-Type', content_type),
('Content-Length', str(len(body)))
]
start_response(status, headers)
return [body]
def run(host, port, ctx, app):
server = make_server(host, port, app)
server.socket = ctx.wrap_socket(server.socket)
print('Server Starts - %s:%s' % (host, port))
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()
print('Server Stops - %s:%s' % (host, port))
if __name__ == '__main__':
host = 'localhost'
port = 8000
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain('server.crt', keyfile='server.key')
ctx.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
run(host, port, ctx, simple_app)
Wenn Sie alle definierten Umgebungsvariablen (env
) kennen möchten, verwenden Sie PEP-333. Siehe. Sie können auch auf der angezeigten Seite überprüfen, wann wsgiref.simple_server.demo_app
zu einer Ausführungsanwendung gemacht wurde.
SSLContext
Für die allgemeine Verwendung wird ssl.create_default_context
empfohlen.
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
Die Konstanten, die Sie angeben können, sind "Purpose.SERVER_AUTH" (zum Erstellen eines clientseitigen Sockets) und "Purpose.CLIENT_AUTH" (zum Erstellen eines serverseitigen Sockets).
Sie können den Konstruktor "SSLContext" auch direkt verwenden.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Die Konstanten, die Sie angeben können, sind "PROTOCOL_TLS" (Standard), "PROTOCOL_TLS_CLIENT" und "PROTOCOL_TLS_SERVER". Verschiedene in Python 3.6 und früheren Versionen verfügbare Konstanten sind veraltet.
Sie können Einstellungen über Optionen überprüfen oder hinzufügen. Sie können die Standardoptionen wie folgt sehen:
>>> import ssl
>>> ssl.create_default_context().options
<Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|
OP_CIPHER_SERVER_PREFERENCE|
OP_SINGLE_DH_USE|OP_SINGLE_ECDH_USE|
OP_NO_COMPRESSION: 2203714559>
Der folgende Code liefert das gleiche Ergebnis.
>>> ssl.SSLContext().options
Die Standardoptionen für Python 3.6 sind:
Sie können auf Kontextoptionen zugreifen oder diese ändern, nachdem Sie ein Socket-Objekt mit "wrap_socket" umbrochen haben.
server = HTTPServer((host, port), handler)
server.socket = ctx.wrap_socket(server.socket)
print(server.socket.context.options)
Sssl.wrap_socket
und SSLContext.wrap_socket
haben unterschiedliche Argumente, die angegeben werden können. Die Argumente in Python 3.6 sind:
ssl.wrap_socket(
sock,
keyfile=None,
certfile=None,
server_side=False,
cert_reqs=CERT_NONE,
ssl_version={see docs},
ca_certs=None,
do_handshake_on_connect=True,
suppress_ragged_eofs=True,
ciphers=None
)
SSLContext.wrap_socket(
sock,
server_side=False,
do_handshake_on_connect=True,
suppress_ragged_eofs=True,
server_hostname=None,
session=None
)
Der Unterschied zwischen den beiden besteht darin, dass "SSLContext.wrap_socket" weniger Optionen zur Angabe hat. Die zu "SSLContext.wrap_socket" in Python 3.6 hinzugefügte "Sitzung" kann nicht in "ssl.wrap_socket" angegeben werden.
Der Standard für die Verwendung von "ssl.wrap_socket" und "SSLContext.wrap_socket" ist, ob es viele oder wenige Einstellungen gibt.
Wenn Sie "ssl.wrap.socket" verwenden, werden die Einstellungen über "server.socket.context" geändert, sodass eine große Anzahl von Einstellungen den Code schwer lesbar macht.
Ein weiterer Vorteil des direkten Erstellens eines SSLContext-Objekts besteht darin, dass es für eine HTTP-Bibliothek eines Drittanbieters verwendet werden kann.
Chiffresuiten finden Sie mit get_ciphers
. Verwenden Sie set_ciphers
, um die Cipher Suite zu ändern.
>>> import ssl
>>> ctx = ssl.create_default_context().get_ciphers()
>>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
>>> ctx.get_ciphers()
Eine empfohlene Verschlüsselungssuite ist auf Mozillas Website verfügbar (https://wiki.mozilla.org/Security/Server_Side_TLS). Cloudflare hat eine Nginx-Konfigurationsdatei (https://github.com/cloudflare/sslconfig) für TLS 1.3 veröffentlicht. OpenSSL 1.1.0 ist Teil von TLS 1.3 und wird in OpenSSL 1.1.1 vollständig unterstützt.
Für PCI DSS v3.2, das für Kreditkartenzahlungsdienste verwendet wird, müssen SSL und TSL 1.0 bis Ende Juni 2018 deaktiviert sein. Der von PyPI verwendete CDN-Dienst deaktiviert schnell auch TLS 1.1.
Im folgenden Einzeiler erfahren Sie, welche von Python 3 unterstützte TLS-Version auf Ihrem System installiert ist.
# https://news.ycombinator.com/item?id=13539034
python3 -c "import json, urllib.request; print(json.loads(urllib.request.urlopen('https://www.howsmyssl.com/a/check').read().decode('UTF-8'))['tls_version'])"
PEP 543 schlägt die Einführung einer neuen API für TLS vor. Zu den Entwicklungsmotiven gehört die Tatsache, dass das SSL-Modul von OpenSSL abhängt. Dies bedeutet, dass Python neu kompiliert werden muss, um das neue OpenSSL einzuführen, und dass es nicht möglich ist, zu verschiedenen TLS-Bibliotheken zu wechseln, die sich von OpenSSL unterscheiden. Ich werde.
Die wichtigsten SSL / TLS-Bibliotheken sind:
Recommended Posts