Python'da bir şifreyi tuzlayın ve şifreleyin


93

Bu kodun bir şifre ile şifrelenmesi gerekiyor. Tuz ve karma şifre veri tabanına kaydediliyor. Parolanın kendisi değildir.

Operasyonun hassas doğası göz önüne alındığında, her şeyin koşer olduğundan emin olmak istedim.

import hashlib
import base64
import uuid

password = 'test_password'
salt     = base64.urlsafe_b64encode(uuid.uuid4().bytes)


t_sha = hashlib.sha512()
t_sha.update(password+salt)
hashed_password =  base64.urlsafe_b64encode(t_sha.digest())

Neden tuzu b64 kodluyorsunuz? Sadece tuzu doğrudan kullanmak ve ardından b64 ikisini birlikte kodlamak daha basit olurdu t_sha.digest() + salt. Çözülmüş karma şifrenin tam olarak 32 bayt olduğunu bildiğiniz için tuzlu karma şifresini çözdüğünüzde tuzu daha sonra tekrar bölebilirsiniz.
Duncan

1
@Duncan - Base64 tuzu kodladım, böylece tuhaf konular hakkında endişelenmeden güçlü işlemler yapabildim. "Bayt" sürümü bir dizge olarak çalışacak mı? Eğer durum buysa, o zaman t_sha.digest () 'i de base64 kodlamam gerekmez. Sırf biraz daha karmaşık ve biraz daha az okunabilir göründüğü için muhtemelen karma şifreyi ve tuzu birlikte kaydetmezdim.
Chris Dutrow

Python 2.x kullanıyorsanız, bayt nesnesi bir dizge olarak mükemmel şekilde çalışacaktır. Python, bir dizede sahip olabileceğiniz şeylere herhangi bir kısıtlama getirmez. Bununla birlikte, dizeyi veritabanı gibi herhangi bir harici koda iletirseniz aynısı geçerli olmayabilir. Python 3.x, bayt türlerini ve dizeleri ayırt eder, bu durumda tuz üzerinde dize işlemlerini kullanmak istemezsiniz.
Duncan

4
Bunu python'da nasıl yapacağınızı söyleyemem, ancak sade SHA-512 kötü bir seçimdir. PBKDF2, bcrypt veya scrypt gibi yavaş bir karma kullanın.
CodesInChaos

Yan not: Kriptografik rastgelelik kaynağı olarak UUID'leri kullanmamanızı tavsiye ederim. Evet, CPython tarafından kullanılan uygulama kriptografik olarak güvenlidir , ancak bu Python'un spesifikasyonu veya UUID spesifikasyonu tarafından dikte edilmemiştir ve savunmasız uygulamalar mevcuttur . Kod tabanınız güvenli UUID4'ler olmadan bir Python uygulaması kullanılarak çalıştırılırsa, güvenliğinizi zayıflatmış olursunuz. Bu pek olası olmayan bir senaryo olabilir, ancak bunun secretsyerine kullanmanın hiçbir maliyeti yoktur .
Mark Amery

Yanıtlar:


49

DÜZENLEME: Bu cevap yanlış. SHA512'nin tek bir yinelemesi hızlıdır , bu da onu bir parola karma işlevi olarak kullanılmasını uygunsuz kılar. Onun yerine buradaki diğer cevaplardan birini kullanın.


Bana göre iyi görünüyor. Ancak, gerçekten base64'e ihtiyacınız olmadığından eminim. Bunu yapabilirsin:

import hashlib, uuid
salt = uuid.uuid4().hex
hashed_password = hashlib.sha512(password + salt).hexdigest()

Zorluk yaratmazsa, tuz ve karma parolayı onaltılık dizeler yerine ham baytlar olarak depolayarak veritabanınızda biraz daha verimli depolama elde edebilirsiniz. Bunu yapmak hexiçin bytesve hexdigestile değiştirin digest.


1
Evet, hex işe yarardı. Ben base64'ü tercih ediyorum çünkü dizeler biraz daha kısa. Daha kısa dizelerde dolaşmak ve işlemleri yapmak daha etkilidir.
Chris Dutrow

Şimdi, şifreyi geri almak için nasıl tersine çevirirsiniz?
nodebase

28
Tersine çevirmezsiniz, şifreyi asla tersine çeviremezsiniz. Bu yüzden onu hash ediyoruz ve şifrelemiyoruz. Bir giriş şifresini saklanan bir şifre ile karşılaştırmanız gerekirse, girişi hashler ve karmaları karşılaştırırsınız. Bir şifreyi şifrelerseniz, anahtara sahip olan herhangi biri şifresini çözebilir ve görebilir. Güvenli değil
Sebastian Gabriel Vinci

4
uuid.uuid4 (). hex her oluşturulduğunda farklıdır. Aynı uuid'i geri alamazsanız, kontrol amaçlı bir şifreyi nasıl karşılaştıracaksınız?
LittleBobbyTables

