Le serveur HTTP de Python est un moyen pratique d'expérimenter le code JavaScript dans un navigateur. HTTPS est requis pour la Progressive Web App (PWA), qui est au centre de la communauté JavaScript depuis 2016, car il n'y avait pas d'autre article sur la modification du module ssl sur le serveur HTTPS de Python et la série Python 3. , J'ai décidé de résumer ce que j'avais enquêté.
En supposant Python 3.6 et OpenSSL 1.0.2 et versions ultérieures. Diverses méthodes et constantes ont été ajoutées au module ssl de la série Python 3, et l'exemple de code peut ne pas fonctionner sur Python 3.6 et les versions antérieures.
Vous pouvez trouver la version d'OpenSSL à partir de la constante de ssl.
>>> import ssl
>>> ssl.OPENSSL_VERSION
Si vous souhaitez démarrer le serveur qui fournit le fichier HTML à partir de la ligne de commande, exécutez la commande suivante:
python3 -m http.server 8000
Si vous souhaitez utiliser CGI, spécifiez l'option --cgi
.
python3 -m http.server --cgi 8000
Le script que vous voulez exécuter doit être dans / cgi-bin
ou / htbin
.
#!/usr/bin/env python3
print("Content-Type: text/plain; charset=utf-8;\r\n")
print("hello world")
Vous pouvez générer un certificat auto-signé et une clé privée avec le one-liner suivant.
# 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
En plus du module http.server
, vous devez charger le module ssl
.
Créez un objet SSLContext
, spécifiez divers paramètres, puis utilisez SSLContext.wrap_socket
pour créer un objet SSLSocket
.
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
et ssl.OP_NO_TLSv1_1
signifient pour désactiver TLS 1.0 et TLS 1.1, respectivement.
Pour CGI, utilisez CGIHTTPRequestHandler
au lieu de 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)
Pour macOS et Unix, le serveur ne fonctionnera pas sans ajouter handler.have_fork = False
.
L'avantage de CGI est qu'il peut exécuter des programmes dans des langages autres que Python. Le code Node.js ressemble à ceci:
node.cgi
#!/usr/bin/env node
console.log("Content-type: text/plain; charset=utf-8\n");
console.log("Hello World");
Vous pouvez utiliser le module wsgiref
pour configurer un serveur compatible WSGI.
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)
Si vous voulez connaître toutes les variables d'environnement définies (ʻenv), allez dans [PEP-333](https://www.python.org/dev/peps/pep-0333/#environ-variables) Se il vous plaît se référer. Vous pouvez également vérifier à partir de la page affichée lorsque
wsgiref.simple_server.demo_app` devient une application d'exécution.
SSLContext
ssl.create_default_context
est recommandé pour un usage général.
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
Les constantes que vous pouvez spécifier sont Purpose.SERVER_AUTH
(utilisé pour créer un socket côté client) et Purpose.CLIENT_AUTH
(utilisé pour créer un socket côté serveur).
Vous pouvez également utiliser directement le constructeur SSLContext
.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Les constantes que vous pouvez spécifier sont PROTOCOL_TLS
(par défaut), PROTOCOL_TLS_CLIENT
et PROTOCOL_TLS_SERVER
. Diverses constantes disponibles dans Python 3.6 et versions antérieures sont obsolètes.
Vous pouvez vérifier ou ajouter des paramètres via les options. Vous pouvez voir les options par défaut comme suit:
>>> 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>
Le code suivant donne le même résultat.
>>> ssl.SSLContext().options
Les options par défaut de Python 3.6 sont:
Vous pouvez accéder ou modifier les options de contexte après avoir encapsulé un objet socket avec wrap_socket
.
server = HTTPServer((host, port), handler)
server.socket = ctx.wrap_socket(server.socket)
print(server.socket.context.options)
Les arguments qui peuvent être spécifiés pour ssl.wrap_socket
et SSLContext.wrap_socket
sont différents. Les arguments de Python 3.6 sont:
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
)
La différence entre les deux est que SSLContext.wrap_socket
a moins d'options à spécifier. La session
ajoutée à SSLContext.wrap_socket
dans Python 3.6 ne peut pas être spécifiée dans ssl.wrap_socket
.
La norme d'utilisation de ssl.wrap_socket
et SSLContext.wrap_socket
est de savoir s'il y a beaucoup ou peu de paramètres.
Si vous utilisez ssl.wrap.socket
, les paramètres seront modifiés via server.socket.context
, donc un grand nombre de paramètres rendront le code difficile à lire.
Un autre avantage de la création directe d'un objet SSLContext
est qu'il peut être utilisé pour une bibliothèque HTTP tierce.
Les suites de chiffrement peuvent être trouvées avec get_ciphers
. Utilisez set_ciphers
pour changer la suite de chiffrement.
>>> import ssl
>>> ctx = ssl.create_default_context().get_ciphers()
>>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
>>> ctx.get_ciphers()
Une suite de chiffrement recommandée est disponible sur le site de Mozilla (https://wiki.mozilla.org/Security/Server_Side_TLS). Cloudflare a publié un fichier de configuration nginx (https://github.com/cloudflare/sslconfig) pour TLS 1.3. OpenSSL 1.1.0 fait partie de TLS 1.3 et sera entièrement pris en charge dans OpenSSL 1.1.1.
La norme PCI DSS v3.2, qui est utilisée pour les services de paiement par carte de crédit, exige que SSL et TSL 1.0 soient désactivés jusqu'à la fin juin 2018. Rapidement, le service CDN utilisé par PyPI, désactive également TLS 1.1.
Vous pouvez trouver la version de TLS prise en charge par Python 3 installée sur votre système dans la ligne suivante.
# 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 propose l'introduction d'une nouvelle API pour TLS. Les motivations de développement incluent le fait que le module ssl dépend d'OpenSSL, ce qui signifie que Python doit être recompilé pour introduire le nouvel OpenSSL et qu'il n'est pas possible de basculer vers différentes bibliothèques TLS différentes d'OpenSSL. Je vais.
Les principales bibliothèques SSL / TLS sont:
Recommended Posts