पायथन में कोई अंतर्निहित एन्क्रिप्शन योजनाएं नहीं हैं। आपको एन्क्रिप्टेड डेटा स्टोरेज को भी गंभीर रखना चाहिए; तुच्छ एन्क्रिप्शन योजनाएं जो एक डेवलपर असुरक्षित समझती हैं और एक खिलौना योजना को एक कम अनुभवी डेवलपर द्वारा सुरक्षित योजना के लिए गलत माना जा सकता है। यदि आप एन्क्रिप्ट करते हैं, तो ठीक से एन्क्रिप्ट करें।
हालाँकि, एक उचित एन्क्रिप्शन योजना को लागू करने के लिए आपको बहुत अधिक काम करने की आवश्यकता नहीं है। सबसे पहले, क्रिप्टोग्राफी व्हील का फिर से आविष्कार न करें, आपके लिए इसे संभालने के लिए एक विश्वसनीय क्रिप्टोग्राफी लाइब्रेरी का उपयोग करें। पायथन 3 के लिए, वह विश्वसनीय पुस्तकालय है cryptography
।
मैं यह भी सलाह देता हूं कि एन्क्रिप्शन और डिक्रिप्शन बाइट्स पर लागू होता है ; पहले बाइट्स के लिए पाठ संदेश सांकेतिक शब्दों में बदलना; stringvalue.encode()
UTF8 के लिए एन्कोड, आसानी से फिर से उपयोग करके वापस bytesvalue.decode()
।
अंतिम लेकिन कम से कम, जब एन्क्रिप्ट और डिक्रिप्टिंग नहीं है, तो हम कुंजी के बारे में बात करते हैं , पासवर्ड नहीं। एक कुंजी मानव यादगार नहीं होनी चाहिए, यह कुछ ऐसा है जिसे आप गुप्त स्थान पर स्टोर करते हैं लेकिन मशीन पठनीय है, जबकि एक पासवर्ड अक्सर मानव-पठनीय और यादगार हो सकता है। आप पासवर्ड से एक कुंजी प्राप्त कर सकते हैं, थोड़ी देखभाल के साथ।
लेकिन एक वेब अनुप्रयोग या प्रक्रिया के लिए एक क्लस्टर में चल रहे मानव ध्यान के बिना इसे चलाने के लिए, आप एक कुंजी का उपयोग करना चाहते हैं। पासवर्ड तब होते हैं जब केवल अंतिम उपयोगकर्ता को विशिष्ट जानकारी तक पहुंचने की आवश्यकता होती है। फिर भी, आप आमतौर पर पासवर्ड के साथ एप्लिकेशन को सुरक्षित करते हैं, फिर एक कुंजी का उपयोग करके एन्क्रिप्टेड जानकारी का आदान-प्रदान करते हैं, शायद उपयोगकर्ता खाते से जुड़ा हुआ है।
सममित कुंजी एन्क्रिप्शन
फर्नेट - एईएस सीबीसी + एचएमएसी, दृढ़ता से अनुशंसित
cryptography
पुस्तकालय भी शामिल है Fernet नुस्खा , क्रिप्टोग्राफी का उपयोग कर के लिए एक सर्वोत्तम प्रथाओं नुस्खा। फर्नेट एक खुला मानक है , जिसमें प्रोग्रामिंग भाषाओं की एक विस्तृत श्रृंखला में तैयार कार्यान्वयन है और यह आपके लिए संस्करण सूचना, टाइमस्टैम्प और संदेश छेड़छाड़ को रोकने के लिए एक एचएमएसी हस्ताक्षर के साथ एईएस सीबीसी एन्क्रिप्शन पैकेज करता है।
फर्नेट संदेशों को एन्क्रिप्ट और डिक्रिप्ट करना और आपको सुरक्षित रखना बहुत आसान बनाता है । यह एक गुप्त के साथ डेटा एन्क्रिप्ट करने के लिए आदर्श विधि है।
मैं आपको Fernet.generate_key()
एक सुरक्षित कुंजी उत्पन्न करने के लिए उपयोग करने की सलाह देता हूं । आप एक पासवर्ड (अगले भाग) का भी उपयोग कर सकते हैं, लेकिन एक पूर्ण 32-बाइट गुप्त कुंजी (16 बाइट्स के साथ एन्क्रिप्ट करने के लिए, और हस्ताक्षर के लिए एक और 16) सबसे पासवर्ड से अधिक सुरक्षित होने जा रहा है जिसके बारे में आप सोच सकते थे।
फ़र्नेट उत्पन्न करने वाली कुंजी bytes
URL के साथ एक ऑब्जेक्ट है और सुरक्षित बेस 64 वर्णों को फ़ाइल करती है, इसलिए प्रिंट करने योग्य:
from cryptography.fernet import Fernet
key = Fernet.generate_key() # store in a secure location
print("Key:", key.decode())
संदेशों को एन्क्रिप्ट या डिक्रिप्ट करने के Fernet()
लिए, दिए गए कुंजी के साथ एक इंस्टेंस बनाएं , और Fernet.encrypt()
या Fernet.decrypt()
, कॉल करने के लिए प्लेनटेक्स्ट संदेश को एन्क्रिप्ट करने और एन्क्रिप्टेड टोकन दोनों ही bytes
ऑब्जेक्ट हैं।
encrypt()
और decrypt()
कार्यों की तरह दिखेगा:
from cryptography.fernet import Fernet
def encrypt(message: bytes, key: bytes) -> bytes:
return Fernet(key).encrypt(message)
def decrypt(token: bytes, key: bytes) -> bytes:
return Fernet(key).decrypt(token)
डेमो:
>>> key = Fernet.generate_key()
>>> print(key.decode())
GZWKEhHGNopxRdOHS4H4IyKhLQ8lwnyU7vRLrM3sebY=
>>> message = 'John Doe'
>>> encrypt(message.encode(), key)
'gAAAAABciT3pFbbSihD_HZBZ8kqfAj94UhknamBuirZWKivWOukgKQ03qE2mcuvpuwCSuZ-X_Xkud0uWQLZ5e-aOwLC0Ccnepg=='
>>> token = _
>>> decrypt(token, key).decode()
'John Doe'
पासवर्ड के साथ फर्नेट - पासवर्ड से ली गई कुंजी, सुरक्षा को कुछ हद तक कमजोर करती है
आप एक गुप्त कुंजी के बजाय एक पासवर्ड का उपयोग कर सकते हैं, बशर्ते आप एक मजबूत कुंजी व्युत्पत्ति विधि का उपयोग करें । आपको संदेश में नमक और HMAC पुनरावृत्ति गणना को शामिल करना है, इसलिए पहले अलग किए गए नमक, गिनती और फर्नेट टोकन के बिना एन्क्रिप्ट किया गया मान फर्नेट-संगत नहीं है:
import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
backend = default_backend()
iterations = 100_000
def _derive_key(password: bytes, salt: bytes, iterations: int = iterations) -> bytes:
"""Derive a secret key from a given password and salt"""
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(), length=32, salt=salt,
iterations=iterations, backend=backend)
return b64e(kdf.derive(password))
def password_encrypt(message: bytes, password: str, iterations: int = iterations) -> bytes:
salt = secrets.token_bytes(16)
key = _derive_key(password.encode(), salt, iterations)
return b64e(
b'%b%b%b' % (
salt,
iterations.to_bytes(4, 'big'),
b64d(Fernet(key).encrypt(message)),
)
)
def password_decrypt(token: bytes, password: str) -> bytes:
decoded = b64d(token)
salt, iter, token = decoded[:16], decoded[16:20], b64e(decoded[20:])
iterations = int.from_bytes(iter, 'big')
key = _derive_key(password.encode(), salt, iterations)
return Fernet(key).decrypt(token)
डेमो:
>>> message = 'John Doe'
>>> password = 'mypass'
>>> password_encrypt(message.encode(), password)
b'9Ljs-w8IRM3XT1NDBbSBuQABhqCAAAAAAFyJdhiCPXms2vQHO7o81xZJn5r8_PAtro8Qpw48kdKrq4vt-551BCUbcErb_GyYRz8SVsu8hxTXvvKOn9QdewRGDfwx'
>>> token = _
>>> password_decrypt(token, password).decode()
'John Doe'
आउटपुट में नमक को शामिल करना एक यादृच्छिक नमक मूल्य का उपयोग करना संभव बनाता है, जो बदले में एन्क्रिप्टेड आउटपुट को पासवर्ड पुन: उपयोग या संदेश पुनरावृत्ति की परवाह किए बिना पूरी तरह से यादृच्छिक होने की गारंटी देता है। पुनरावृत्ति गिनती सहित यह सुनिश्चित करता है कि आप पुराने संदेशों को डिक्रिप्ट करने की क्षमता खोए बिना समय के साथ सीपीयू के प्रदर्शन में वृद्धि के लिए समायोजित कर सकते हैं।
एक पासवर्ड अकेले एक फ़र्नेट 32-बाइट रैंडम की के रूप में सुरक्षित हो सकता है, बशर्ते आप एक समान आकार के पूल से एक ठीक से रैंडम पासवर्ड उत्पन्न करें। 32 बाइट्स आपको 256 ^ 32 नंबर की चाबियाँ प्रदान करते हैं, इसलिए यदि आप 74 वर्णों (26 ऊपरी, 26 निचले, 10 अंक और 12 संभावित प्रतीकों) की वर्णमाला का उपयोग करते हैं, तो आपका पासवर्ड कम से कम math.ceil(math.log(256 ** 32, 74))
== 42 वर्ण लंबा होना चाहिए । हालांकि, एचएमएसी पुनरावृत्तियों की एक अच्छी तरह से चुनी गई बड़ी संख्या कुछ हद तक एन्ट्रापी की कमी को कम कर सकती है क्योंकि यह हमलावर के लिए अपने तरीके से बल देने के लिए बहुत अधिक महंगा बनाता है।
बस पता है कि एक छोटा लेकिन अभी भी सुरक्षित रूप से सुरक्षित पासवर्ड चुनने से इस योजना को अपंग नहीं किया जाएगा, यह सिर्फ संभावित मूल्यों की संख्या को कम करता है जो एक क्रूर-बल हमलावर के माध्यम से खोजना होगा; अपनी सुरक्षा आवश्यकताओं के लिए एक मजबूत पर्याप्त पासवर्ड चुनना सुनिश्चित करें ।
वैकल्पिक
obscuring
एक विकल्प एन्क्रिप्ट करने के लिए नहीं है । Vignere कहते हैं, बस एक कम सुरक्षा सिफर का उपयोग करने के लिए, या एक घर का का उपयोग नहीं करने के लिए परीक्षा हो। इन दृष्टिकोणों में कोई सुरक्षा नहीं है, लेकिन एक अनुभवहीन डेवलपर दे सकता है जिसे भविष्य में आपके कोड को सुरक्षा का भ्रम बनाए रखने का काम दिया जाता है, जो किसी भी सुरक्षा से बदतर नहीं है।
यदि आप सभी की जरूरत अस्पष्टता है, तो बस डेटा 6464; URL- सुरक्षित आवश्यकताओं के लिए, base64.urlsafe_b64encode()
फ़ंक्शन ठीक है। यहां एक पासवर्ड का उपयोग न करें, बस सांकेतिक शब्दों में बदलना और आप कर रहे हैं। अधिकतम पर, कुछ संपीड़न जोड़ें (जैसे zlib
):
import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
def obscure(data: bytes) -> bytes:
return b64e(zlib.compress(data, 9))
def unobscure(obscured: bytes) -> bytes:
return zlib.decompress(b64d(obscured))
यह में बदल जाता b'Hello world!'
है b'eNrzSM3JyVcozy_KSVEEAB0JBF4='
।
अखंडता ही
यदि आप सभी को यह सुनिश्चित करने का एक तरीका है कि डेटा को एक अविश्वसनीय ग्राहक को भेजे जाने और वापस प्राप्त होने के बाद अनलॉक्ड होने पर भरोसा किया जा सकता है , तो आप डेटा पर हस्ताक्षर करना चाहते हैं, तो आप SHA1 के साथ इसके लिए hmac
लाइब्रेरी का उपयोग कर सकते हैं (फिर भी HMAC हस्ताक्षर के लिए सुरक्षित माना जाता है ) या बेहतर:
import hmac
import hashlib
def sign(data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
assert len(key) >= algorithm().digest_size, (
"Key must be at least as long as the digest size of the "
"hashing algorithm"
)
return hmac.new(key, data, algorithm).digest()
def verify(signature: bytes, data: bytes, key: bytes, algorithm=hashlib.sha256) -> bytes:
expected = sign(data, key, algorithm)
return hmac.compare_digest(expected, signature)
डेटा साइन करने के लिए इसका उपयोग करें, फिर डेटा के साथ हस्ताक्षर संलग्न करें और क्लाइंट को भेजें। जब आप डेटा वापस प्राप्त करते हैं, तो डेटा और हस्ताक्षर को विभाजित करें और सत्यापित करें। मैंने SHA256 के लिए डिफ़ॉल्ट एल्गोरिथ्म सेट किया है, इसलिए आपको 32-बाइट कुंजी की आवश्यकता होगी:
key = secrets.token_bytes(32)
आप itsdangerous
लाइब्रेरी को देखना चाहते हैं , जो विभिन्न स्वरूपों में क्रमबद्धता और डी-सीरियलाइजेशन के साथ इसे तैयार करता है।
एन्क्रिप्शन और अखंडता प्रदान करने के लिए एईएस-जीसीएम एन्क्रिप्शन का उपयोग करना
फर्नेट एन्क्रिप्टेड डेटा की अखंडता सुनिश्चित करने के लिए एचएमएसी हस्ताक्षर के साथ एईसी-सीबीसी पर बनाता है; दुर्भावनापूर्ण हमलावर आपके सिस्टम को खराब इनपुट के साथ हलकों में व्यस्त रखने के लिए आपके सिस्टम को बकवास डेटा नहीं खिला सकता है, क्योंकि सिफरटेक्स्ट पर हस्ताक्षर किए गए हैं।
गाल्वा / काउंटर मोड ब्लॉक सिफर सिफर है और एक का उत्पादन टैग इसी उद्देश्य को पूरा करने के लिए है, तो एक ही प्रयोजनों की सेवा करने के लिए इस्तेमाल किया जा सकता। नकारात्मक पक्ष यह है कि फर्नेट के विपरीत अन्य प्लेटफार्मों पर पुन: उपयोग करने के लिए एक आसान-से-उपयोग-एक-फिट-सभी नुस्खा नहीं है। एईएस-जीसीएम भी पैडिंग का उपयोग नहीं करता है, इसलिए यह एन्क्रिप्शन सिफरटेक्स्ट इनपुट संदेश की लंबाई से मेल खाता है (जबकि फ़र्नेट / एईएस-सीबीसी संदेशों को निश्चित लंबाई के ब्लॉकों को एन्क्रिप्ट करता है, संदेश की लंबाई को कुछ हद तक अस्पष्ट करता है)।
AES256-GCM एक कुंजी के रूप में सामान्य 32 बाइट गुप्त लेता है:
key = secrets.token_bytes(32)
तो उपयोग करें
import binascii, time
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.exceptions import InvalidTag
backend = default_backend()
def aes_gcm_encrypt(message: bytes, key: bytes) -> bytes:
current_time = int(time.time()).to_bytes(8, 'big')
algorithm = algorithms.AES(key)
iv = secrets.token_bytes(algorithm.block_size // 8)
cipher = Cipher(algorithm, modes.GCM(iv), backend=backend)
encryptor = cipher.encryptor()
encryptor.authenticate_additional_data(current_time)
ciphertext = encryptor.update(message) + encryptor.finalize()
return b64e(current_time + iv + ciphertext + encryptor.tag)
def aes_gcm_decrypt(token: bytes, key: bytes, ttl=None) -> bytes:
algorithm = algorithms.AES(key)
try:
data = b64d(token)
except (TypeError, binascii.Error):
raise InvalidToken
timestamp, iv, tag = data[:8], data[8:algorithm.block_size // 8 + 8], data[-16:]
if ttl is not None:
current_time = int(time.time())
time_encrypted, = int.from_bytes(data[:8], 'big')
if time_encrypted + ttl < current_time or current_time + 60 < time_encrypted:
# too old or created well before our current time + 1 h to account for clock skew
raise InvalidToken
cipher = Cipher(algorithm, modes.GCM(iv, tag), backend=backend)
decryptor = cipher.decryptor()
decryptor.authenticate_additional_data(timestamp)
ciphertext = data[8 + len(iv):-16]
return decryptor.update(ciphertext) + decryptor.finalize()
मैंने उसी टाइम-टू-लाइव उपयोग-मामलों का समर्थन करने के लिए टाइमस्टैम्प को शामिल किया है जो फर्नेट का समर्थन करता है।
इस पृष्ठ पर अन्य दृष्टिकोण, पायथन 3 में
एईएस सीएफबी - सीबीसी की तरह लेकिन पैड की आवश्यकता के बिना
यह वह दृष्टिकोण है जो सभी а Vаиітy का अनुसरण करता है, यद्यपि गलत तरीके से। यह cryptography
संस्करण है, लेकिन ध्यान दें कि मैं IV को सिफरटेक्स्ट में शामिल करता हूं , इसे वैश्विक के रूप में संग्रहीत नहीं किया जाना चाहिए (IV का पुन: उपयोग करने से कुंजी की सुरक्षा कमजोर हो जाती है, और इसे मॉड्यूल वैश्विक के रूप में संग्रहीत करने का मतलब है कि यह फिर से उत्पन्न होगा। अगले पायथन के आह्वान पर, सभी सिफरटेक्स्ट अविवेकी को प्रस्तुत करते हुए):
import secrets
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
backend = default_backend()
def aes_cfb_encrypt(message, key):
algorithm = algorithms.AES(key)
iv = secrets.token_bytes(algorithm.block_size // 8)
cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
encryptor = cipher.encryptor()
ciphertext = encryptor.update(message) + encryptor.finalize()
return b64e(iv + ciphertext)
def aes_cfb_decrypt(ciphertext, key):
iv_ciphertext = b64d(ciphertext)
algorithm = algorithms.AES(key)
size = algorithm.block_size // 8
iv, encrypted = iv_ciphertext[:size], iv_ciphertext[size:]
cipher = Cipher(algorithm, modes.CFB(iv), backend=backend)
decryptor = cipher.decryptor()
return decryptor.update(encrypted) + decryptor.finalize()
इसमें एचएमएसी हस्ताक्षर के अतिरिक्त कवच का अभाव है और टाइमस्टैम्प नहीं है; आपको स्वयं को जोड़ना होगा।
उपरोक्त यह भी दर्शाता है कि बुनियादी क्रिप्टोग्राफी बिल्डिंग ब्लॉकों को गलत तरीके से संयोजित करना कितना आसान है; IV मूल्य के सभी the Vаиітy के गलत हैंडलिंग से डेटा ब्रीच हो सकता है या सभी एन्क्रिप्टेड संदेश बिना पढ़े जा सकते हैं क्योंकि IV खो गया है। इसके बजाय फर्नेट का उपयोग करना आपको ऐसी गलतियों से बचाता है।
एईएस ईसीबी - सुरक्षित नहीं है
यदि आपने पहले एईएस ईसीबी एन्क्रिप्शन को लागू किया था और अभी भी पायथन 3 में इसका समर्थन करने की आवश्यकता है, तो आप ऐसा अभी भी कर सकते हैं cryptography
। एक ही चेतावनी लागू होती है, ईसीबी वास्तविक जीवन के अनुप्रयोगों के लिए पर्याप्त सुरक्षित नहीं है । पायथन 3 के लिए उस उत्तर को फिर से लागू करना, जिसमें पैडिंग की स्वचालित हैंडलिंग शामिल है:
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
backend = default_backend()
def aes_ecb_encrypt(message, key):
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
encryptor = cipher.encryptor()
padder = padding.PKCS7(cipher.algorithm.block_size).padder()
padded = padder.update(msg_text.encode()) + padder.finalize()
return b64e(encryptor.update(padded) + encryptor.finalize())
def aes_ecb_decrypt(ciphertext, key):
cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend)
decryptor = cipher.decryptor()
unpadder = padding.PKCS7(cipher.algorithm.block_size).unpadder()
padded = decryptor.update(b64d(ciphertext)) + decryptor.finalize()
return unpadder.update(padded) + unpadder.finalize()
फिर, इसमें एचएमएसी हस्ताक्षर का अभाव है, और आपको वैसे भी ईसीबी का उपयोग नहीं करना चाहिए। ऊपर केवल यह वर्णन करना है कि cryptography
आम क्रिप्टोग्राफिक बिल्डिंग ब्लॉकों को संभाल सकते हैं, यहां तक कि उन लोगों को भी जिन्हें आपको वास्तव में उपयोग नहीं करना चाहिए।