Muối và mật khẩu băm bằng Python


93

Mã này được cho là băm mật khẩu với một muối. Muối và mật khẩu băm đang được lưu trong cơ sở dữ liệu. Mật khẩu chính nó không phải là.

Do tính chất nhạy cảm của hoạt động, tôi muốn đảm bảo rằng mọi thứ đều khác biệt.

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())

Tại sao bạn mã hóa b64 muối? Sẽ đơn giản hơn nếu chỉ sử dụng trực tiếp muối và sau đó mã hóa b64 với nhau t_sha.digest() + salt. Bạn có thể tách muối ra một lần nữa sau khi bạn đã giải mã mật khẩu băm muối vì bạn biết mật khẩu băm được giải mã chính xác là 32 byte.
Duncan

1
@Duncan - Tôi đã mã hóa base64 muối để tôi có thể thực hiện các hoạt động mạnh mẽ trên nó mà không phải lo lắng về các vấn đề kỳ lạ. Phiên bản "byte" có hoạt động như một chuỗi không? Nếu đúng như vậy thì tôi cũng không cần phải mã hóa base64 t_sha.digest (). Tôi có thể sẽ không lưu mật khẩu băm và muối với nhau chỉ vì điều đó có vẻ phức tạp hơn một chút và ít dễ đọc hơn một chút.
Chris Dutrow

Nếu bạn đang sử dụng Python 2.x thì đối tượng byte sẽ hoạt động hoàn hảo như một chuỗi. Python không đặt bất kỳ hạn chế nào đối với những gì bạn có thể có trong một chuỗi. Tuy nhiên, điều tương tự có thể không áp dụng nếu bạn chuyển chuỗi vào bất kỳ mã bên ngoài nào, chẳng hạn như cơ sở dữ liệu. Python 3.x phân biệt kiểu byte và chuỗi, vì vậy trong trường hợp đó, bạn sẽ không muốn sử dụng các thao tác chuỗi trên muối.
Duncan

4
Tôi không thể cho bạn biết cách làm điều đó trong python, nhưng SHA-512 đơn giản là một lựa chọn không tồi. Sử dụng hàm băm chậm như PBKDF2, bcrypt hoặc scrypt.
CodesInChaos

Lưu ý phụ: Tôi khuyên bạn không nên sử dụng UUID như một nguồn ngẫu nhiên mật mã. Có, việc triển khai được CPython sử dụng là an toàn về mặt mật mã , nhưng điều đó không được quy định bởi thông số kỹ thuật của Python cũng như thông số kỹ thuật UUIDtồn tại các triển khai dễ bị tấn công . Nếu cơ sở mã của bạn được chạy bằng cách sử dụng triển khai Python mà không có UUID4 an toàn, bạn sẽ làm yếu tính bảo mật của mình. Đó có thể là một kịch bản khó xảy ra, nhưng secretsthay vào đó sẽ không tốn kém gì .
Mark Amery

Câu trả lời:


49

CHỈNH SỬA: Câu trả lời này là sai. Một lần lặp lại duy nhất của SHA512 rất nhanh , điều này khiến nó không thích hợp để sử dụng làm hàm băm mật khẩu. Sử dụng một trong những câu trả lời khác ở đây để thay thế.


Trông tôi ổn. Tuy nhiên, tôi khá chắc rằng bạn không thực sự cần base64. Bạn chỉ có thể làm điều này:

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

Nếu nó không tạo ra khó khăn, bạn có thể lưu trữ hiệu quả hơn một chút trong cơ sở dữ liệu của mình bằng cách lưu trữ muối và mật khẩu băm dưới dạng byte thô thay vì chuỗi hex. Để làm như vậy, hãy thay thế hexbằng byteshexdigestbằng digest.


1
Có, hex sẽ hoạt động tốt. Tôi thích base64 hơn vì các chuỗi ngắn hơn một chút. Hiệu quả hơn để chuyển xung quanh và thực hiện các hoạt động trên các chuỗi ngắn hơn.
Chris Dutrow

Bây giờ, bạn làm thế nào để đảo ngược nó để lấy lại mật khẩu?
nodebase

28
Bạn không đảo ngược nó, bạn không bao giờ đảo ngược mật khẩu. Đó là lý do tại sao chúng tôi băm nó và chúng tôi không mã hóa nó. Nếu bạn cần so sánh mật khẩu đầu vào với mật khẩu được lưu trữ, bạn băm đầu vào và so sánh các băm. Nếu bạn mã hóa mật khẩu, bất kỳ ai có khóa đều có thể giải mã và xem nó. Nó không an toàn
Sebastian Gabriel Vinci

