You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:58:06