我正在创建一个需要用户登录的应用程序。用户名和密码必须存储在数据库中,但我不想将它们存储为平面文本。我按照 this 教程了解如何在数据库中安全地存储密码。我正在使用教程中的代码,除了我做了一些更改以使用 pyodbc 而不是他们的 SQL 库。
要创建用户名和密码,我使用此代码,该代码效果很好并发布到数据库:
import pyodbc
import os
import hashlib
PEPPER = "SECRET_KEY"
def create_user(username, password):
conn = pyodbc.connect(
r"Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=" + db_path + ";"
)
cursor = conn.cursor()
# Generate secure hash
password_hash = create_secure_password(password)
# Split hash into components
salt, key = password_hash[:16], password_hash[16:]
hash_algo = "sha256"
iterations = 100_000
# Insert into database
info = (username, key, salt, hash_algo, iterations)
cursor.execute("INSERT INTO Login_Info ([username], [password_hash], [salt], [hash_algo], [iterations] ) VALUES (?, ?, ?, ?, ?)", info)
conn.commit()
conn.close()
def create_secure_password(password):
salt = os.urandom(16)
iterations = 100_000
hash_value = hashlib.pbkdf2_hmac('sha256',password.encode('utf-8') + PEPPER.encode('utf-8'), salt, iterations)
password_hash = salt + hash_value
return password_hash
数据库条目显示如下: 数据库中的用户名和密码
但是,当我使用该方法根据数据库检查用户名和密码时,出现以下错误:
TypeError: a bytes-like object is required, not 'str'
错误在于这行代码,我相信它将输入的密码转换为哈希值以与数据库哈希值进行比较
password_hash = hashlib.pbkdf2_hmac(hash_algo, password.encode('utf-8'), salt, iterations)
登录方法完整代码如下:
def login(username, password):
#connect to DB
conn = pyodbc.connect(
r"Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=" + db_path + ";"
)
cursor = conn.cursor()
# Define your SQL query
select_sql = "SELECT * FROM Login_Info WHERE username = ?"
# Execute the query using the cursor
cursor.execute(select_sql, (username,))
# Fetch one row of result
account = cursor.fetchone()
if not account:
print("Invalid username")
return
salt, db_key, hash_algo, iterations = account[2:6]
# Recompute hash from user entered password
password_hash = hashlib.pbkdf2_hmac(hash_algo, password.encode('utf-8'), salt, iterations)
# Compare hashes
if password_hash == db_key:
print("Login successful")
else:
print("Invalid password")
我尝试过在网上寻找其他资源,但找不到遇到这个问题的人
我不相信作为
bytes
对象插入的数据会作为 bytes
对象检索,但我没有您的数据库。 一种解决方案是存储从 str
方法获得的 bytes.hex()
对象,并在从数据库中提取后将其转换回 bytes
对象。
重新计算的哈希值中也缺少
+ PEPPER
。
下面是一个经过验证的最小示例,它删除了数据库并使用字符串:
import os
import hashlib
PEPPER = b'SECRET_KEY' # bytes object so no need to .encode()
def create_user(username, password):
salt, key = create_secure_password(password)
hash_algo = 'sha256'
iterations = 100_000
# Insert these items into database.
return username, key, salt, hash_algo, iterations
def create_secure_password(password):
salt = os.urandom(16)
iterations = 100_000
hash_value = hashlib.pbkdf2_hmac('sha256',password.encode() + PEPPER, salt, iterations)
return salt.hex(), hash_value.hex() # Use hex strings, not bytes objects
def login(username, password):
# Simulate data retrieved from database
_, db_key, salt, hash_algo, iterations = create_user(username, password)
# Recompute hash from user entered password.
# Convert salt to bytes, then the hash to hex.
# Added missing PEPPER.
password_hash = hashlib.pbkdf2_hmac(hash_algo, password.encode() + PEPPER, bytes.fromhex(salt), iterations).hex()
# Compare hashes as strings.
if password_hash == db_key:
print('Login successful')
else:
print('Invalid password')
login('mark', 'abc')
输出:
Login successful