4
uuid.uuid4 (). hex khác nhau mỗi khi nó được tạo. Bạn sẽ so sánh mật khẩu với mục đích kiểm tra như thế nào nếu bạn không thể lấy lại được mật khẩu như cũ?
LittleBobbyTables

3
@LittleBobbyTables Tôi nghĩ saltđược lưu trữ trong cơ sở dữ liệu và mật khẩu được băm mặn quá.
clemtoy

70

Dựa trên các câu trả lời khác cho câu hỏi này, tôi đã triển khai một cách tiếp cận mới bằng cách sử dụng bcrypt.

Tại sao sử dụng bcrypt

Nếu tôi hiểu đúng, lập luận để sử dụng bcrypttrên SHA512bcryptđược thiết kế để chậm. bcryptcũng có một tùy chọn để điều chỉnh độ chậm mà bạn muốn khi tạo mật khẩu băm lần đầu tiên:

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

Chậm là điều mong muốn bởi vì nếu một bên độc hại đặt tay lên bàn chứa mật khẩu đã băm, thì việc ép buộc họ sẽ khó hơn nhiều.

Thực hiện

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)

Ghi chú

Tôi đã có thể cài đặt thư viện khá dễ dàng trong hệ thống linux bằng cách sử dụng:

pip install py-bcrypt

Tuy nhiên, tôi gặp nhiều khó khăn hơn khi cài đặt nó trên hệ thống cửa sổ của mình. Có vẻ như nó cần một bản vá. Xem câu hỏi Stack Overflow này: cài đặt py-bcrypt trên win 7 64bit python


4
12 là giá trị mặc định cho gensalt
Ahmed Hegazy

2
Theo pypi.python.org/pypi/bcrypt/3.1.0 , độ dài mật khẩu tối đa cho bcrypt là 72 byte. Bất kỳ ký tự nào vượt quá đó đều bị bỏ qua. Vì lý do này, họ khuyên bạn nên băm bằng hàm băm mật mã trước và sau đó mã hóa base64 cho hàm băm (xem liên kết để biết chi tiết). Nhận xét bên lề: Có vẻ như đây py-bcryptlà gói pypi cũ và đã được đổi tên thành bcrypt.
balu

48

Điều thông minh là không phải tự viết tiền điện tử mà sử dụng một thứ gì đó như passlib: https://bitbucket.org/ecollins/passlib/wiki/Home

Rất dễ nhầm lẫn khi viết mã tiền điện tử của bạn một cách an toàn. Điều khó chịu là với mã không phải tiền điện tử, bạn thường nhận ra ngay lập tức khi nó không hoạt động kể từ khi chương trình của bạn gặp sự cố. Trong khi với mã tiền điện tử, bạn thường chỉ phát hiện ra sau khi đã muộn và dữ liệu của bạn đã bị xâm phạm. Vì vậy, tôi nghĩ rằng tốt hơn là sử dụng một gói được viết bởi người khác có hiểu biết về chủ đề này và dựa trên các giao thức đã được thử nghiệm trong trận chiến.

Ngoài ra passlib có một số tính năng hay giúp dễ sử dụng và cũng dễ dàng nâng cấp lên giao thức băm mật khẩu mới hơn nếu giao thức cũ bị hỏng.

Ngoài ra, chỉ một đợt sha512 cũng dễ bị tấn công từ điển hơn. sha512 được thiết kế để nhanh và đây thực sự là một điều tồi tệ khi cố gắng lưu trữ mật khẩu một cách an toàn. Những người khác đã suy nghĩ rất lâu và kỹ lưỡng về tất cả các vấn đề loại này, vì vậy bạn nên tận dụng điều này.


5
Tôi cho rằng lời khuyên về việc sử dụng thư viện crypo là tốt, nhưng OP đã sử dụng hashlib, một thư viện tiền điện tử cũng nằm trong thư viện chuẩn Python (không giống như passlib). Tôi sẽ tiếp tục sử dụng hashlib nếu tôi ở trong tình huống OPs.
dgh

18
@dghubble hashlibdành cho các hàm băm mật mã. passliblà để lưu trữ mật khẩu một cách an toàn. Chúng không giống nhau (mặc dù nhiều người dường như nghĩ như vậy .. và sau đó mật khẩu người dùng của họ bị bẻ khóa).
Brendan Long

