Python Language
Bezpieczeństwo i kryptografia
Szukaj…
Wprowadzenie
Python, będący jednym z najpopularniejszych języków w zabezpieczeniach komputerów i sieci, ma duży potencjał w zakresie bezpieczeństwa i kryptografii. Ten temat dotyczy funkcji kryptograficznych i implementacji w Pythonie, od zastosowań w zabezpieczeniach komputera i sieci po algorytmy mieszania i szyfrowania / deszyfrowania.
Składnia
- hashlib.new (nazwa)
- hashlib.pbkdf2_hmac (nazwa, hasło, sól, rundy, dklen = Brak)
Uwagi
Wiele metod w hashlib
wymaga przekazywania wartości interpretowanych jako bufory bajtów, a nie ciągów znaków. Dzieje się tak w przypadku hashlib.new().update()
oraz hashlib.pbkdf2_hmac
. Jeśli masz ciąg znaków, możesz przekonwertować go na bufor bajtów, wstawiając znak b
na początek ciągu:
"This is a string"
b"This is a buffer of bytes"
Obliczanie podsumowania wiadomości
Moduł hashlib
umożliwia tworzenie generatorów hashlib
wiadomości za pomocą new
metody. Te generatory zamieniają dowolny ciąg w skrót o stałej długości:
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-='
Zauważ, że możesz wywołać update
dowolną liczbę razy przed wywołaniem digest
co jest przydatne do mieszania dużej porcji pliku po porcji. Możesz także uzyskać skrót w formacie szesnastkowym, używając hexdigest
:
h.hexdigest()
# ==> '2edfdada56525b1290ff16fb1744cfb482dd2914ffbcb649790c0e589e462d3d'
Dostępne algorytmy mieszania
hashlib.new
wymaga nazwy algorytmu podczas wywoływania go w celu wygenerowania generatora. Aby dowiedzieć się, jakie algorytmy są dostępne w bieżącym interpretatorze Pythona, użyj 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'}
Zwrócona lista będzie się różnić w zależności od platformy i tłumacza; upewnij się, że algorytm jest dostępny.
Istnieją również pewne algorytmy, które są gwarantowane na wszystkich platformach i interpretatorach, które są dostępne przy użyciu hashlib.algorithms_guaranteed
:
hashlib.algorithms_guaranteed
# ==> {'sha256', 'sha384', 'sha1', 'sha224', 'md5', 'sha512'}
Bezpieczne mieszanie haseł
Algorytmu PBKDF2 udostępnionego przez moduł hashlib
można użyć do bezpiecznego szyfrowania hasła. Chociaż ten algorytm nie może zapobiec atakom typu brute-force w celu odzyskania oryginalnego hasła z przechowywanego skrótu, powoduje, że takie ataki są bardzo kosztowne.
import hashlib
import os
salt = os.urandom(16)
hash = hashlib.pbkdf2_hmac('sha256', b'password', salt, 100000)
PBKDF2 może pracować z dowolnym algorytmem skrótu, powyższy przykład używa SHA256, który jest zwykle zalecany. Losowa sól powinna być przechowywana wraz z hashowanym hasłem, będziesz go potrzebować ponownie, aby porównać wprowadzone hasło z przechowywanym hashem. Ważne jest, aby każde hasło było mieszane z inną solą. Jeśli chodzi o liczbę rund, zaleca się ustawienie jej jak najwyżej dla danego zastosowania .
Jeśli chcesz uzyskać wynik w systemie szesnastkowym, możesz użyć modułu binascii
:
import binascii
hexhash = binascii.hexlify(hash)
Uwaga: Podczas PBKDF2 nie jest zły, bcrypt a zwłaszcza scrypt są uważane za silniejsze przed atakami brute-force. W tej chwili żadna z nich nie jest częścią standardowej biblioteki Pythona.
Hashowanie plików
Hash jest funkcją, która konwertuje sekwencję bajtów o zmiennej długości na sekwencję o stałej długości. Hashowanie plików może być korzystne z wielu powodów. Za pomocą skrótów można sprawdzić, czy dwa pliki są identyczne lub sprawdzić, czy zawartość pliku nie została uszkodzona lub zmieniona.
Możesz użyć hashlib
do wygenerowania skrótu dla pliku:
import hashlib
hasher = hashlib.new('sha256')
with open('myfile', 'r') as f:
contents = f.read()
hasher.update(contents)
print hasher.hexdigest()
W przypadku większych plików można zastosować bufor o stałej długości:
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())
Szyfrowanie symetryczne za pomocą pycrypto
Wbudowana funkcja szyfrowania Pythona jest obecnie ograniczona do mieszania. Szyfrowanie wymaga modułu innej firmy, takiego jak pycrypto . Na przykład zapewnia algorytm AES, który uważa się za najnowocześniejszy w dziedzinie szyfrowania symetrycznego. Poniższy kod zaszyfruje daną wiadomość za pomocą hasła:
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)
Algorytm AES przyjmuje trzy parametry: klucz szyfrowania, wektor inicjalizacji (IV) i rzeczywistą wiadomość do zaszyfrowania. Jeśli masz losowo wygenerowany klucz AES, możesz go użyć bezpośrednio i po prostu wygenerować losowy wektor inicjalizacji. Hasło nie ma jednak odpowiedniego rozmiaru i nie byłoby zalecane, aby używać go bezpośrednio, ponieważ nie jest tak naprawdę losowe i dlatego ma stosunkowo małą entropię. Zamiast tego używamy wbudowanej implementacji algorytmu PBKDF2 do wygenerowania 128-bitowego wektora inicjalizacji i 256-bitowego klucza szyfrowania z hasła.
Zwróć uwagę na losową sól, która jest ważna, aby mieć inny wektor inicjowania i klucz dla każdej wiadomości zaszyfrowanej. Zapewnia to w szczególności, że dwie równe wiadomości nie będą skutkowały identycznym zaszyfrowanym tekstem, ale także zapobiegnie ponownemu wykorzystaniu pracy przez hakera poświęconej zgadywaniu jednego hasła na wiadomościach zaszyfrowanych innym hasłem. Ta sól musi być przechowywana wraz z zaszyfrowaną wiadomością, aby uzyskać ten sam wektor inicjalizacji i klucz do odszyfrowania.
Poniższy kod ponownie odszyfruje naszą wiadomość:
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:])
Generowanie podpisów RSA za pomocą pycrypto
RSA może być użyte do utworzenia podpisu wiadomości. Ważny podpis można wygenerować tylko z dostępem do prywatnego klucza RSA, z drugiej strony sprawdzanie poprawności jest możliwe tylko z odpowiednim kluczem publicznym. Tak długo, jak druga strona zna Twój klucz publiczny, może zweryfikować wiadomość, która ma zostać podpisana przez Ciebie i niezmieniona - na przykład w przypadku wiadomości e-mail. Obecnie do tej funkcji wymagany jest moduł innej firmy, taki jak pycrypto .
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)
Weryfikacja podpisu działa podobnie, ale używa klucza publicznego zamiast klucza prywatnego:
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')
Uwaga : powyższe przykłady wykorzystują algorytm podpisywania PKCS # 1 v1.5, który jest bardzo powszechny. pycrypto implementuje również nowszy algorytm PSS PKCS # 1, zastępując PKCS1_v1_5
przez PKCS1_PSS
w przykładach powinno działać, jeśli chcesz go użyć. Obecnie jednak wydaje się, że nie ma powodu, aby z niego korzystać .
Asymetryczne szyfrowanie RSA za pomocą pycrypto
Zaletą szyfrowania asymetrycznego jest to, że wiadomość można zaszyfrować bez wymiany tajnego klucza z odbiorcą wiadomości. Nadawca musi jedynie znać klucz publiczny adresatów, co pozwala na zaszyfrowanie wiadomości w taki sposób, że tylko wyznaczony odbiorca (który ma odpowiedni klucz prywatny) może ją odszyfrować. Obecnie do tej funkcji wymagany jest moduł innej firmy, taki jak pycrypto .
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)
Odbiorca może odszyfrować wiadomość, jeśli ma odpowiedni klucz prywatny:
with open('privkey.pem', 'rb') as f:
key = RSA.importKey(f.read())
cipher = PKCS1_OAEP.new(key)
decrypted = cipher.decrypt(encrypted)
Uwaga : powyższe przykłady wykorzystują schemat szyfrowania OAEP PKCS # 1. pycrypto implementuje również schemat szyfrowania PKCS # 1 v1.5, ten nie jest zalecany do nowych protokołów, jednak ze względu na znane zastrzeżenia .