3
@LittleBobbyTables Sanırım saltveritabanında ve tuzlu karma şifre de saklanıyor.
clemtoy

70

Bu sorunun diğer yanıtlarına dayanarak, bcrypt kullanarak yeni bir yaklaşım uyguladım.

Neden bcrypt kullanıyorsunuz?

Eğer doğru anlamak, kullanmak argüman bcryptüzerinde SHA512olmasıdır bcryptyavaş olacak şekilde tasarlanmıştır. bcryptkarma şifreyi ilk kez oluştururken ne kadar yavaş olmasını istediğinizi ayarlama seçeneği de vardır:

# The '12' is the number that dictates the 'slowness'
bcrypt.hashpw(password, bcrypt.gensalt( 12 ))

Yavaşlatma arzu edilir çünkü kötü niyetli bir taraf, şifreleri karıştırılmış şifrelerin bulunduğu masaya el koyarsa, onları kaba kuvvet uygulamak çok daha zordur.

Uygulama

def get_hashed_password(plain_text_password):
    # Hash a password for the first time
    #   (Using bcrypt, the salt is saved into the hash itself)
    return bcrypt.hashpw(plain_text_password, bcrypt.gensalt())

def check_password(plain_text_password, hashed_password):
    # Check hashed password. Using bcrypt, the salt is saved into the hash itself
    return bcrypt.checkpw(plain_text_password, hashed_password)

Notlar

Kütüphaneyi aşağıdakileri kullanarak bir linux sisteminde oldukça kolay bir şekilde kurabildim:

pip install py-bcrypt

Ancak, Windows sistemlerime kurarken daha fazla sorun yaşadım. Bir yamaya ihtiyacı var gibi görünüyor. Bu Stack Overflow sorusuna bakın: win 7 64bit python'a py-bcrypt kurulumu


4
12, gensalt için varsayılan değerdir
Ahmed Hegazy

2
Pypi.python.org/pypi/bcrypt/3.1.0'a göre , bcrypt için maksimum şifre uzunluğu 72 bayttır. Bunun dışındaki tüm karakterler göz ardı edilir. Bu nedenle, önce bir kriptografik karma işleviyle karma oluşturmayı ve ardından karmayı base64 kodlamasını önerirler (ayrıntılar için bağlantıya bakın). Yan not: Görünüşe py-bcryptgöre eski pypi paketi ve o zamandan beri olarak yeniden adlandırıldı bcrypt.
balu

48

Akıllı olan şey, kriptoyu kendiniz yazmak değil, passlib gibi bir şey kullanmaktır: https://bitbucket.org/ecollins/passlib/wiki/Home

Kripto kodunuzu güvenli bir şekilde yazmakla uğraşmak çok kolaydır. İşin kötü yanı, kripto olmayan bir kodla, programınız çöktüğü için çalışmadığında genellikle hemen fark edersiniz. Kripto koduyla, çoğu zaman yalnızca geç kaldıktan ve verilerinizin güvenliği ihlal edildikten sonra öğrenirsiniz. Bu nedenle, konu hakkında bilgili bir başkası tarafından yazılmış, savaşta test edilmiş protokollere dayanan bir paket kullanmanın daha iyi olacağını düşünüyorum.

Ayrıca passlib, kullanımı kolaylaştıran ve eski bir protokolün bozulduğu ortaya çıkarsa daha yeni bir parola hashing protokolüne yükseltmeyi kolaylaştıran bazı güzel özelliklere sahiptir.

Ayrıca sha512'nin sadece tek bir turu sözlük saldırılarına karşı daha savunmasızdır. sha512 hızlı olacak şekilde tasarlanmıştır ve bu aslında parolaları güvenli bir şekilde saklamaya çalışırken kötü bir şeydir. Başkaları bu tür konular hakkında uzun süredir ve derinlemesine düşündüler, bu yüzden bundan yararlansanız iyi olur.


