如何用Python解密PHP mcrypt与MD5加密的MySQL登录密码?
解决PHP加密密码的Python解密验证方案
没问题,我帮你拆解清楚PHP代码里的加密逻辑,然后给出对应的Python实现,这样就能从MySQL读取加密后的密码,验证用户输入的明文是否正确了。
先理清PHP加密的核心细节
你的PHP加密/解密逻辑可以拆解成这些关键步骤:
- 密钥生成:加密用的密钥是
md5('MY FIXED KEY')——PHP的md5默认返回32位十六进制字符串,这个字符串直接作为32字节的密钥,刚好匹配MCRYPT_RIJNDAEL_256的256位密钥要求。 - IV(初始化向量)生成:IV是
md5(md5('MY FIXED KEY')),同样是32位十六进制字符串,作为32字节的IV,对应MCRYPT_RIJNDAEL_256的256位块大小。 - 加密算法:用的是块大小256位的Rijndael(注意这不是AES,AES的块大小固定是128位),模式为CBC。
- 填充方式:mcrypt默认用零填充——也就是如果明文长度不是块大小的倍数,会在末尾补0,解密后用
rtrim()去掉这些补的0。 - 输出处理:加密后的原始字节数据会经过Base64编码,存储到数据库里。
Python实现解密与验证步骤
1. 先安装依赖库
Python标准库没有Rijndael算法的实现,我们需要用pycryptodome库,执行下面的命令安装:
pip install pycryptodome
2. 完整的解密与数据库验证代码
下面的代码完全对应你的PHP逻辑,包含从MySQL读取加密密码、解密并验证用户输入的功能:
from Crypto.Cipher import Rijndael import base64 import hashlib import mysql.connector # 和PHP里一致的固定密钥 FIXED_KEY = 'MY FIXED KEY' def php_style_md5(s): # 模拟PHP的md5默认行为:返回32位十六进制字符串 return hashlib.md5(s.encode('utf-8')).hexdigest() def decrypt_encrypted_password(encrypted_b64_str): # 1. 生成密钥和IV,和PHP逻辑完全对齐 key = php_style_md5(FIXED_KEY).encode('utf-8') # 转成32字节的密钥 iv = php_style_md5(php_style_md5(FIXED_KEY)).encode('utf-8') # 转成32字节的IV # 2. 对存储的Base64字符串解码 encrypted_bytes = base64.b64decode(encrypted_b64_str) # 3. 初始化Rijndael密码器,指定块大小为32字节(256位) cipher = Rijndael.new(key, Rijndael.MODE_CBC, iv=iv, block_size=32) # 4. 解密并去掉末尾的零填充(对应PHP的rtrim()) decrypted_bytes = cipher.decrypt(encrypted_bytes) # 只去掉末尾的\x00,和PHP的rtrim()行为一致 decrypted_str = decrypted_bytes.rstrip(b'\x00').decode('utf-8') return decrypted_str def verify_user_password(username, input_password): # 连接你的MySQL数据库,请替换成自己的数据库信息 db_connection = mysql.connector.connect( host="你的数据库地址", user="数据库用户名", password="数据库密码", database="数据库名称" ) cursor = db_connection.cursor() # 查询用户的加密密码 cursor.execute("SELECT encrypted_password FROM users WHERE username = %s", (username,)) query_result = cursor.fetchone() if not query_result: # 用户不存在 cursor.close() db_connection.close() return False # 从查询结果中取出加密后的密码 stored_encrypted_pwd = query_result[0] # 解密数据库中的密码 decrypted_stored_pwd = decrypt_encrypted_password(stored_encrypted_pwd) # 验证输入的密码是否和解密后的密码一致 is_valid = (decrypted_stored_pwd == input_password) # 关闭数据库连接 cursor.close() db_connection.close() return is_valid # 测试示例(取消注释即可使用) # if verify_user_password("test_user", "用户输入的密码"): # print("密码验证通过!") # else: # print("密码错误或用户不存在。")
几个关键注意点
- 别混淆Rijndael和AES:PHP的
MCRYPT_RIJNDAEL_256是块大小256位的Rijndael,而AES的块大小固定是128位,所以不能用Python的AES类,必须用Rijndael类。 - 编码要一致:确保PHP和Python都用UTF-8编码处理字符串,避免解密后出现乱码。
- 零填充的处理:PHP的
mcrypt_encrypt默认补0,解密后用rtrim()去掉末尾的0,Python里对应rstrip(b'\x00'),不要用其他填充方式的处理函数(比如PKCS7)。 - 密钥和IV的字节转换:PHP中md5返回的十六进制字符串直接作为字节传给mcrypt,所以Python里也要把md5的hex字符串转成字节,不能直接用md5的二进制结果。
内容的提问来源于stack exchange,提问作者Sina Jahanbakhsh
相关产品推荐
相关产品推荐

