Richten Sie mit Python 3 einen einfachen HTTPS-Server ein

Überblick

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.

Annahme

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

Überprüfung des HTTP-Servers

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")

Selbstsigniertes Zertifikat und private Schlüsselgenerierung

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

Richten Sie einen HTTPS-Server ein

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.

Unterstützt CGI

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");

Unterstützt WSGI

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

Generieren

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.

Möglichkeit

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)

Unterschied zwischen zwei wrap_socket

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.

Kryptografische Suite

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.

Andere

Mindestversion von TLS

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'])"

Vorschlag einer neuen API für TLS

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

Richten Sie mit Python 3 einen einfachen HTTPS-Server ein
Richten Sie einen einfachen SMTP-Server in Python ein
Richten Sie einen einfachen HTTPS-Server mit Asyncio ein
Richten Sie einen Test-SMTP-Server in Python ein.
[Vagrant] Richten Sie einen einfachen API-Server mit Python ein
Richten Sie einen UDP-Server in der Sprache C ein
So richten Sie einen einfachen SMTP-Server ein, der lokal in Python getestet werden kann
Richten Sie mit http.server in Python 3 in 30 Sekunden einen lokalen Webserver ein
Richten Sie einen einfachen lokalen Server auf Ihrem Mac ein
Richten Sie in 30 Minuten einen kostenlosen Server unter AWS ein
Implementierung eines einfachen Algorithmus in Python 2
Führen Sie einen einfachen Algorithmus in Python aus
Ein einfacher HTTP-Client, der in Python implementiert ist
Richten Sie mit Docker einen Samba-Server ein
Versuchen Sie, eine einfache Animation in Python zu zeichnen
Erstellen Sie eine einfache GUI-App in Python
Richten Sie mit Twisted einen Mailserver ein
Schreiben Sie eine einfache Giermethode in Python
Schreiben Sie ein einfaches Vim-Plugin in Python 3
Einfacher gRPC in Python
Richten Sie einen lokalen Server mit Go-File-Upload ein.
Senden Sie E-Mails mit mailx an einen mit Python eingerichteten Dummy-SMTP-Server.
Starten Sie mit Docker einen einfachen Python-Webserver
Einfache Pub / Sub-Programmhinweise in Python
Richten Sie einen lokalen Server mit Go-File-Download ein.
Erstellen Sie in Python ein einfaches Momentum-Investmentmodell
So richten Sie einen lokalen Entwicklungsserver ein
Richten Sie eine Python-Entwicklungsumgebung auf Marvericks ein
DNS-Server in Python ....
Richten Sie mit Python einen Dummy-SMTP-Server ein und überprüfen Sie den Sendevorgang von Action Mailer
Einführung und Verwendung der Python-Flasche ・ Versuchen Sie, einen einfachen Webserver mit Anmeldefunktion einzurichten
Stellen Sie Docker in Windows Home und führen Sie einen einfachen Webserver mit Python aus
Schreiben Sie ein super einfaches molekulardynamisches Programm in Python
So richten Sie eine Python-Umgebung mit pyenv ein
Richten Sie einen Minecraft-Ressourcenserver (Spigot) über Docker (2) ein.
Richten Sie mit Samba einen Dateiserver unter Ubuntu 20.04 ein
Erstellen Sie einen einfachen Slackbot mit einer interaktiven Schaltfläche in Python
[Teil 1] Lassen Sie uns einen Micra-Server unter Linux einrichten
Richten Sie einen Minecraft-Ressourcenserver (Spigot) über Docker ein
Erstellen Sie mit Quarry einen gefälschten Minecraft-Server in Python
Richten Sie mit Sublime Text 2 eine Python-Entwicklungsumgebung ein
Proxy für Python-Pip festlegen (beschrieben in pip.ini)
Richten Sie Python 3.4 unter Ubuntu ein
Machen Sie einen Screenshot in Python
Erstellen Sie eine Funktion in Python
Erstellen Sie ein Wörterbuch in Python
Erstellen Sie einen (einfachen) REST-Server
Für Mac einrichten (Python)
Erstellen Sie ein Lesezeichen in Python
Einfache Regressionsanalyse mit Python
Einfacher HTTP-Server für Python
Erstellen Sie einen einfachen Textlint-Server
Zeichne ein Herz in Python
Einfacher IRC-Client mit Python
Richten Sie Nunjucks in Node.js ein
Stellen Sie den Python-Test in Jenkins ein
Ich möchte mit swagger-codegen in Sekundenschnelle einen Mock-Server für Python-Flask einrichten.
Hello World mit einem einfachen Webserver, der WSGI (Web Server Gateway Interface) in Python folgt