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

PHP加密存SQL数据库后Node.js解密的最优算法及适配问题

跨PHP和Node.js的加密解密方案:解决你遇到的openssl_encrypt与aes-js不兼容问题

嘿,你遇到的情况太常见了——不是加密算法选得不对,而是PHP的openssl_encrypt和Node.js的aes-js在参数配置上没对齐(比如加密模式、填充方式、密钥/IV的处理、输出编码这些细节)。下面给你推荐两种可靠的方案,附完整的可运行代码,直接抄就能用:

优先选:AES-256-GCM(带认证的加密,更安全)

GCM模式自带数据完整性校验,能防止数据被篡改,而且不需要额外处理填充问题,跨语言适配起来最省心。

PHP 加密代码(存到varchar字段)

<?php
// 注意:密钥必须是32字节(256位),建议随机生成后存在配置文件里,别硬写!
$secretKey = 'your-32-byte-secret-key-1234567890abcdef';
// GCM模式推荐用12字节的随机IV,安全又高效
$iv = random_bytes(12);
$rawData = '要加密的敏感内容,比如用户手机号、邮箱';

// 加密:用OPENSSL_RAW_DATA输出二进制,之后转base64方便存在varchar
$encryptedData = openssl_encrypt($rawData, 'aes-256-gcm', $secretKey, OPENSSL_RAW_DATA, $iv, $authTag);

// 把IV、认证标签、加密数据拼在一起,转base64后存储——这三个都是解密必需的
$storedValue = base64_encode($iv . $authTag . $encryptedData);

// 现在$storedValue就是可以直接存到varchar字段的文本啦
echo "存入数据库的值:" . $storedValue;
?>

Node.js 解密代码

const aesjs = require('aes-js');

// 和PHP端完全一致的32字节密钥
const secretKey = aesjs.utils.utf8.toBytes('your-32-byte-secret-key-1234567890abcdef');
// 从数据库取出的base64字符串
const storedValue = '这里替换成从数据库拿到的加密值';

// 解码base64,拆分出IV、认证标签、加密数据
const decodedBuffer = aesjs.utils.hex.toBytes(aesjs.utils.base64.toHex(storedValue));
const iv = decodedBuffer.slice(0, 12); // 前12字节是IV
const authTag = decodedBuffer.slice(12, 28); // 接下来16字节是GCM的认证标签
const encryptedBytes = decodedBuffer.slice(28); // 剩下的是加密后的内容

// 初始化GCM解密器,解密同时验证标签(防止数据被篡改)
const gcmMode = new aesjs.ModeOfOperation.gcm(secretKey, iv);
const decryptedBytes = gcmMode.decrypt(encryptedBytes, authTag);
// 转成可读的字符串
const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);

console.log("解密后的内容:", decryptedText);

备选:AES-256-CBC(兼容性更广,适合老环境)

如果你的服务器环境不支持GCM模式,可以用CBC,但要注意填充方式的一致性(PHP默认用PKCS7填充,Node.js的aes-js需要手动处理去填充)。

PHP 加密代码

<?php
$secretKey = 'your-32-byte-secret-key-1234567890abcdef';
// CBC模式需要16字节的随机IV
$iv = random_bytes(16);
$rawData = '要加密的敏感内容';

// 加密:默认PKCS7填充,输出二进制后转base64
$encryptedData = openssl_encrypt($rawData, 'aes-256-cbc', $secretKey, OPENSSL_RAW_DATA, $iv);
// 拼接IV和加密数据,转base64存储
$storedValue = base64_encode($iv . $encryptedData);

echo "存入数据库的值:" . $storedValue;
?>

Node.js 解密代码

const aesjs = require('aes-js');

const secretKey = aesjs.utils.utf8.toBytes('your-32-byte-secret-key-1234567890abcdef');
const storedValue = '从数据库拿到的加密值';

// 解码并拆分IV和加密数据
const decodedBuffer = aesjs.utils.hex.toBytes(aesjs.utils.base64.toHex(storedValue));
const iv = decodedBuffer.slice(0, 16);
const encryptedBytes = decodedBuffer.slice(16);

// 初始化CBC解密器
const cbcMode = new aesjs.ModeOfOperation.cbc(secretKey, iv);
// 解密后的数据带PKCS7填充,需要手动去除
const decryptedWithPadding = cbcMode.decrypt(encryptedBytes);

// 实现PKCS7去填充
function removePkcs7Padding(data) {
    const paddingLength = data[data.length - 1];
    return data.slice(0, data.length - paddingLength);
}

const decryptedBytes = removePkcs7Padding(decryptedWithPadding);
const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);

console.log("解密后的内容:", decryptedText);

必须注意的几个关键点

  • 密钥要一致且安全:两边必须用完全相同的密钥,密钥长度要和算法匹配(AES-256要32字节,AES-128要16字节),别把密钥硬写在代码里,存到环境变量或配置文件里。
  • IV必须随机且一起存储:每次加密都要生成随机IV,不能固定IV,IV不需要保密,但必须和加密时的一致才能解密,所以要和加密数据一起存在数据库里。
  • 二进制转文本存储:加密后的二进制数据一定要转成base64或hex格式再存到varchar字段,不然会出现乱码或数据丢失。
  • 填充方式对齐:CBC模式下PHP默认用PKCS7填充,Node.js的aes-js不会自动处理,所以要手动实现去填充逻辑,这也是很多人踩坑的地方。

内容的提问来源于stack exchange,提问作者Debashish Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:08:13