文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9MB
PDF (A4) - 40.0MB
手册页 (TGZ) - 258.5KB
手册页 (Zip) - 365.5KB
信息 (Gzip) - 4.0MB
信息 (Zip) - 4.0MB


MySQL 8.4 参考手册  /  函数和运算符  /  加密和压缩函数

14.13 加密和压缩函数

表 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() 确定密码强度

许多加密和压缩函数返回的字符串结果可能包含任意字节值。如果要存储这些结果,请使用具有 VARBINARYBLOB 二进制字符串数据类型的列。这可以避免由于尾随空格删除或字符集转换而导致数据值发生更改的潜在问题,例如,如果使用非二进制字符串数据类型(CHARVARCHARTEXT)时可能会出现这种情况。

一些加密函数返回 ASCII 字符串:MD5()SHA()SHA1()SHA2()STATEMENT_DIGEST()STATEMENT_DIGEST_TEXT()。它们的返回值是由 character_set_connectioncollation_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()

警告

除非使用 SSL 连接,作为参数提供给加密函数的密码或其他敏感值将以明文形式发送到 MySQL 服务器。此外,此类值会出现在写入它们的任何 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 服务器实例中,用户无法访问。强烈建议使用 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 服务器实例中,用户无法访问。强烈建议使用 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                                                        |
    +-------------------------------------------------------------+
  • COMPRESS(string_to_compress)

    压缩字符串并将结果作为二进制字符串返回。此函数要求使用压缩库(如 zlib)编译 MySQL。否则,返回值始终为 NULL。如果 string_to_compressNULL,则返回值也为 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

    压缩后的字符串内容按以下方式存储

    • 空字符串存储为空字符串。

    • 非空字符串存储为未压缩字符串的 4 字节长度(低字节优先),后跟压缩后的字符串。如果字符串以空格结尾,则会添加一个额外的 . 字符,以避免在将结果存储在 CHARVARCHAR 列中时出现尾随空格修剪问题。(但是,无论如何都不建议使用非二进制字符串数据类型(如 CHARVARCHAR)来存储压缩后的字符串,因为可能会发生字符集转换。请改用 VARBINARYBLOB 二进制字符串列。)

    如果从 mysql 客户端内调用 COMPRESS(),则二进制字符串将使用十六进制表示法显示,具体取决于 --binary-as-hex 的值。有关该选项的更多信息,请参阅第 6.5.1 节“mysql - MySQL 命令行客户端”

  • MD5(str)

    计算字符串的 MD5 128 位校验和。该值将作为包含 32 个十六进制数字的字符串返回,如果参数为 NULL,则返回 NULL。例如,返回值可以用作哈希键。请参阅本节开头有关高效存储哈希值的注释。

    返回值是连接字符集中的字符串。

    如果启用了 FIPS 模式,则 MD5() 返回 NULL。请参阅第 8.8 节“FIPS 支持”

    mysql> SELECT MD5('testing');
            -> 'ae2b1fca515949e5d54fb22b8ed95575'

    这是RSA Data Security, Inc. MD5 消息摘要算法。

    请参阅本节开头有关 MD5 算法的注释。

  • RANDOM_BYTES(len)

    此函数返回一个二进制字符串,其中包含使用 SSL 库的随机数生成器生成的 len 个随机字节。len 的允许值范围为 1 到 1024。对于超出该范围的值,将发生错误。如果 lenNULL,则返回 NULL

    RANDOM_BYTES() 可用于为 AES_DECRYPT()AES_ENCRYPT() 函数提供初始化向量。为了在该上下文中使用,len 必须至少为 16。允许使用更大的值,但超过 16 的字节将被忽略。

    RANDOM_BYTES() 生成一个随机值,这使得其结果不确定。因此,使用此函数的语句对于基于语句的复制是不安全的。

    如果从 mysql 客户端内调用 RANDOM_BYTES(),则二进制字符串将使用十六进制表示法显示,具体取决于 --binary-as-hex 的值。有关该选项的更多信息,请参阅第 6.5.1 节“mysql - MySQL 命令行客户端”

  • SHA1(str), SHA(str)

    如 RFC 3174(安全哈希算法)中所述,计算字符串的 SHA-1 160 位校验和。该值将作为包含 40 个十六进制数字的字符串返回,如果参数为 NULL,则返回 NULL。此函数的可能用途之一是作为哈希键。请参阅本节开头有关高效存储哈希值的注释。 SHA()SHA1() 的同义词。

    返回值是连接字符集中的字符串。

    mysql> SELECT SHA1('abc');
            -> 'a9993e364706816aba3e25717850c26c9cd0d89d'

    SHA1() 可以被认为是 MD5() 的加密更安全的等效函数。但是,请参阅本节开头有关 MD5 和 SHA-1 算法的注释。

  • SHA2(str, hash_length)

    计算 SHA-2 系列哈希函数(SHA-224、SHA-256、SHA-384 和 SHA-512)。第一个参数是要进行哈希处理的明文字符串。第二个参数指示结果所需的位长,其值必须为 224、256、384、512 或 0(等效于 256)。如果任一参数为 NULL 或哈希长度不是允许的值之一,则返回值为 NULL。否则,函数结果是一个包含所需位数的哈希值。请参阅本节开头有关高效存储哈希值的注释。

    返回值是连接字符集中的字符串。

    mysql> SELECT SHA2('abc', 224);
            -> '23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'

    仅当使用 SSL 支持配置了 MySQL 时,此函数才有效。请参阅第 8.3 节“使用加密连接”

    SHA2() 可以被认为比 MD5()SHA1() 的加密更安全。

  • STATEMENT_DIGEST(statement)

    给定一个字符串形式的 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_uncompressNULL,则结果也为 NULL。此函数要求使用压缩库(如 zlib)编译 MySQL。否则,返回值始终为 NULL

    mysql> SELECT UNCOMPRESS(COMPRESS('any string'));
            -> 'any string'
    mysql> SELECT UNCOMPRESS('any string');
            -> NULL
  • UNCOMPRESSED_LENGTH(compressed_string)

    返回压缩字符串在压缩前的长度。如果 compressed_stringNULL,则返回 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