5
Sanırım kripto kitaplıklarını kullanma tavsiyesi iyi, ancak OP zaten Python standart kitaplığında (passlib'in aksine) bir kripto kitaplığı olan hashlib kullanıyor. OPs durumunda olsaydım hashlib kullanmaya devam ederdim.
dgh

18
@dghubble hashlib, kriptografik hash fonksiyonları içindir. passlibparolaları güvenli bir şekilde saklamak içindir. Aynı şey değiller (pek çok insan öyle düşünüyor gibi görünse de .. ve sonra kullanıcılarının şifreleri kırılır).
Brendan Long

3
Herhangi birinin merak passlibetmesi durumunda: döndürülen hash dizesinde saklanan kendi tuzunu üretir (en azından BCrypt + SHA256 gibi belirli şemalar için ) - bu yüzden endişelenmenize gerek yok.
z0r

22

Bunun Python 3'te çalışması için örneğin UTF-8 kodlamanız gerekir:

hashed_password = hashlib.sha512(password.encode('utf-8') + salt.encode('utf-8')).hexdigest()

Aksi takdirde şunları elde edersiniz:

Traceback (en son çağrı):
Dosya "", satır 1,
hashed_password = hashlib.sha512 (parola + salt) .hexdigest ()
TypeError: Unicode nesneleri hashing işleminden önce kodlanmalıdır


7
Hayır. Şifreleri karıştırmak için herhangi bir sha hash işlevi kullanmayın. Bcrypt gibi bir şey kullanın. Nedeni için diğer soruların yorumlarını görün.
josch

12

Python 3.4'ten itibaren, hashlibstandart kitaplıktaki modül , "güvenli parola karması için tasarlanmış" anahtar türetme işlevlerini içerir .

Öyleyse bunlardan birini, örneğin hashlib.pbkdf2_hmac, aşağıdakiler kullanılarak oluşturulan bir tuzla kullanın os.urandom:

from typing import Tuple
import os
import hashlib
import hmac

def hash_new_password(password: str) -> Tuple[bytes, bytes]:
    """
    Hash the provided password with a randomly-generated salt and return the
    salt and hash to store in the database.
    """
    salt = os.urandom(16)
    pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    return salt, pw_hash

def is_correct_password(salt: bytes, pw_hash: bytes, password: str) -> bool:
    """
    Given a previously-stored salt and hash, and a password provided by a user
    trying to log in, check whether the password is correct.
    """
    return hmac.compare_digest(
        pw_hash,
        hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    )

# Example usage:
salt, pw_hash = hash_new_password('correct horse battery staple')
assert is_correct_password(salt, pw_hash, 'correct horse battery staple')
assert not is_correct_password(salt, pw_hash, 'Tr0ub4dor&3')
assert not is_correct_password(salt, pw_hash, 'rosebud')

Bunu not et:

  • 16 baytlık tuz ve 100000 PBKDF2 yinelemesinin kullanımı, Python belgelerinde önerilen minimum sayılarla eşleşir. Yineleme sayısını daha da artırmak, karmalarınızın hesaplanmasını yavaşlatacak ve dolayısıyla daha güvenli hale getirecektir.
  • os.urandom her zaman kriptografik olarak güvenli bir rastgelelik kaynağı kullanır
  • hmac.compare_digestiçinde kullanılan is_correct_password, temelde ==dizeler için yalnızca operatördür, ancak kısa devre yapamaz, bu da onu zamanlama saldırılarına karşı bağışık hale getirir. Bu muhtemelen herhangi bir ekstra güvenlik değeri sağlamaz, ancak canımı da yakmaz, bu yüzden devam ettim ve kullandım.

İyi bir parola karmasını neyin oluşturduğuna dair teori ve parolaları hashing için uygun diğer işlevlerin bir listesi için bkz. Https://security.stackexchange.com/q/211/29805 .


10

passlib, mevcut bir sistem tarafından depolanan karmaları kullanmanız gerekiyorsa yararlı görünmektedir. Biçimin kontrolüne sahipseniz, bcrypt veya scrypt gibi modern bir karma kullanın. Şu anda, bcrypt'in python'dan kullanımı çok daha kolay görünüyor.

passlib, bcrypt'i destekler ve arkaplan olarak py-bcrypt kurulmasını önerir: http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

Ayrıca kullanabilirsiniz py-Bcrypt sen passlib yüklemek istemiyorsanız doğrudan. Benioku dosyasında temel kullanım örnekleri vardır.

ayrıca bkz: Python'da şifre ve tuz için karma oluşturmak üzere scrypt nasıl kullanılır



0

Öncelikle içe aktar: -

import hashlib, uuid

Ardından, yönteminizde buna göre kodunuzu değiştirin:

uname = request.form["uname"]
pwd=request.form["pwd"]
salt = hashlib.md5(pwd.encode())

Sonra bu tuzu ve uname'i veritabanı sql sorgunuzda geçirin, aşağıdaki oturum açma bir tablo adıdır:

sql = "insert into login values ('"+uname+"','"+email+"','"+salt.hexdigest()+"')"

uname = request.form ["uname"] pwd = request.form ["pwd"] salt = hashlib.md5 (pwd.encode ()) Daha sonra bu tuzu ve uname veritabanınıza geçirin sql sorgusu, aşağıdaki oturum açma bir tablo adıdır : - sql = "giriş değerlerine ekle ('" + uname + "', '" + email + "', '" + salt.hexdigest () + "')"
Sheetal Jha

-1 çünkü md5 çok hızlıdır, bu da md5'in tek bir yinelemesini bir parola karma işlevi için zayıf bir seçim haline getirir.
Mark Amery
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.