PHP 与 MySQL 的加密
一
最近需要给数据库存储的敏感数据进行加密,手机号身份证号之类的,本想只要全部hash一遍就可以了,筛选的时候只需要把接收到的数据一样hash就能匹配上了。
但是考虑到可能有需要到模糊匹配的需求,以及某些时候需要返回给前台原始数据的信息,所以直接使用hash单向加密似乎过于暴力了,毕竟加密也只是需要数据库信息单独展示的时候无法被分辨,所以这个需求下也许对称加密是最好的方式。
之前写过一篇关于AES的文章,大概的认识了下这个算法,所以这次就使用AES的方式加密。
后台是PHP,数据库是MySQL。
二
由于是基于现有的系统改造,所以我们需要修改大量的旧数据,这里用较为方便的原生的MySQL语句来批量执行加密操作,这也是我们后面构建后端加密逻辑的基础:
-- 对user表中的phone字段进行加密,处理表中的全部数据
UPDATE `user` SET phone=HEX(AES_ENCRYPT(phone, SUBSTR(SHA2("encrypt_key",256),1,16)));
-- 筛选解密获取原始数据
select AES_DECRYPT(UNHEX(phone),SUBSTR(SHA2(("encrypt_key",256),1,16)) FROM `user`;
加密的SQL语句比较简单,有两个点需要注意:
AES_ENCRYPT()
加密之后需要用HEX()
转为十六进制字符串,否则原始的二进制数据输出无法存储在varchar格式的字段中- 在使用口令加密前需要切出前16位。这是因为在PHP中
openssl_encrypt()
函数接受的口令存在一个默认的长度限制,超出限制就会被静默截断,如果你使用的语言调用加密函数可以控制截断或没有截断则不需要SUBSTR
操作
三
下面的代码展示了PHP中AES加密的基本流程:
<?
$key = "encrypt_key";
$str = "12312341234";
$cipherAlgo = "aes-128-ecb";
$hashKey = hash('sha256', $key);
// 加密
$encryptStr = openssl_encrypt($str , $cipherAlgo, $hashKey);
// byHeNZonWNZGtHQkmmeeNA==
var_dump($encryptStr);
// 存储到数据库中使用格式,与数据库自带的加密方式同步
$dbStr = strtoupper(bin2hex(base64_decode($encryptStr)))
// 6F21DE359A2758D646B474249A679E34
var_dump($dbStr);
// 解密
$decryptStr = openssl_decrypt(base64_encode(hex2bin($encryptStr)), $cipherAlgo, $hashKey);
$key
是AES加密所需的口令,在实际使用的时候,为安全起见最好先对它进行一次hash再用来加密。
上面的代码中使用的格式是aes-128-ecb
,主要是由于这是MySQL中原生的加密函数AES_DECRYPT()
默认使用的加密格式。这里为了方便MySQL统一处理旧数据所以由后端向数据库端妥协,虽然这里用到的是MySQL中的默认加密格式,但实际上使用中并不建议在查询数据库时调用AES_DECRYPT()
函数操作,这些复杂的加解密逻辑操作应该放到后端中处理。
直接使用openssl_encrypt()
加密的结果是如byHeNZonWNZGtHQkmmeeNA==
这样的数据。我们先对其base64_decode()
,然后转换为十六进制字符串,再转为大写。得到的数据是6F21DE359A2758D646B474249A679E34
这样的形式,这和直接在数据库中使用HEX(AES_ENCRYPT())
的结果相同。
这样我们就完成了加密解密敏感数据的逻辑。