表 14.18 加密函数
Name | Description |
---|---|
AES_DECRYPT() |
使用 AES 解密 |
AES_ENCRYPT() |
使用 AES 加密 |
COMPRESS() |
以二进制字符串形式返回结果 |
MD5() |
计算 MD5 校验和 |
RANDOM_BYTES() |
返回随机字节向量 |
SHA1() , SHA() |
计算 SHA-1 160 位校验和 |
SHA2() |
计算 SHA-2 校验和 |
STATEMENT_DIGEST() |
计算语句摘要哈希值 |
STATEMENT_DIGEST_TEXT() |
计算标准化语句摘要 |
UNCOMPRESS() |
解压缩字符串 |
UNCOMPRESSED_LENGTH() |
返回字符串在压缩前的长度 |
VALIDATE_PASSWORD_STRENGTH() |
确定密码强度 |
许多加密和压缩函数返回的字符串可能包含任意字节值。如果您想存储这些结果,请使用具有 VARBINARY
或 BLOB
二进制字符串数据类型的列。这避免了可能的尾随空格删除或字符集转换问题,这些问题可能会更改数据值,例如,如果您使用非二进制字符串数据类型 (CHAR
, VARCHAR
, TEXT
)。
一些加密函数返回 ASCII 字符串:MD5()
, SHA()
, SHA1()
, SHA2()
, STATEMENT_DIGEST()
, STATEMENT_DIGEST_TEXT()
。它们的返回值是一个字符串,其字符集和排序规则由 character_set_connection
和 collation_connection
系统变量确定。这是一个非二进制字符串,除非字符集是 binary
。
如果应用程序存储来自函数的值,例如 MD5()
或 SHA1()
,这些函数返回十六进制数字字符串,可以使用 UNHEX()
将十六进制表示形式转换为二进制,并将结果存储在 BINARY(
列中。每对十六进制数字需要一个字节的二进制形式,因此 N
)N
的值取决于十六进制字符串的长度。N
是 16 对于 MD5()
值,20 对于 SHA1()
值。对于 SHA2()
,N
的值范围从 28 到 32,取决于结果的位长。
将十六进制字符串存储在 CHAR
列中的空间罚款至少是两倍,最高达八倍,如果值存储在使用 utf8mb4
字符集的列中(每个字符使用 4 字节)。存储字符串也会导致比较速度变慢,因为需要考虑字符集排序规则。
假设应用程序在 MD5()
字符串值存储在 CHAR(32)
列中:
CREATE TABLE md5_tbl (md5_val CHAR(32), ...);
INSERT INTO md5_tbl (md5_val, ...) VALUES(MD5('abcdef'), ...);
要将十六进制字符串转换为更紧凑的形式,修改应用程序以使用 UNHEX()
和 BINARY(16)
,如下所示:
CREATE TABLE md5_tbl (md5_val BINARY(16), ...);
INSERT INTO md5_tbl (md5_val, ...) VALUES(UNHEX(MD5('abcdef')), ...);
应用程序应该准备好处理哈希函数产生相同值的非常罕见情况。检测碰撞的一种方法是使哈希列成为主键。
MD5 和 SHA-1 算法的漏洞已经被发现。你可能想考虑使用本节中描述的其他单向加密函数,例如 SHA2()
。
密码或其他敏感值作为加密函数的参数发送到 MySQL 服务器,除非使用 SSL 连接。此外,这些值还出现在 MySQL 日志中。为了避免这种类型的暴露,应用程序可以在客户端加密敏感值,然后将其发送到服务器。同样的考虑也适用于加密密钥。为了避免暴露这些密钥,应用程序可以使用存储过程在服务器端加密和解密值。
-
AES_DECRYPT(
crypt_str
,key_str
[,init_vector
][,kdf_name
][,salt
][,info | iterations
])该函数使用官方的 AES(高级加密标准)算法对数据进行解密。有关更多信息,请参阅
AES_ENCRYPT()
的描述。使用
AES_DECRYPT()
的语句对于基于语句的复制是不安全的。 -
AES_ENCRYPT(
str
,key_str
[,init_vector
][,kdf_name
][,salt
][,info | iterations
])AES_ENCRYPT()
和AES_DECRYPT()
实现了使用官方的 AES(高级加密标准)算法对数据进行加密和解密,之前称为 “Rijndael。” AES 标准允许使用不同的密钥长度。默认情况下,这些函数实现了 128 位密钥长度的 AES。可以使用 196 位或 256 位密钥长度,如后面所述。密钥长度是性能和安全性之间的权衡。AES_ENCRYPT()
使用密钥字符串key_str
加密字符串str
,并返回包含加密输出的二进制字符串。AES_DECRYPT()
使用密钥字符串key_str
解密加密字符串crypt_str
,并返回原始(二进制)字符串的十六进制格式。(要将结果作为纯文本获取,可以将其转换为CHAR
。或者,在启动 mysql 客户端时使用--skip-binary-as-hex
,以便所有二进制值都显示为文本。)如果函数的任何参数为NULL
,则函数返回NULL
。如果AES_DECRYPT()
检测到无效数据或不正确的填充,它将返回NULL
。然而,如果输入数据或密钥无效,AES_DECRYPT()
可能返回非NULL
值(可能是垃圾)。这些函数支持使用密钥派生函数(KDF)从
key_str
中创建加密强密钥。派生密钥用于加密和解密数据,并且它保留在 MySQL 服务器实例中,不可访问用户。使用 KDF 是非常推荐的,因为它提供了比指定自己的预制密钥或使用更简单的方法来派生密钥更好的安全性。这些函数支持 HKDF(从 OpenSSL 1.1.0 开始可用),您可以指定可选的 salt 和上下文特定信息,以便在密钥材料中包含,并且支持 PBKDF2(从 OpenSSL 1.0.2 开始可用),您可以指定可选的 salt 和设置用于生成密钥的迭代次数。AES_ENCRYPT()
和AES_DECRYPT()
允许控制块加密模式。系统变量block_encryption_mode
控制块加密算法的模式。其默认值为aes-128-ecb
,表示使用 128 位密钥长度和 ECB 模式。有关该变量的允许值的描述,请参见 第 7.1.8 节,“服务器系统变量”。可选的init_vector
参数用于为块加密模式提供初始化向量。使用
AES_ENCRYPT()
或AES_DECRYPT()
的语句对于基于语句的复制是不安全的。如果从 mysql 客户端调用
AES_ENCRYPT()
,二进制字符串将以十六进制表示,取决于--binary-as-hex
选项的值。有关该选项的更多信息,请参见 第 6.5.1 节,“mysql — MySQL 命令行客户端”。函数
AES_ENCRYPT()
和AES_DECRYPT()
的参数如下:-
str
-
要加密的字符串,使用密钥字符串
key_str
或从中派生的密钥。字符串可以是任何长度。为了满足块加密算法(如 AES)的要求,字符串将自动添加填充。该填充将被AES_DECRYPT()
函数自动删除。 -
crypt_str
-
要解密的加密字符串,使用密钥字符串
key_str
或从中派生的密钥。字符串可以是任何长度。可以使用以下公式计算crypt_str
的长度:16 * (trunc(string_length / 16) + 1)
-
key_str
-
加密密钥,或者用于派生密钥的输入密钥材料。对于同一数据实例,使用相同的
key_str
值来加密AES_ENCRYPT()
和解密AES_DECRYPT()
。如果您使用 KDF,
key_str
可以是任意信息,例如密码或密码短语。在函数的其他参数中,您可以指定 KDF 名称,然后添加其他选项以提高 KDF 的安全性。当您使用 KDF 时,函数将从
key_str
中派生出一个加密强的密钥,并将其用于加密和解密数据。该派生密钥将保留在 MySQL 服务器实例中,并且对用户不可访问。使用 KDF 是非常推荐的,因为它提供了比指定自己的预制密钥或使用更简单的派生方法更好的安全性。如果您不使用 KDF,对于 128 位密钥长度,最安全的方式是创建一个真正随机的 128 位值,并将其作为二进制值传递。例如:
INSERT INTO t VALUES (1,AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));
可以使用密码短语生成 AES 密钥。例如:
INSERT INTO t VALUES (1,AES_ENCRYPT('text', UNHEX(SHA2('My secret passphrase',512))));
如果您超过了 128 位的最大密钥长度,将返回警告。如果您不使用 KDF,不要直接将密码或密码短语传递给
key_str
,请先对其进行哈希处理。以前的文档版本建议使用前一种方法,但现在不再推荐,因为这里显示的示例更加安全。 -
init_vector
-
初始化向量,用于块加密模式。系统变量
block_encryption_mode
控制模式。对于同一数据实例,使用相同的init_vector
值来加密AES_ENCRYPT()
和解密AES_DECRYPT()
。Note如果您使用 KDF,必须指定初始化向量或空字符串,以访问 KDF 的后续参数。
对于需要初始化向量的模式,必须至少 16 字节长(超过 16 字节的字节将被忽略)。如果
init_vector
缺失,将发生错误。对于不需要初始化向量的模式,将忽略该参数,并生成警告,除非您使用 KDF。默认情况下,
block_encryption_mode
系统变量的值是aes-128-ecb
,或 ECB 模式,不需要初始化向量。其他允许的块加密模式 CBC、CFB1、CFB8、CFB128 和 OFB 都需要初始化向量。可以通过调用
RANDOM_BYTES(16)
生成一个随机字节字符串来用作初始化向量。 -
kdf_name
-
密钥派生函数(KDF)的名称,以从输入的密钥材料
key_str
和其他适当的参数中派生出密钥。可选。对于同一个数据实例,使用相同的
kdf_name
值来加密AES_ENCRYPT()
和解密AES_DECRYPT()
。当您指定kdf_name
时,必须指定init_vector
,使用有效的初始化向量或空字符串,如果加密模式不需要初始化向量。支持以下值:
-
hkdf
-
HKDF,从 OpenSSL 1.1.0 开始可用。HKDF 从密钥材料中提取伪随机密钥,然后将其扩展到其他密钥。使用 HKDF,可以指定可选的盐 (
salt
) 和应用程序详细信息 (info
),以包含在密钥材料中。 -
pbkdf2_hmac
-
PBKDF2,从 OpenSSL 1.0.2 开始可用。PBKDF2 将伪随机函数应用于密钥材料,并重复该过程许多次以生成密钥。使用 PBKDF2,可以指定可选的盐 (
salt
),以包含在密钥材料中,并设置生成密钥所需的迭代次数 (iterations
)。
在这个示例中,指定 HKDF 作为密钥派生函数,并提供了盐和应用程序详细信息。初始化向量的参数也被包括,但为空字符串:
SELECT AES_ENCRYPT('mytext','mykeystring', '', 'hkdf', 'salt', 'info');
在这个示例中,指定 PBKDF2 作为密钥派生函数,提供了盐,并将迭代次数从推荐的最小值加倍:
SELECT AES_ENCRYPT('mytext','mykeystring', '', 'pbkdf2_hmac','salt', '2000');
-
-
salt
-
要传递给密钥派生函数(KDF)的盐。可选。HKDF 和 PBKDF2 都可以使用盐,并且建议使用盐,以帮助防止基于字典的攻击或彩虹表攻击。
盐由随机数据组成,为了安全,必须对每个加密操作使用不同的盐。可以通过调用
RANDOM_BYTES()
生成随机字节字符串来用作盐。这个示例生成了 64 位盐:SET @salt = RANDOM_BYTES(8);
对于同一个数据实例,使用相同的
salt
值来加密AES_ENCRYPT()
和解密AES_DECRYPT()
。盐可以安全地与加密数据一起存储。 -
info
-
HKDF 的应用程序详细信息,以包含在密钥材料中。可选;仅当指定
hkdf
作为 KDF 名称时可用。HKDF 将该信息添加到密钥材料key_str
和盐salt
中,以生成密钥。对于同一个数据实例,使用相同的
info
值来加密AES_ENCRYPT()
和解密AES_DECRYPT()
。 -
iterations
-
PBKDF2 的迭代次数,以生成密钥。可选;仅当指定
pbkdf2_hmac
作为 KDF 名称时可用。较高的计数提供了更好的抵御暴力攻击的能力,因为攻击者需要付出更高的计算成本,但这同样适用于密钥派生过程。默认值为 1000,是 OpenSSL 标准推荐的最小值。对于同一个数据实例,使用相同的
iterations
值来加密AES_ENCRYPT()
和解密AES_DECRYPT()
。
mysql> SET block_encryption_mode = 'aes-256-cbc'; mysql> SET @key_str = SHA2('My secret passphrase',512); mysql> SET @init_vector = RANDOM_BYTES(16); mysql> SET @crypt_str = AES_ENCRYPT('text',@key_str,@init_vector); mysql> SELECT CAST(AES_DECRYPT(@crypt_str,@key_str,@init_vector) AS CHAR); +-------------------------------------------------------------+ | CAST(AES_DECRYPT(@crypt_str,@key_str,@init_vector) AS CHAR) | +-------------------------------------------------------------+ | text | +-------------------------------------------------------------+
-
-
将字符串压缩并返回二进制字符串结果。该函数需要 MySQL 编译时带有压缩库,如
zlib
。否则,返回值始终为NULL
。如果string_to_compress
为NULL
,则返回值也为NULL
。压缩后的字符串可以使用UNCOMPRESS()
解压缩。mysql> SELECT LENGTH(COMPRESS(REPEAT('a',1000))); -> 21 mysql> SELECT LENGTH(COMPRESS('')); -> 0 mysql> SELECT LENGTH(COMPRESS('a')); -> 13 mysql> SELECT LENGTH(COMPRESS(REPEAT('a',16))); -> 15
压缩后的字符串内容存储方式如下:
如果
COMPRESS()
从mysql客户端调用,二进制字符串将以十六进制表示,取决于--binary-as-hex
选项的值。有关该选项的更多信息,请参见第6.5.1节,“mysql — MySQL命令行客户端”。 -
计算字符串的MD5 128位校验和。返回值是一个32个十六进制数字的字符串,或者如果参数为空,则返回
NULL
。返回值可以用作哈希键。请参见本节开头关于存储哈希值的注意事项。返回值是一个连接字符集中的字符串。
如果启用了FIPS模式,
MD5()
返回NULL
。请参见第8.8节,“FIPS支持”。mysql> SELECT MD5('testing'); -> 'ae2b1fca515949e5d54fb22b8ed95575'
这是“RSA Data Security, Inc. MD5 Message-Digest Algorithm.”
请参见本节开头关于MD5算法的注意事项。
-
该函数返回一个长度为
len
的随机二进制字符串,使用SSL库的随机数生成器生成。允许的len
值范围从1到1024。如果len
超出该范围,将发生错误。如果len
为空,则返回NULL
。RANDOM_BYTES()
可以用来提供AES_DECRYPT()
和AES_ENCRYPT()
函数的初始化向量。在该上下文中,len
必须至少为16。允许更大的值,但超过16的字节将被忽略。RANDOM_BYTES()
生成一个随机值,因此其结果是非确定性的。因此,使用该函数的语句对于基于语句的复制是不安全的。如果
RANDOM_BYTES()
从mysql客户端调用,二进制字符串将以十六进制表示,取决于--binary-as-hex
选项的值。有关该选项的更多信息,请参见第6.5.1节,“mysql — MySQL命令行客户端”。 -
计算字符串的SHA-1 160位校验和,如RFC 3174(Secure Hash Algorithm)所述。返回值是一个40个十六进制数字的字符串,或者如果参数为空,则返回
NULL
。该函数的一个可能用途是作为哈希键。请参见本节开头关于存储哈希值的注意事项。SHA()
是SHA1()
的同义词。返回值是一个连接字符集中的字符串。
mysql> SELECT SHA1('abc'); -> 'a9993e364706816aba3e25717850c26c9cd0d89d'
-
计算SHA-2哈希函数家族(SHA-224、SHA-256、SHA-384和SHA-512)。第一个参数是要哈希的明文字符串。第二个参数指示结果的所需位长,必须是224、256、384、512或0(相当于256)。如果任一参数是
NULL
或哈希长度不是允许的值,则返回值是NULL
。否则,函数结果是一个包含所需位数的哈希值。请参阅本节开头关于高效存储哈希值的注意事项。返回值是一个字符串,在连接字符集中。
mysql> SELECT SHA2('abc', 224); -> '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'
该函数仅在MySQL配置了SSL支持时有效。请参阅第8.3节,“使用加密连接”。
-
给定一个SQL语句作为字符串,返回语句摘要哈希值作为连接字符集中的字符串,或者如果参数是
NULL
,则返回NULL
。相关的STATEMENT_DIGEST_TEXT()
函数返回标准化的语句摘要。关于语句摘要的信息,请参阅第29.10节,“性能架构语句摘要和采样”。这两个函数使用MySQL解析器来解析语句。如果解析失败,将发生错误。错误消息仅在语句作为文字字符串提供时包括解析错误。
系统变量
max_digest_length
确定了这些函数用于计算标准化语句摘要的最大字节数。mysql> SET @stmt = 'SELECT * FROM mytable WHERE cola = 10 AND colb = 20'; mysql> SELECT STATEMENT_DIGEST(@stmt); +------------------------------------------------------------------+ | STATEMENT_DIGEST(@stmt) | +------------------------------------------------------------------+ | 3bb95eeade896657c4526e74ff2a2862039d0a0fe8a9e7155b5fe492cbd78387 | +------------------------------------------------------------------+ mysql> SELECT STATEMENT_DIGEST_TEXT(@stmt); +----------------------------------------------------------+ | STATEMENT_DIGEST_TEXT(@stmt) | +----------------------------------------------------------+ | SELECT * FROM `mytable` WHERE `cola` = ? AND `colb` = ? | +----------------------------------------------------------+
-
STATEMENT_DIGEST_TEXT(
statement
)给定一个SQL语句作为字符串,返回标准化的语句摘要作为连接字符集中的字符串,或者如果参数是
NULL
,则返回NULL
。关于更多讨论和示例,请参阅相关的STATEMENT_DIGEST()
函数的描述。 -
UNCOMPRESS(
string_to_uncompress
)解压缩由
COMPRESS()
函数压缩的字符串。如果参数不是压缩值,结果是NULL
;如果string_to_uncompress
是NULL
,结果也是NULL
。该函数需要MySQL使用压缩库(如zlib
)编译。否则,返回值始终是NULL
。mysql> SELECT UNCOMPRESS(COMPRESS('any string')); -> 'any string' mysql> SELECT UNCOMPRESS('any string'); -> NULL
-
UNCOMPRESSED_LENGTH(
compressed_string
)返回压缩字符串之前的长度。如果
compressed_string
是NULL
,则返回NULL
。mysql> SELECT UNCOMPRESSED_LENGTH(COMPRESS(REPEAT('a',30))); -> 30
-
VALIDATE_PASSWORD_STRENGTH(
str
)给定一个明文密码字符串,返回一个整数以指示密码的强度,或者如果参数是
NULL
,则返回NULL
。返回值范围从0(弱)到100(强)。密码评估由
VALIDATE_PASSWORD_STRENGTH()
函数执行,由validate_password
组件实现。如果该组件未安装,函数总是返回0。关于安装validate_password
的信息,请参阅第8.4.3节,“密码验证组件”。要检查或设置影响密码测试的参数,请检查或设置validate_password
系统变量。请参阅第8.4.3.2节,“密码验证选项和变量”。密码将受到逐渐严格的测试,返回值反映了哪些测试被满足,如下表所示。此外,如果
validate_password.check_user_name
系统变量启用并且密码匹配用户名,VALIDATE_PASSWORD_STRENGTH()
函数返回0,不管其他validate_password
系统变量如何设置。Password Test Return Value 长度 < 4 0 长度 ≥ 4 and < validate_password.length
25 满足策略 1 ( 低
)50 满足策略 2 ( 中等
)75 满足策略 3 ( 强
)100