表 14.18 加密函数
名称 | 描述 |
---|---|
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
的值取决于十六进制字符串的长度。对于 MD5()
值,N
为 16,对于 SHA1()
值,N
为 20。对于 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
。或者,使用--skip-binary-as-hex
启动 mysql 客户端,使所有二进制值都以文本形式显示。)如果任何函数参数为NULL
,则函数返回NULL
。如果AES_DECRYPT()
检测到无效数据或不正确的填充,则返回NULL
。但是,如果输入数据或密钥无效,则AES_DECRYPT()
可能返回非NULL
值(可能是垃圾数据)。这些函数支持使用密钥派生函数 (KDF) 从
key_str
中传递的信息创建加密强密钥。派生的密钥用于加密和解密数据,它保留在 MySQL Server 实例中,用户无法访问。强烈建议使用 KDF,因为它比在使用函数时指定您自己的预制密钥或通过更简单的方法派生密钥提供更好的安全性。这些函数支持 HKDF(可从 OpenSSL 1.1.0 获取),您可以为其指定可选的盐和特定于上下文的其他信息以包含在密钥材料中,以及 PBKDF2(可从 OpenSSL 1.0.2 获取),您可以为其指定可选的盐并设置用于生成密钥的迭代次数。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
用于
AES_ENCRYPT()
的字符串,使用密钥字符串key_str
或通过指定的 KDF 从中派生的密钥进行加密。字符串可以是任意长度。str
会自动添加填充,使其成为块的倍数,这是基于块的算法(如 AES)所要求的。此填充会由AES_DECRYPT()
函数自动删除。-
crypt_str
用于
AES_DECRYPT()
的加密字符串,使用密钥字符串key_str
或通过指定的 KDF 从中派生的密钥进行解密。字符串可以是任意长度。crypt_str
的长度可以使用以下公式从原始字符串的长度计算出来16 * (trunc(string_length / 16) + 1)
-
key_str
加密密钥或用作使用密钥派生函数 (KDF) 派生密钥的基础的输入密钥材料。对于相同的数据实例,在使用
AES_ENCRYPT()
进行加密和使用AES_DECRYPT()
进行解密时,使用相同的值key_str
。如果您使用的是 KDF,则
key_str
可以是任何任意信息,例如密码或密码短语。在函数的后续参数中,您指定 KDF 名称,然后根据 KDF 的情况添加其他选项以提高安全性。当您使用 KDF 时,函数会从
key_str
中传递的信息以及您在其他参数中提供的任何盐或其他信息中创建加密强密钥。派生的密钥用于加密和解密数据,它保留在 MySQL Server 实例中,用户无法访问。强烈建议使用 KDF,因为它比在使用函数时指定您自己的预制密钥或通过更简单的方法派生密钥提供更好的安全性。如果您不使用 KDF,对于 128 位密钥长度,将密钥传递给
key_str
参数的最安全方法是创建一个真正的随机 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
系统变量控制模式。对于相同的数据实例,在使用AES_ENCRYPT()
进行加密和使用AES_DECRYPT()
进行解密时,使用相同的值init_vector
。注意如果您使用的是 KDF,则必须为该参数指定初始化向量或空字符串,以便访问后续参数以定义 KDF。
对于需要初始化向量的模式,它必须为 16 字节或更长(超过 16 字节的字节将被忽略)。如果缺少
init_vector
,则会发生错误。对于不需要初始化向量的模式,它将被忽略,并且如果指定了init_vector
,则会生成警告,除非您使用的是 KDF。block_encryption_mode
系统变量的默认值为aes-128-ecb
或 ECB 模式,它不需要初始化向量。允许的替代块加密模式 CBC、CFB1、CFB8、CFB128 和 OFB 都需要初始化向量。可以通过调用
RANDOM_BYTES(16)
生成用于初始化向量的随机字节字符串。-
kdf_name
密钥派生函数 (KDF) 的名称,用于从
key_str
中传递的输入密钥材料以及根据 KDF 的情况提供的其他参数创建密钥。可选。对于相同的数据实例,在使用
AES_ENCRYPT()
进行加密和使用AES_DECRYPT()
进行解密时,使用相同的值kdf_name
。当您指定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);
对于相同的数据实例,在使用
AES_ENCRYPT()
进行加密和使用AES_DECRYPT()
进行解密时,使用相同的值salt
。盐可以安全地与加密数据一起存储。-
info
用于 HKDF 的特定于上下文的其他信息,以包含在密钥材料中,例如有关应用程序的信息。可选;当您指定
hkdf
作为 KDF 名称时可用。HKDF 会将此信息添加到key_str
中指定的密钥材料和salt
中指定的盐中,以生成密钥。对于相同的数据实例,在使用
AES_ENCRYPT()
进行加密和使用AES_DECRYPT()
进行解密时,使用相同的值info
。-
iterations
用于生成密钥的 PBKDF2 迭代次数。可选;当您指定
pbkdf2_hmac
作为 KDF 名称时可用。更高的次数可以提高对暴力攻击的抵抗力,因为它对攻击者具有更高的计算成本,但密钥推导过程也必然如此。如果您没有指定此参数,默认值为 1000,这是 OpenSSL 标准推荐的最小值。对于相同的数据实例,请对使用
AES_ENCRYPT()
加密和使用AES_DECRYPT()
解密时使用相同的iterations
值。
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
压缩后的字符串内容以以下方式存储
如果从 mysql 客户端中调用
COMPRESS()
,二进制字符串将使用十六进制表示法显示,具体取决于--binary-as-hex
的值。有关该选项的更多信息,请参见 第 6.5.1 节,“mysql — MySQL 命令行客户端”。计算字符串的 MD5 128 位校验和。该值将作为 32 个十六进制数字的字符串返回,或者如果参数为
NULL
,则返回NULL
。返回值可以例如用作哈希键。有关有效存储哈希值的说明,请参见本节开头的说明。返回值是连接字符集中的字符串。
如果启用了 FIPS 模式,则
MD5()
返回NULL
。请参见 第 8.8 节,“FIPS 支持”。mysql> SELECT MD5('testing'); -> 'ae2b1fca515949e5d54fb22b8ed95575'
这是 “RSA Data Security, Inc. MD5 消息摘要算法。”
请参见本节开头有关 MD5 算法的说明。
此函数返回一个二进制字符串,其中包含使用 SSL 库的随机数生成器生成的
len
个随机字节。允许的len
值范围为 1 到 1024。对于超出该范围的值,将发生错误。如果len
为NULL
,则返回NULL
。RANDOM_BYTES()
可用于为AES_DECRYPT()
和AES_ENCRYPT()
函数提供初始化向量。在该上下文中使用时,len
必须至少为 16。允许更大的值,但超过 16 的字节将被忽略。RANDOM_BYTES()
生成一个随机值,这使得其结果不确定。因此,使用此函数的语句对于基于语句的复制来说是不安全的。如果从 mysql 客户端中调用
RANDOM_BYTES()
,二进制字符串将使用十六进制表示法显示,具体取决于--binary-as-hex
的值。有关该选项的更多信息,请参见 第 6.5.1 节,“mysql — MySQL 命令行客户端”。计算字符串的 SHA-1 160 位校验和,如 RFC 3174(安全哈希算法)中所述。该值将作为 40 个十六进制数字的字符串返回,或者如果参数为
NULL
,则返回NULL
。此函数的可能用途之一是作为哈希键。有关有效存储哈希值的说明,请参见本节开头的说明。SHA()
与SHA1()
同义。返回值是连接字符集中的字符串。
mysql> SELECT SHA1('abc'); -> 'a9993e364706816aba3e25717850c26c9cd0d89d'
SHA1()
可以被认为是MD5()
的密码学上更安全的等效项。但是,请参见本节开头有关 MD5 和 SHA-1 算法的说明。计算 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
系统变量如何设置。密码测试 返回值 长度 < 4 0 长度 ≥ 4 且 < validate_password.length
25 满足策略 1 ( LOW
)50 满足策略 2 ( MEDIUM
)75 满足策略 3 ( STRONG
)100