扩展 MySQL 8.0  / 第 6 章 向 MySQL 添加函数  /  12.14 加密和压缩函数

12.14 加密和压缩函数

表 12.18 加密函数

姓名 描述 介绍 弃用
AES_DECRYPT() 使用 AES 解密
AES_ENCRYPT() 使用 AES 加密
COMPRESS() 以二进制字符串形式返回结果
DECODE() 解码使用 ENCODE() 加密的字符串
DES_DECRYPT() 解密字符串
DES_ENCRYPT() 加密字符串
ENCODE() 编码一个字符串
ENCRYPT() 加密字符串
MD5() 计算MD5校验和
OLD_PASSWORD() 返回 4.1 之前实现的 PASSWORD 的值 是的
PASSWORD() 计算并返回密码字符串
RANDOM_BYTES() 返回一个随机字节向量 5.6.17
SHA1(),SHA() 计算 SHA-1 160 位校验和
SHA2() 计算 SHA-2 校验和
UNCOMPRESS() 解压缩压缩的字符串
UNCOMPRESSED_LENGTH() 返回压缩前字符串的长度
VALIDATE_PASSWORD_STRENGTH() 确定密码强度

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

一些加密函数返回 ASCII 字符的字符串: MD5(), OLD_PASSWORD(), PASSWORD(), SHA(), SHA1(), SHA2()character_set_connection它们的返回值是一个字符串,该字符串具有由和 collation_connection系统变量确定的字符集和排序规则 。这是一个非二进制字符串,除非字符集是 binary.

如果应用程序存储来自返回十六进制数字字符串的函数的值, MD5()SHA1()可以通过将十六进制表示形式转换为二进制 UNHEX()并将结果存储在 列中来获得更有效的存储和比较。每对十六进制数字需要二进制形式的一个字节,因此 的值取决于十六进制字符串的长度。一个值是 16,一个值是 20 。对于 , 范围从 28 到 32,具体取决于指定结果所需位长度的参数。 BINARY(N)NNMD5()SHA1()SHA2()N

将十六进制字符串存储在列中的大小损失 至少是两倍,如果值存储在使用字符集(每个字符使用 4 个字节)CHAR的列中,则高达八倍 。utf8存储字符串还会导致比较速度变慢,因为值较大并且需要考虑字符集排序规则。

假设应用程序将 MD5()字符串值存储在 CHAR(32)列中:

CREATE TABLE md5_tbl (md5_val CHAR(32), ...);
INSERT INTO md5_tbl (md5_val, ...) VALUES(MD5('abcdef'), ...);