3
Trong trường hợp có ai đó đang thắc mắc: passlibtạo muối của riêng nó, được lưu trữ trong chuỗi băm trả về (ít nhất là đối với một số lược đồ nhất định như BCrypt + SHA256 ) - vì vậy bạn không cần phải lo lắng về điều đó.
z0r

22

Để điều này hoạt động trong Python 3, bạn sẽ cần mã hóa UTF-8, ví dụ:

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

Nếu không, bạn sẽ nhận được:

Traceback (lần gọi gần đây nhất):
Tệp "", dòng 1, trong
hashed_password = hashlib.sha512 (password + salt) .hexdigest ()
TypeError: Unicode-object phải được mã hóa trước khi băm


7
Không. Không sử dụng bất kỳ hàm băm nào để băm mật khẩu. Sử dụng một cái gì đó như bcrypt. Xem các bình luận cho các câu hỏi khác để biết lý do.
josch

12

Kể từ Python 3.4, hashlibmô-đun trong thư viện chuẩn chứa các hàm dẫn xuất khóa được "thiết kế để băm mật khẩu an toàn" .

Vì vậy, hãy sử dụng một trong những thứ đó, chẳng hạn như hashlib.pbkdf2_hmac, với muối được tạo ra bằng cách sử dụng 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')

Lưu ý rằng:

  • Việc sử dụng muối 16 byte và 100000 lần lặp lại của PBKDF2 khớp với số lượng tối thiểu được đề xuất trong tài liệu Python. Việc tăng thêm số lần lặp sẽ làm cho các hàm băm của bạn được tính toán chậm hơn và do đó an toàn hơn.
  • os.urandom luôn sử dụng một nguồn ngẫu nhiên an toàn bằng mật mã
  • hmac.compare_digest, được sử dụng trong is_correct_password, về cơ bản chỉ là ==toán tử cho chuỗi nhưng không có khả năng đoản mạch, điều này làm cho nó miễn nhiễm với các cuộc tấn công định thời. Điều đó có lẽ không thực sự cung cấp thêm bất kỳ giá trị bảo mật nào , nhưng nó cũng không ảnh hưởng gì, vì vậy tôi đã tiếp tục và sử dụng nó.

Để biết lý thuyết về điều gì tạo nên một hàm băm mật khẩu tốt và danh sách các chức năng khác thích hợp để băm mật khẩu, hãy xem https://security.stackexchange.com/q/211/29805 .


10

passlib có vẻ hữu ích nếu bạn cần sử dụng hàm băm được lưu trữ bởi hệ thống hiện có. Nếu bạn có quyền kiểm soát định dạng, hãy sử dụng hàm băm hiện đại như bcrypt hoặc scrypt. Tại thời điểm này, bcrypt có vẻ dễ sử dụng hơn nhiều từ python.

passlib hỗ trợ bcrypt và nó khuyên bạn nên cài đặt py-bcrypt làm phần phụ trợ: http://pythonhosted.org/passlib/lib/passlib.hash.bcrypt.html

Bạn cũng có thể sử dụng trực tiếp py-bcrypt nếu không muốn cài đặt passlib. Readme có các ví dụ về cách sử dụng cơ bản.

xem thêm: Cách sử dụng scrypt để tạo hàm băm cho mật khẩu và muối trong Python



0

Đầu tiên nhập khẩu: -

import hashlib, uuid

Sau đó, thay đổi mã của bạn theo điều này trong phương pháp của bạn:

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

Sau đó, chuyển muối và uname này vào truy vấn sql cơ sở dữ liệu của bạn, thông tin đăng nhập bên dưới là tên bảng:

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

uname = request.form ["uname"] pwd = request.form ["pwd"] salt = hashlib.md5 (pwd.encode ()) Sau đó chuyển muối và uname này vào truy vấn sql cơ sở dữ liệu của bạn, bên dưới đăng nhập là tên bảng : - sql = "chèn vào giá trị đăng nhập ( ' "+ uname +"', ' "+ email +"', ' "+ salt.hexdigest () +"')"
Sheetal Jha

-1 vì md5 rất nhanh, điều này làm cho việc sử dụng một lần lặp duy nhất của md5 là một lựa chọn không tốt cho hàm băm mật khẩu.
Mark Amery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.