Python Language
Sécurité et cryptographie
Recherche…
Introduction
Python, l'un des langages les plus populaires en matière de sécurité informatique et réseau, présente un grand potentiel en matière de sécurité et de cryptographie. Cette rubrique traite des fonctionnalités et des implémentations cryptographiques de Python, de ses utilisations dans la sécurité informatique et réseau aux algorithmes de hachage et de chiffrement / déchiffrement.
Syntaxe
- hashlib.new (nom)
- hashlib.pbkdf2_hmac (nom, mot de passe, sel, tours, dklen = Aucun)
Remarques
De nombreuses méthodes de hashlib
nécessitent que vous passiez des valeurs interprétables en tant que tampons d’octets, plutôt que des chaînes. C'est le cas pour hashlib.new().update()
ainsi que hashlib.pbkdf2_hmac
. Si vous avez une chaîne, vous pouvez la convertir en un tampon d'octets en ajoutant le caractère b
au début de la chaîne:
"This is a string"
b"This is a buffer of bytes"
Calcul d'un résumé de message
Le module hashlib
permet de créer des générateurs de résumé de message via la new
méthode. Ces générateurs transformeront une chaîne arbitraire en un condensé de longueur fixe:
import hashlib
h = hashlib.new('sha256')
h.update(b'Nobody expects the Spanish Inquisition.')
h.digest()
# ==> b'.\xdf\xda\xdaVR[\x12\x90\xff\x16\xfb\x17D\xcf\xb4\x82\xdd)\x14\xff\xbc\xb6Iy\x0c\x0eX\x9eF-='
Notez que vous pouvez appeler update
un nombre arbitraire de fois avant d'appeler digest
ce qui est utile pour hacher un bloc de fichier volumineux par bloc. Vous pouvez également obtenir le résumé au format hexadécimal en utilisant hexdigest
:
h.hexdigest()
# ==> '2edfdada56525b1290ff16fb1744cfb482dd2914ffbcb649790c0e589e462d3d'
Algorithmes de hachage disponibles
hashlib.new
nécessite le nom d'un algorithme lorsque vous l'appelez pour produire un générateur. Pour savoir quels algorithmes sont disponibles dans l’interpréteur Python actuel, utilisez hashlib.algorithms_available
:
import hashlib
hashlib.algorithms_available
# ==> {'sha256', 'DSA-SHA', 'SHA512', 'SHA224', 'dsaWithSHA', 'SHA', 'RIPEMD160', 'ecdsa-with-SHA1', 'sha1', 'SHA384', 'md5', 'SHA1', 'MD5', 'MD4', 'SHA256', 'sha384', 'md4', 'ripemd160', 'sha224', 'sha512', 'DSA', 'dsaEncryption', 'sha', 'whirlpool'}
La liste retournée variera selon la plate-forme et l'interprète; assurez-vous de vérifier que votre algorithme est disponible.
Il y a aussi quelques algorithmes qui sont garantis pour être disponibles sur toutes les plateformes et les interprètes, qui sont disponibles à l' aide hashlib.algorithms_guaranteed
:
hashlib.algorithms_guaranteed
# ==> {'sha256', 'sha384', 'sha1', 'sha224', 'md5', 'sha512'}
Hachage de mot de passe sécurisé
L' algorithme PBKDF2 exposé par le module hashlib
peut être utilisé pour effectuer un hachage sécurisé des mots de passe. Bien que cet algorithme ne puisse pas empêcher les attaques par force brute afin de récupérer le mot de passe original à partir du hachage stocké, cela rend ces attaques très coûteuses.
import hashlib
import os
salt = os.urandom(16)
hash = hashlib.pbkdf2_hmac('sha256', b'password', salt, 100000)
PBKDF2 peut fonctionner avec n'importe quel algorithme de résumé, l'exemple ci-dessus utilise SHA256 qui est généralement recommandé. Le sel aléatoire doit être stocké avec le mot de passe haché, vous en aurez de nouveau besoin pour comparer un mot de passe saisi au hachage stocké. Il est essentiel que chaque mot de passe soit haché avec un sel différent. En ce qui concerne le nombre de tours, il est recommandé de le définir aussi haut que possible pour votre application .
Si vous voulez le résultat en hexadécimal, vous pouvez utiliser le module binascii
:
import binascii
hexhash = binascii.hexlify(hash)
Note : Bien que PBKDF2 ne soit pas mauvais, bcrypt et surtout scrypt sont considérés plus forts contre les attaques en force. Aucune des deux ne fait partie de la bibliothèque standard Python pour le moment.
Hachage de fichiers
Un hachage est une fonction qui convertit une séquence d'octets de longueur variable en une séquence de longueur fixe. Le hachage de fichiers peut être avantageux pour de nombreuses raisons. Les hachages peuvent être utilisés pour vérifier si deux fichiers sont identiques ou vérifier que le contenu d'un fichier n'a pas été corrompu ou modifié.
Vous pouvez utiliser hashlib
pour générer un hachage pour un fichier:
import hashlib
hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
contents = f.read()
hasher.update(contents)
print hasher.hexdigest()
Pour les fichiers plus volumineux, un tampon de longueur fixe peut être utilisé:
import hashlib
SIZE = 65536
hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
buffer = f.read(SIZE)
while len(buffer) > 0:
hasher.update(buffer)
buffer = f.read(SIZE)
print(hasher.hexdigest())
Chiffrement symétrique avec pycrypto
La fonctionnalité de cryptage intégrée de Python se limite actuellement au hachage. Le cryptage nécessite un module tiers tel que pycrypto . Par exemple, il fournit l' algorithme AES qui est considéré comme étant à la pointe de la technologie pour le chiffrement symétrique. Le code suivant chiffrera un message donné à l'aide d'une phrase secrète:
import hashlib
import math
import os
from Crypto.Cipher import AES
IV_SIZE = 16 # 128 bit, fixed for the AES algorithm
KEY_SIZE = 32 # 256 bit meaning AES-256, can also be 128 or 192 bits
SALT_SIZE = 16 # This size is arbitrary
cleartext = b'Lorem ipsum'
password = b'highly secure encryption password'
salt = os.urandom(SALT_SIZE)
derived = hashlib.pbkdf2_hmac('sha256', password, salt, 100000,
dklen=IV_SIZE + KEY_SIZE)
iv = derived[0:IV_SIZE]
key = derived[IV_SIZE:]
encrypted = salt + AES.new(key, AES.MODE_CFB, iv).encrypt(cleartext)
L'algorithme AES prend trois paramètres: la clé de chiffrement, le vecteur d'initialisation (IV) et le message réel à chiffrer. Si vous avez une clé AES générée aléatoirement, vous pouvez l'utiliser directement et simplement générer un vecteur d'initialisation aléatoire. Une phrase secrète n’a pas la bonne taille, cependant, il ne serait pas recommandé de l’utiliser directement, étant donné qu’elle n’est pas vraiment aléatoire et qu’elle a donc peu d’entropie. Au lieu de cela, nous utilisons l' implémentation intégrée de l'algorithme PBKDF2 pour générer un vecteur d'initialisation de 128 bits et une clé de chiffrement de 256 bits à partir du mot de passe.
Notez le sel aléatoire qui est important d'avoir un vecteur d'initialisation et une clé différents pour chaque message chiffré. Cela garantit notamment que deux messages égaux ne produiront pas un texte chiffré identique, mais empêchera également les attaquants de réutiliser le travail passé en devinant une phrase secrète sur les messages chiffrés avec une autre phrase secrète. Ce sel doit être stocké avec le message crypté afin de dériver le même vecteur d'initialisation et la même clé pour le décryptage.
Le code suivant déchiffrera à nouveau notre message:
salt = encrypted[0:SALT_SIZE]
derived = hashlib.pbkdf2_hmac('sha256', password, salt, 100000,
dklen=IV_SIZE + KEY_SIZE)
iv = derived[0:IV_SIZE]
key = derived[IV_SIZE:]
cleartext = AES.new(key, AES.MODE_CFB, iv).decrypt(encrypted[SALT_SIZE:])
Génération de signatures RSA à l'aide de pycrypto
RSA peut être utilisé pour créer une signature de message. Une signature valide ne peut être générée qu'avec un accès à la clé RSA privée, mais la validation est possible avec simplement la clé publique correspondante. Aussi longtemps que l’autre partie connaît votre clé publique, elle peut vérifier le message à signer par vous et inchangé - une approche utilisée par exemple pour le courrier électronique. Actuellement, un module tiers tel que pycrypto est requis pour cette fonctionnalité.
import errno
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
message = b'This message is from me, I promise.'
try:
with open('privkey.pem', 'r') as f:
key = RSA.importKey(f.read())
except IOError as e:
if e.errno != errno.ENOENT:
raise
# No private key, generate a new one. This can take a few seconds.
key = RSA.generate(4096)
with open('privkey.pem', 'wb') as f:
f.write(key.exportKey('PEM'))
with open('pubkey.pem', 'wb') as f:
f.write(key.publickey().exportKey('PEM'))
hasher = SHA256.new(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(hasher)
La vérification de la signature fonctionne de la même manière mais utilise la clé publique plutôt que la clé privée:
with open('pubkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
hasher = SHA256.new(message)
verifier = PKCS1_v1_5.new(key)
if verifier.verify(hasher, signature):
print('Nice, the signature is valid!')
else:
print('No, the message was signed with the wrong private key or modified')
Remarque : Les exemples ci-dessus utilisent l'algorithme de signature PKCS # 1 v1.5, qui est très courant. pycrypto implémente également le nouvel algorithme PSS PKCS # 1, en remplaçant PKCS1_v1_5
par PKCS1_PSS
dans les exemples qui devraient fonctionner si vous souhaitez utiliser celui-ci. Actuellement, il semble y avoir peu de raisons de l'utiliser .
Chiffrement asymétrique RSA avec pycrypto
Le chiffrement asymétrique présente l'avantage de pouvoir chiffrer un message sans échanger une clé secrète avec le destinataire du message. L'expéditeur doit simplement connaître la clé publique du destinataire, ce qui permet de chiffrer le message de telle sorte que seul le destinataire désigné (qui possède la clé privée correspondante) puisse le déchiffrer. Actuellement, un module tiers tel que pycrypto est requis pour cette fonctionnalité.
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
message = b'This is a very secret message.'
with open('pubkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
encrypted = cipher.encrypt(message)
Le destinataire peut déchiffrer le message s'il dispose de la clé privée appropriée:
with open('privkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
decrypted = cipher.decrypt(encrypted)
Remarque : les exemples ci-dessus utilisent le schéma de chiffrement PKCS # 1 OAEP. pycrypto implémente également le schéma de cryptage PKCS # 1 v1.5, celui-ci n'est cependant pas recommandé pour les nouveaux protocoles en raison de mises en garde connues .