要将十六进制字符串转换为更紧凑的形式,请修改应用程序以使用UNHEX()and 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])

    此函数使用官方 AES(高级加密标准)算法解密数据。有关详细信息,请参阅 的说明AES_ENCRYPT()

    可选的初始化向量参数 , init_vector从 MySQL 5.6.17 开始可用。从那个版本开始,使用 AES_DECRYPT()的语句对于基于语句的复制是不安全的,并且不能存储在查询缓存中。

  • AES_ENCRYPT(str,key_str[,init_vector])

    AES_ENCRYPT()AES_DECRYPT()使用官方的 AES(高级加密标准)算法(以前称为 Rijndael”)实现数据的加密和解密。 AES 标准允许各种密钥长度。默认情况下,这些函数使用 128 位密钥长度实现 AES。从 MySQL 5.6.17 开始,可以使用 196 或 256 位的密钥长度,如后所述。密钥长度是性能和安全性之间的权衡。

    AES_ENCRYPT()str使用密钥字符串 加密字符串key_str并返回包含加密输出的二进制字符串。 使用密钥字符串AES_DECRYPT()解密加密字符串并返回原始明文字符串。如果任一函数参数是 ,则函数返回 。 crypt_strkey_strNULLNULL

    和参数可以是任意长度,并且会自动添加填充, str因此 它是 AES 等基于块的算法所要求的块的倍数。此填充由 函数自动删除。的长度可以使用以下公式计算: crypt_strstrAES_DECRYPT()crypt_str

    16 * (trunc(string_length / 16) + 1)

    对于 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))));

    不要将密码或密码直接传递给 crypt_str,首先对其进行哈希处理。本文档的早期版本建议采用前一种方法,但不再推荐,因为此处显示的示例更安全。

    如果AES_DECRYPT()检测到无效数据或不正确的填充,则返回 NULL. 但是, 如果输入数据或密钥无效 ,则有可能AES_DECRYPT()返回非值(可能是垃圾)。NULL

    从 MySQL 5.6.17 开始, AES_ENCRYPT()允许 AES_DECRYPT()控制块加密模式并采用可选的 init_vector初始化向量参数:

    • 系统变量控制基于块的加密算法的block_encryption_mode 模式。它的默认值为 aes-128-ecb,表示使用 128 位密钥长度和 ECB 模式进行加密。有关此变量的允许值的说明,请参阅 第 5.1.7 节,“服务器系统变量”

    • 可选init_vector 参数为需要它的块加密模式提供初始化向量。

    对于需要可选 init_vector参数的模式,它必须为 16 字节或更长(超过 16 的字节将被忽略)。init_vector如果缺少, 则会发生错误。

    对于不需要的模式 init_vector,它会被忽略,如果指定,则会生成警告。

    用于初始化向量的随机字节串可以通过调用 RANDOM_BYTES(16). 对于需要初始化向量的加密模式,加密和解密必须使用相同的向量。

    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 AES_DECRYPT(@crypt_str,@key_str,@init_vector);
    +-----------------------------------------------+
    | AES_DECRYPT(@crypt_str,@key_str,@init_vector) |
    +-----------------------------------------------+
    | text                                          |
    +-----------------------------------------------+

    下表列出了每种允许的块加密模式、支持它的 SSL 库以及是否需要初始化向量参数。

    块加密模式 支持模式的 SSL 库 需要初始化向量
    欧洲央行 OpenSSL, yaSSL
    加拿大广播公司 OpenSSL, yaSSL 是的
    循环流化床1 打开SSL 是的
    循环流化床8 打开SSL 是的
    CFB128 打开SSL 是的
    OFB 打开SSL 是的

    从 MySQL 5.6.17 开始,使用 AES_ENCRYPT()AES_DECRYPT()对基于语句的复制不安全的语句不能存储在查询缓存中。

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

  • COMPRESS(string_to_compress)

    压缩字符串并将结果作为二进制字符串返回。此功能要求 MySQL 编译时带有压缩库,例如zlib. 否则,返回值始终为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 字节长度(低字节在前),然后是压缩字符串。如果字符串以空格结尾,则添加一个额外的 字符以避免在结果存储在或 列.中时出现尾空格修剪问题 。(但是,无论如何不建议 使用非二进制字符串数据类型(例如 or 来存储压缩字符串,因为可能会发生字符集转换。请改用or 二进制字符串列。) CHARVARCHARCHARVARCHARVARBINARYBLOB

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

  • DECODE(crypt_str,pass_str)

    crypt_str使用 pass_stras 密码 解密加密的字符串 。crypt_str应该是从返回的字符串ENCODE()

  • DES_DECRYPT(crypt_str[,key_str])

    解密用 加密的字符串 DES_ENCRYPT()。如果发生错误,该函数返回NULL

    此功能仅在 MySQL 已配置为支持 SSL 时才有效。请参阅第 6.3 节,“使用加密连接”

    如果未key_str给出参数,则 DES_DECRYPT()检查加密字符串的第一个字节以确定用于加密原始字符串的 DES 密钥编号,然后从 DES 密钥文件中读取密钥以解密消息。为此,用户必须具有 SUPER特权。可以使用 --des-key-file服务器选项指定密钥文件。

    如果您向此函数传递一个key_str 参数,则该字符串将用作解密消息的密钥。

    如果crypt_str参数不是加密字符串,MySQL 返回给定的 crypt_str.

  • DES_ENCRYPT(str[,{key_num|key_str}])

    使用 Triple-DES 算法使用给定的密钥加密字符串。

    此功能仅在 MySQL 已配置为支持 SSL 时才有效。请参阅第 6.3 节,“使用加密连接”

    要使用的加密密钥是根据给定的第二个参数选择的DES_ENCRYPT()。如果没有参数,则使用 DES 密钥文件中的第一个密钥。使用key_num 参数时,将使用 DES 密钥文件中给定的密钥编号(0 到 9)。使用key_str参数,给定的密钥字符串用于加密 str

    可以使用 --des-key-file服务器选项指定密钥文件。

    返回字符串是一个二进制字符串,其中第一个字符是。如果发生错误,则返回 。 CHAR(128 | key_num)DES_ENCRYPT()NULL

    添加 128 是为了更容易识别加密密钥。如果使用字符串键, key_num则为 127。

    结果的字符串长度由以下公式给出:

    new_len = orig_len + (8 - (orig_len % 8)) + 1

    DES 密钥文件中的每一行都具有以下格式:

    key_num des_key_str

    每个key_num值必须是从0到 范围内的数字9。文件中的行可以是任何顺序。 des_key_str是用于加密消息的字符串。数字和密钥之间至少应有一个空格。如果您没有为 指定任何键参数,则第一个键是使用的默认键DES_ENCRYPT()

    FLUSH DES_KEY_FILE 您可以使用语句 告诉 MySQL 从密钥文件中读取新的密钥值。这需要 RELOAD特权。

    拥有一组默认密钥的一个好处是,它为应用程序提供了一种检查加密列值是否存在的方法,而无需为最终用户提供解密这些值的权利。

    mysql> SELECT customer_address FROM customer_table 
        -> WHERE crypted_credit_card = DES_ENCRYPT('credit_card_number');

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

  • ENCODE(str,pass_str)

    str使用 pass_str作为密码进行 加密。结果是与 长度相同的二进制字符串 str。要解密结果,请使用 DECODE().

    ENCODE()不应再使用 该功能。如果您仍然需要使用 ENCODE(),则必须与它一起使用 salt 值以降低风险。例如:

    ENCODE('cleartext', CONCAT('my_random_salt','my_secret_password'))

    每当更新密码时,都必须使用新的随机盐值。

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

  • ENCRYPT(str[,salt])

    str使用 Unix crypt()系统调用 加密并返回二进制字符串。salt参数必须是至少包含两个字符的字符串,否则结果 为NULL。如果没有salt 给出参数,则使用随机值。

    mysql> SELECT ENCRYPT('hello');
            -> 'VxuFAJXVARROc'

    ENCRYPT()忽略除 的前八个字符以外的所有字符str,至少在某些系统上是这样。此行为由底层crypt() 系统调用的实现决定。

    不建议使用ENCRYPT()ucs2、或多字节字符集utf16, 因为系统调用需要一个以零字节结尾的字符串。 utf16leutf32

    如果crypt()在您的系统上不可用(Windows 就是这种情况),则 ENCRYPT()始终返回 NULL.

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

  • MD5(str)

    计算字符串的 MD5 128 位校验和。该值以 32 位十六进制数字的字符串形式返回,或者 NULL如果参数是 NULL. 例如,返回值可以用作散列键。请参阅本节开头有关有效存储哈希值的注释。

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

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

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

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

  • OLD_PASSWORD(str)

    OLD_PASSWORD()PASSWORD()在 MySQL 4.1 中更改 实现时添加, 以提高安全性。OLD_PASSWORD()将 pre-4.1 实现的值 PASSWORD()作为字符串返回,旨在允许您为任何需要连接到 MySQL 服务器的 pre-4.1 客户端重置密码而不锁定它们。请参阅第 6.1.2.4 节,“MySQL 中的密码散列”

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

    笔记

    使用 pre-4.1 哈希方法的密码不如使用本机密码哈希方法的密码安全,应避免使用。4.1 之前的密码已弃用;期望在未来的 MySQL 版本中删除对它们的支持。因此, OLD_PASSWORD()也被弃用。

  • PASSWORD(str)

    返回根据明文密码计算的散列密码字符串str。返回值是连接字符集中的字符串,或者 NULL如果参数是 NULL. 此函数是服务器用于加密 MySQL 密码以存储在mysql.user授权表中的算法的 SQL 接口。

    系统变量控制函数使用的old_passwords密码散列方法 PASSWORD()。它还会影响使用子句指定密码的语句CREATE USER执行 的密码散列 。 GRANTIDENTIFIED BY

    下表显示了每种密码哈希方法的允许值old_passwords以及哪些身份验证插件使用该哈希方法。

    密码散列法 old_passwords 值 关联的身份验证插件
    MySQL 4.1 本机散列 0 mysql_native_password
    Pre-4.1()散列 1个 mysql_old_password
    SHA-256 哈希 2个 sha256_password
    笔记

    使用 pre-4.1 哈希方法的密码不如使用本机密码哈希方法的密码安全,应避免使用。4.1 之前的密码已弃用;期望在未来的 MySQL 版本中删除对它们的支持。因此, old_passwords=1导致PASSWORD()生成 pre-4.1 密码哈希的 也已弃用。有关帐户升级说明,请参阅 第 6.4.1.3 节,“从 4.1 版之前的密码哈希和 mysql_old_password 插件迁移”

    如果old_passwords=1, 返回与 相同的值 。后一个函数不受 值的影响 。 PASSWORD(str)OLD_PASSWORD(str)old_passwords

    mysql> SET old_passwords = 0;
    mysql> SELECT PASSWORD('mypass'), OLD_PASSWORD('mypass');
    +-------------------------------------------+------------------------+
    | PASSWORD('mypass')                        | OLD_PASSWORD('mypass') |
    +-------------------------------------------+------------------------+
    | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 | 6f8c114b58f2ce9e       |
    +-------------------------------------------+------------------------+
    
    mysql> SET old_passwords = 1;
    mysql> SELECT PASSWORD('mypass'), OLD_PASSWORD('mypass');
    +--------------------+------------------------+
    | PASSWORD('mypass') | OLD_PASSWORD('mypass') |
    +--------------------+------------------------+
    | 6f8c114b58f2ce9e   | 6f8c114b58f2ce9e       |
    +--------------------+------------------------+

    SHA-256 密码哈希 ( old_passwords=2) 使用随机盐值,这使得结果具有不 PASSWORD()确定性。因此,使用此函数的语句对于基于语句的复制是不安全的,并且不能存储在查询缓存中。

    执行的加密 PASSWORD()是单向的(不可逆)。它与用于 Unix 密码的加密类型不同;为此,使用 ENCRYPT().

    笔记

    PASSWORD()由 MySQL 服务器中的身份验证系统使用;你 应该在你自己的应用程序中使用它。为此,请考虑使用更安全的函数,例如 AES_ENCRYPT()or SHA2()。另请参阅 RFC 2195,第 2 节(挑战-响应身份验证机制 (CRAM)),了解有关在应用程序中安全处理密码和身份验证的更多信息。

    警告

    在某些情况下,调用的语句 PASSWORD()可能会记录在服务器日志中或在客户端的历史文件中,例如 ~/.mysql_history,这意味着任何对该信息具有读取权限的人都可以读取明文密码。有关服务器日志发生这种情况的条件以及如何控制它的信息,请参阅第 6.1.2.3 节,“密码和日志记录”。有关客户端日志记录的类似信息,请参阅 第 4.5.1.3 节,“mysql 客户端日志记录”

  • RANDOM_BYTES(len)

    此函数返回 len使用 SSL 库的随机数生成器生成的随机字节的二进制字符串。允许的值len范围为 1 到 1024。对于超出该范围的值,将发生错误。

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

    RANDOM_BYTES()生成一个随机值,这使得它的结果不确定。因此,使用此函数的语句对于基于语句的复制是不安全的,并且不能存储在查询缓存中。

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

    此功能从 MySQL 5.6.17 开始可用。

  • 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 时才有效。请参阅第 6.3 节,“使用加密连接”

    SHA2()可以认为在密码学上比 MD5()或 更安全SHA1()

  • UNCOMPRESS(string_to_uncompress)

    解压缩由 COMPRESS()函数压缩的字符串。如果参数不是压缩值,则结果为 NULL. 此功能要求 MySQL 编译时带有压缩库,例如 zlib. 否则,返回值始终为 NULL

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

    返回压缩字符串在压缩前的长度。

    mysql> SELECT UNCOMPRESSED_LENGTH(COMPRESS(REPEAT('a',30)));
            -> 30
  • VALIDATE_PASSWORD_STRENGTH(str)

    给定一个表示明文密码的参数,此函数返回一个整数以指示密码的强度。返回值范围从 0(弱)到 100(强)。

    密码评估由 插件VALIDATE_PASSWORD_STRENGTH()完成。validate_password如果未安装该插件,该函数始终返回 0。有关安装的信息 validate_password,请参阅 第 6.4.3 节“密码验证插件”。要检查或配置影响密码测试的参数,请检查或设置由 实现的系统变量 validate_password。请参阅 第 6.4.3.2 节,“密码验证插件选项和变量”

    密码受到越来越严格的测试,返回值反映满足哪些测试,如下表所示。

    密码测试 返回值
    长度 < 4 0
    长度 ≥ 4 且 < validate_password_length 25
    满足政策 1 ( LOW) 50
    满足策略 2 ( MEDIUM) 75
    满足政策 3 ( STRONG) 100