Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  Functions and Operators  /  Encryption and Compression Functions

14.13 加密和压缩函数

表 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() 计算 normalize 语句摘要
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取决于十六进制字符串的长度。NMD5()值时为16,SHA1()值时为20,对于SHA2()N取决于指定结果的位长。

将十六进制字符串存储在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')), ...);

应用程序应该准备好处理哈希函数对两个不同输入值产生相同值的极其罕见情况。使哈希列为主键是一种检测碰撞的方法。

Note

MD5和SHA-1算法已知有漏洞。你可能想使用本节中描述的另一种单向加密函数,例如SHA2()

Caution

除非使用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 位密钥长度。可以使用 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 开始),您可以指定可选的盐和上下文相关信息来包含在加密材料中,支持 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 — The MySQL Command-Line Client”.

    AES_ENCRYPT()和AES_DECRYPT()函数的参数如下:

    str

    使用密钥字符串key_str或由指定KDF派生的密钥对AES_ENCRYPT()进行加密的字符串,可以是任意长度。为了满足基于块的算法如AES所需,自动添加了填充到str使其为块的倍数。这个填充由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时,函数从key_str和其他参数中的盐或额外信息派生出一个cryptographically强大的秘密密钥,然后用这个密钥来加密和解密数据,该密钥将在MySQL Server实例中保留,不可访问用户。使用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系统变量控制该模式。对于同一实例数据,使用AES_ENCRYPT()AES_DECRYPT()加密和解密时,使用相同的init_vector值。

    Note

    如果您使用KDF,必须指定初始化向量或空字符串,以访问后续参数来定义KDF。

    对于需要初始化向量的模式,它必须是16个字节以上(超过16个字节将被忽略)。如果init_vector缺失,会出现错误。对于不需要初始化向量的模式,如果指定了init_vector

    系统变量block_encryption_mode 的默认值是 aes-128-ecb,即 ECB 模式,不需要初始化向量。所有其他的块加密模式 CBC、CFB1、CFB8 和 OFB 都需要初始化向量。

    可以通过调用RANDOM_BYTES(16)生成一个随机字节串来用作初始化向量。

    kdf_name

    用于从输入密钥材料key_str创建密钥的名称,其他参数根据 KDF 适当。可选项。

    对同一实例数据使用AES_ENCRYPT()加密和AES_DECRYPT()解密时,必须使用相同的kdf_name值,并且可以指定有效的初始化向量或在不需要初始化向量的情况下指定 null 字符串。

    支持以下值:

    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()进行解密,使用的盐值可以与加密数据一起存储。

    info

    HKDF 关键材料中包含的上下文相关信息,例如应用程序信息。可选;当您指定 hkdf 作为 KDF 名称时可用。HKDF 将在 key_str 中指定的关键材料和 salt 中指定的盐中添加这些信息以产生键。

    对于同一实例数据,使用相同的 info 值进行加密和解密,以AES_ENCRYPT()AES_DECRYPT()

    iterations

    PBKDF2 的迭代次数,用于产生键。可选;当您指定 pbkdf2_hmac 作为 KDF 名称时可用。更高的次数使攻击者对 brute-force 攻击更加困难,但同样也增加了关键派生过程的计算成本。默认情况下,如果不指定该参数,则使用 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                                                        |
    +-------------------------------------------------------------+
  • COMPRESS(string_to_compress)

    将字符串压缩并返回结果作为二进制字符串。这個函数需要MySQL编译时使用压缩库,例如zlib。否则,返回值总是NULL。返回值也为NULL如果string_to_compressNULL。压缩后的字符串可以使用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 数据安全,Inc. MD5 消息摘要算法。”

    请参阅本节开始处关于 MD5 算法的注意事项。

  • RANDOM_BYTES(len)

    该函数返回使用 SSL 库随机数生成器生成的长度为 len 的二进制字符串。允许的 len 值范围从 1 到 1024。如果超出该范围,发生错误。如果 lenNULL,则返回 NULL

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

    RANDOM_BYTES() 生成一个随机值,因此其结果是非确定性的。因此,使用该函数的语句不安全用于语句级别复制。

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

  • SHA1(str), SHA(str)

    计算字符串的 SHA-1 160 位校验和,见 RFC 3174(安全哈希算法)。返回值是一个 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'

    这个函数只有在 MySQL 配置了 SSL 支持时才能工作。见第 8.3 节,“使用加密连接”

    SHA2() 可以被认为是 MD5()SHA1() 的加密更安全的等价物。

  • STATEMENT_DIGEST(statement)

    将一个SQL语句字符串作为参数,返回该语句的哈希值字符串,以连接字符集表示,如果参数为NULL,则返回NULL。相关函数STATEMENT_DIGEST_TEXT()返回normalized语句哈希值。关于语句摘要的信息,请参阅第29.10节,“性能_schema_语句摘要和采样”

    这两个函数使用MySQL解析器来解析语句。如果解析失败,发生错误。错误信息仅在将语句作为文字字符串提供时包含解析错误。

    max_digest_length系统变量确定这些函数用于计算normalized语句摘要的最大字节数。

    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语句字符串作为参数,返回该语句的normalized哈希值字符串,以连接字符集表示,如果参数为NULL,则返回NULL。关于相关函数STATEMENT_DIGEST()的讨论和示例,请参阅该函数的描述。

  • UNCOMPRESS(<em class="replaceable"><code>string_to_uncompress</code></em>)

    解压一个由COMPRESS()函数压缩的字符串。如果参数不是压缩值,结果是 NULL;如果<em class="replaceable"><code>string_to_uncompress</code></em> 是 NULL,结果也是 NULL。这个函数需要 MySQL 编译时使用压缩库,如 zlib。否则,返回值总是 NULL

    mysql> SELECT UNCOMPRESS(COMPRESS('any string'));
            -> 'any string'
    mysql> SELECT UNCOMPRESS('any string');
            -> NULL
  • UNCOMPRESSED_LENGTH(<em class="replaceable"><code>compressed_string</code></em>)

    返回压缩字符串被压缩前所在的长度。如果<em class="replaceable"><code>compressed_string</code></em> 是 NULL,返回 NULL

    mysql> SELECT UNCOMPRESSED_LENGTH(COMPRESS(REPEAT('a',30)));
            -> 30
  • VALIDATE_PASSWORD_STRENGTH(<em class="replaceable"><code>str</code></em>)

    给定一个表示明文密码的参数,这个函数返回一个整数,表示密码强度,或者如果参数是 NULL,返回 NULL。返回值从 0 (弱) 到 100 (强)。

    密码由 VALIDATE_PASSWORD_STRENGTH() 组件进行评估。如果该组件未安装,函数总是返回 0。关于安装 validate_password 的信息,请参阅第8.4.3节,“密码验证组件”。要检查或配置影响密码测试的参数,查看或设置由 validate_password 实现的系统变量。见第8.4.3.2节,“密码验证选项和变量”

    密码将被逐渐加强的测试,并且返回值反映了哪些测试通过,如下表所示。另外,如果validate_password.check_user_name 系统变量启用且密码匹配用户名,VALIDATE_PASSWORD_STRENGTH() 无论其他 validate_password 系统变量设置如何都返回 0。

    Password Test Return Value
    长度<4 0
    长度≥4且<validate_password.length 25
    满足策略 1 (LOW) 50
    满足策略 2 (MEDIUM) 75
    满足策略3(STRONG 100