认证方法

1. mysql 320 password(3.20)             32-bit hash
2. mysql_old_password(3.21-4.0)         16-byte MD5 hash(aka. mysql323) 无盐
3. mysql_native_password(4.1)           41-byte double-SHA1 hash 无盐
4. sha256_password(5.6.6)               SHA-256 hash
5. caching_sha2_password(8.0.3)         caching SHA-256 hash
6. Multi-Factor Authentication(8.0.27)  支持多种认证方式
7. mysql_clear_password(5.5.10)         仅 client 端支持,用于 server 端需要 cleartext 的特殊情况,如 PAM and simple LDAP
8. ed25519(mariadb 10.1.22)             仅 MariaDB 支持
9. ...

mysql_320_password

MySQL 3.20 开始使用的认证方法

It should come as no surprise that MySQL never sent passwords from the client to the server in clear text. It sent random bytes that were generated from hashes of passwords. Technically, the first MySQL authentication protocol (as in MySQL–3.20, 1996) worked as follows:

Server has stored the password hash in the mysql.user table. The hash function was rather simple, though:

for (; *password ; password++)
{tmp1 = *password;hash ^= (((hash & 63) + tmp2) * tmp1) + (hash << 8);tmp2 += tmp1;
}

Note, that the hash value was only 32 bits!

认证协议

  • During the authentication, the server started the handshake with a string of 8 random letters (called scramble).
  • The client calculated the hash (as above) of this scramble, and the hash of the password. XOR of these two numbers produced one 32-bit seed that was used to initialize a pseudo-random number generator. That generator generated “random” 8 bytes and they were sent to the server.
  • The server, basically, repeated the same — it knew the scramble, it had the hash of a password from the mysql.user table. So it also initialized a random number generator, generated 8 bytes, and compared them with what the client had sent.

This wasn’t a bad protocol. It had obvious strengths, for example, the password was never sent in clear. And was never stored in clear either. But, seriously, 32-bit? That wasn’t enough even in 1996. Which is why the next major MySQL release — 3.21 — used 64-bit hashes. Otherwise the protocol stayed the same. And it is still present (although not the default) in MySQL–5.6 and MariaDB–10.2. Luckily, it was removed from MySQL–5.7. I really hope nobody uses it nowadays.

mysql_old_password

MySQL 3.21 开始使用的认证方法,5.7.5 起不再支持

Lacking any sort of salt, ignoring all whitespace, and having a simplistic algorithm that amounts to little more than a checksum, this is not secure, and should not be used for any purpose.

认证协议 同上 mysql 320

the next major MySQL release — 3.21 — used 64-bit hashes. Otherwise the protocol stayed the same. And it is still present (although not the default) in MySQL–5.6 and MariaDB–10.2. Luckily, it was removed from MySQL–5.7. I really hope nobody uses it nowadays.

密码格式

16字节密码散列如
318b243e220ca492
673761a01d8a119f

密码算法

#!python3
# https://stackoverflow.com/questions/37596450/old-password-function-in-5-7-5def mysql_old_password(password):nr = 1345345333add = 7nr2 = 0x12345671for c in (ord(x) for x in password if x not in (' ', '\t')):nr^= (((nr & 63)+add)*c)+ (nr << 8) & 0xFFFFFFFFnr2= (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFFadd= (add + c) & 0xFFFFFFFFreturn "%08x%08x" % (nr & 0x7FFFFFFF,nr2 & 0x7FFFFFFF)if __name__ == '__main__':import sysif len(sys.argv) != 2:print >> sys.stderr , 'Python Implementation of MySQL\'s old password hash'print >> sys.stderr , 'Usage: %s password' % sys.argv[0]sys.exit(1)print(mysql_old_password(sys.argv[1]))

mysql_native_password

MySQL 4.1.1 开始使用

Lacking any sort of salt, and using only 2 rounds of the common SHA1 message digest, it’s not very secure, and should not be used for any purpose.

The advantage of mysql_native_password is that it support challenge-response mechanism which is very quick and does not require encrypted connection. However, mysql_native_password relies on SHA1 algorithm and NIST has suggested to stop using it.

认证协议

And this is how the double-SHA1 (or new or mysql_native_password) protocol was created. It was first introduced in MySQL–4.1 and is still the most widely used MySQL authentication protocol. Every MySQL and MariaDB version supports it. It works as follows:

  • The server stores SHA1(SHA1(password)) in the mysql.user table.
  • For authentication the server sends a random 20-letter scramble.
  • The client computes the following:
    SHA1( scramble || SHA1( SHA1( password ) ) ) ⊕ SHA1( password )
    

    where ⊕ is XOR and || is string concatenation. And sends it to the server.

  • The server doesn’t know SHA1(password), but it knows the scramble and SHA1(SHA1(password)), so it can calculate the first part of the expression, and with a XOR it can get SHA1(password).
  • Now all it needs to do is to calculate the SHA1 of SHA1(password) from the previous step, and it can compare the result with what is stored in the mysql.user table. Mission accomplished.

This protocol achieved all goals — sniffing the authentication handshake or stealing the mysql.user table would not help the attacker to impersonate users. Still, it wasn’t perfect. The server received SHA1(password) (so it could show up in core dumps, be extracted from the server memory, etc) and that was sufficient to impersonate a user. Furthermore, if someone was able to sniff the authentication handshake and steal the mysql.user table, he would be able to repeat all steps that server did, extract SHA1(password) and impersonate a legitimate user too. While it was a flaw, it was not a major issue — when one has password hashes it’s usually easier just to brute-force them. And I had a feeling that this flaw was impossible to fix anyway, unless we resort to public key cryptography.

密码格式

星号 + 40字节十六进制表示的密码散列如:
*14BDCEBE19082CE2A1F959FD02F964D7AF4CFC29

密码算法

A mysql-41 password hash consists of an asterisk * followed by 40 hexadecimal digits, directly encoding the 160 bit checksum. An example hash (of password) is *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19. MySQL always uses upper-case letters, and so does Passlib (though Passlib will recognize lower-case letters as well).

The checksum is calculated simply, as the SHA1 hash of the SHA1 hash of the password, which is then encoded into hexadecimal.

#!python3
# https://foss.heptapod.net/python-libs/passlib/-/blob/branch/stable/passlib/handlers/mysql.pydef mysql_native_password(password):if isinstance(password, str):password = password.encode("utf-8")return sha1(sha1(password).digest()).hexdigest().upper()if __name__ == '__main__':import sysfrom hashlib import sha1if len(sys.argv) != 2:print >> sys.stderr , 'Python Implementation of MySQL\'s native password hash'print >> sys.stderr , 'Usage: %s password' % sys.argv[0]sys.exit(1)print(mysql_native_password(sys.argv[1]))

sha256_password

MySQL 5.6.6 开始使用

Since MySQL 5.6, sha256_password authentication plugin is supported. It uses multiple rounds of SHA256 hash on a salted password to make sure that the hash transformation is more secure. However, it requires either encrypted connections or support for an RSA key pair. So, while password security is stronger, secure connections and multiple rounds of hash transformations require more time in the authentication process.

认证协议

So, the new MySQL 5.7 authentication protocol (sha256_password plugin) uses SHA256 (the 256-bit version of SHA2) and RSA. Together it works like this:

  • The server stores SHA256(password), yay! 应该是 salt+password,再 5000 次 sha256
  • During authentication it sends to the client a 20-letter scramble, just as before.
  • Client reads the server’s public RSA key from a file that was distributed to the client in advance.
  • Client calculates XOR of the password and the scramble (repeated as necessary to cover the whole password), encrypts it using the server’s public key, and sends to the server.
  • Server decrypts the data using its secret RSA key, XORs the result with a scramble to extract the client’s plain-text password, calculates SHA2 of it, and compares that with the stored value from the mysql.user table.

Quite straightforward. There is just one nuisance. One has to distribute the server’s public key to all clients. And every client needs to have public keys for all servers it wants to connect to and juggle them as needed. I could see where it could become rather annoying, and probably that’s why if the client does not have the server’s public key, the server helpfully provides it, during authentication, at the cost of one round-trip. Of course, no security-conscious person should ever rely on that, how would the client know that the public key is authentic? A man-in-the-middle can replace it with his own public key and he’ll be able to obtain the client password in plain-text! And again, not that it’s particularly bad, but the server still gets the password in plain-text. Just like it did in all previous authentication protocols.

密码格式

$5$ + 20字节盐 + $ + 43字节密码散列如:
$5$-Q79?S ;"0GQ} $XAnNxGr1TcfE6NdTjeAu.vMV8EW1Nl5dv.XomcV3LHC盐包含不可见的控制字符

密码算法

#!python
import hashlib
import os
import syssmall_letters = list(map(lambda x: bytes((x,)), range(ord('a'), ord('z')+1)))
big_letters = list(map(lambda x: bytes((x,)), range(ord('A'), ord('Z')+1)))
digits = list(map(lambda x: bytes((x,)), range(ord('0'), ord('9')+1)))
i64 = [ b'.', b'/' ]
i64 += digits
i64 += big_letters
i64 += small_lettersdef to64(v, n):str = b''n -= 1while n >= 0:str += i64[ v & 0x3F ]v >>= 6n -= 1return strdef sha_crypts(bits, key, salt, loops):bytes = bits / 8# 计算哈希值 a# print(key)# print(salt)b = hashlib.sha256(key+salt+key).digest()tmp = key + salti = len(key)while i > 0:if i > bytes:tmp += belse:tmp += b[0:i]i -= bytesi = len(key)while i > 0:if i & 1 != 0:tmp += belse:tmp += keyi >>= 1hash = hashlib.sha256(tmp)a = hash.digest()# print(f'a({len(a)})={a}')# print(hash.hexdigest())# 计算字符串 ptmp = b''for i in range(len(key)):tmp += keydp = hashlib.sha256(tmp).digest()p = b''i = len(key)while i > 0:if i > bytes:p += dpelse:p += dp[0:i]i -= bytes# print(f'p({len(p)})={p}')# print(p.hex())# 计算字符串 stmp = b''til = 16 + a[0]for i in range(til):tmp += saltds = hashlib.sha256(tmp).digest()s = b''i = len(salt)while i > 0:if i > bytes:s += dselse:s += ds[0:i]i -= bytes# print(f's({len(s)})={s}')# print(s.hex())# 计算哈希值 cc = afor i in range(loops):if i & 1 != 0:tmp = pelse:tmp = cif i % 3 != 0:tmp += sif i % 7 != 0:tmp += pif i & 1 != 0:tmp += celse:tmp += phash = hashlib.sha256(tmp)c = hash.digest()# print(f'c({len(c)})={c}')# print(hash.hexdigest())# 最终哈希值if bits == 256:inc1 = 10inc2 = 21mod = 30end = 0else:inc1 = 21inc2 = 22mod = 63end = 21i = 0tmp = b''while True:#print(f'i={i}')x = c[i]y = c[(i+inc1)%mod]z = c[(i+inc1*2)%mod]tmp += to64( ((x<<16)|(y<<8)|z), 4)i = (i+inc2)%modif i == end:breakif bits == 256:tmp += to64( ((c[31]<<8)|c[30]), 3 )else:tmp += to64( c[63], 2 )return tmpdef generate_user_salt():tmp = os.urandom(20)str = b''for i in range(len(tmp)):n = tmp[i] & 0x7fif n == 0 or n == ord('$'):n += 1str += bytes((n,))return strdef sha256_password(password, salt=None):if not salt:salt_bytes = generate_user_salt()salt = salt_bytes.hex()password = password.encode('utf8')salt_bytes = bytearray.fromhex(salt).decode().encode('utf8')loops = 5000hash = sha_crypts(256, password, salt_bytes, loops)result = b'$5$'+salt_bytes+b'$'+hashprint(f'bytes: {result}')result2 = '$5$'+salt+'$'+hash.hex()print(f'hex: {result2}')if __name__ == '__main__':if len(sys.argv) < 2:print >> sys.stderr , 'Python Implementation of MySQL\'s sha256 password hash'print >> sys.stderr , 'Usage: %s {password} {salt}' % sys.argv[0]sys.exit(1)if len(sys.argv) >= 3:sha256_password(sys.argv[1], sys.argv[2])else:sha256_password(sys.argv[1])

caching_sha2_password

MySQL 8.0 开始支持

For a majority of connection attempts, when there exists a cached copy of the password hash in memory, it uses a SHA256-based challenge-response mechanism while authenticating a client (compared to a SHA1-based challenge-response mechanism in mysql_native_password). This is faster and allows secure authentication over an unencrypted channel. The following figure summarizes challenge-response based authentication.

!fast.jpg!

It employs a technique similar to sha256_password before storing credential information in the mysql.user table. It uses 5000 rounds of SHA256 transformation on a salted password.

The following figure summarizes full authentication.

!full.jpg!

mysql_native_password vs. caching_sha2_password

                                                mysql_native_password    caching_sha2_password
散列                                            SHA1                     SHA256
用盐                                            没有                     YES – 20 Bytes
使用散列的轮数                                  2                        5000
Supports Challenge-Response Authentication      YES                      YES (FAST mode)
Re-ascertain Password Knowledge                 无需                     YES (COMPLETE mode)

密码格式

$A$005$ + 20字节盐 + 43字节密码散列如
$A$005$tnsH54yUg1kNa        K(6NKkD7aVVSpEqy8NrQa0F94.Pgvv7XO8up991nv32WL8

密码算法同 sha256_password

ed25519

The new MariaDB authentication protocol also uses one of these reference ed25519 implementations, and works like this:

  • The user’s password is the secret key. We calculate SHA512(password) and applying some math magic convert it into a public key. This public key is stored by the server in the mysql.user table.
  • For authentication the server sends a random 32-byte scramble to the client.
  • The client signs it with its secret key.
  • The server verifies the signature with the user’s public key.

That’s all! Much simpler than with double-SHA1. And it’s as secure as it can be, the password is not stored on the server, not sent anywhere, the server doesn’t even see it at any point in time and cannot restore it from the information it has. Nothing for the man-in-the-middle here either. No files. Same number of round-trips. One can still brute-force passwords, given the mysql.user table, but there’s nothing we can do about that.

兼容性

                             MySQL Server         MySQL Client                MySQL Connector/Python     MySQL Connector/J
mysql_old_password           <5.7.5(2014-09-25)   <5.7.5                      not supported              all versions
mysql_native_password        4.1.1+               4.1.1+                      all versions               all versions
sha256_password              5.6.6+(2012-08-07)   5.6.6+                      1.2.1+(2014-03-31)         5.1.31+(2014-06-07)
caching_sha2_password        8.0.3+(2017-09-21)   5.7.23+(2018-07-27),8.0.3+  8.0.5+(2017-09-28)         5.1.46+(2018-03-12),8.0.9+(2018-01-30)
Multi-Factor Authentication  8.0.27+(2021-10-19)  8.0.27+                     8.0.28+(2022-01-18)        8.0.28+(2022-01-18)
ed25519                      not supported        not supported               not supported              not supportedMariaDB Server & Client                   MariaDB Connector/C  MariaDB Connector/J
mysql_old_password           all versions                              all versions         all versions
mysql_native_password        all versions                              all versions         all versions
sha256_password              not supported                             3.0.2+(2017-07-20)   2.5.0+(2019-10-03)
caching_sha2_password        not supported                             3.0.8+(2018-12-21)   2.5.0+
Multi-Factor Authentication  not supported                             not supported        not supported
ed25519                      10.1.22+(2017-03-14),10.2.5+(2017-04-05)  3.1.0+(2019-04-08)   2.2.1+(2017-12-22)Perl-DBD:MySQL
mysql_old_password           depends on lib{mysqlclient,mariadb}.so
mysql_native_password        depends on lib{mysqlclient,mariadb}.so
sha256_password              depends on lib{mysqlclient,mariadb}.so
caching_sha2_password        depends on lib{mysqlclient,mariadb}.so
Multi-Factor Authentication  depends on lib{mysqlclient,mariadb}.so

参考

  • https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html

  • https://dev.mysql.com/doc/refman/5.7/en/authentication-plugins.html

  • https://docs.oracle.com/cd/E17952_01/mysql-5.5-en/authentication-plugins.html

  • https://dev.mysql.com/doc/relnotes/mysql/8.0/en/

  • https://dev.mysql.com/doc/relnotes/mysql/5.6/en/

  • https://downloads.mysql.com/docs/mysql-5.5-relnotes-en.pdf

  • https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/

  • https://dev.mysql.com/doc/relnotes/connector-j/5.1/en/

  • https://dev.mysql.com/doc/relnotes/connector-python/en/

  • https://dev.mysql.com/doc/relnotes/connector-cpp/en/

  • https://mariadb.com/kb/en/authentication-plugins/

  • https://mariadb.com/kb/en/about-mariadb-connector-j/#choosing-a-version

  • https://github.com/mysql/mysql-connector-j/tree/release/8.0/src/main/protocol-impl/java/com/mysql/cj/protocol/a/authentication

  • https://github.com/mysql/mysql-connector-python/blob/master/lib/mysql/connector/authentication.py

  • https://passlib.readthedocs.io/en/stable/lib/passlib.hash.mysql323.html

  • https://lefred.be/content/perl-mysql-8-0/

  • https://www.cnblogs.com/olinux/p/13201497.html

  • https://dev.mysql.com/blog-archive/mysql-8-0-4-new-default-authentication-plugin-caching_sha2_password/

  • https://stackoverflow.com/questions/37596450/old-password-function-in-5-7-5

  • https://mariadb.org/history-of-mysql-mariadb-authentication-protocols/

  • https://dciabrin.net/posts/2020/09/connecting-to-mariadb-with-auth_ed25519-and-pymysql.html

  • http://mysqlblog.fivefarmers.com/2015/08/31/protecting-mysql-passwords-with-sha256_password-plugin/

  • https://crypto.stackexchange.com/questions/77427/whats-the-algorithm-behind-mysqls-sha256-password-hashing-scheme

  • https://mysqlserverteam.com/mysql-8-0-4-new-default-authentication-plugin-caching_sha2_password/

  • https://github.com/hashcat/hashcat/issues/2305

  • https://www.percona.com/blog/2020/06/12/brute-force-mysql-password-from-a-hash/

  • https://dev.mysql.com/blog-archive/a-tale-of-two-password-authentication-plugins/

.eof.

MySQL Authentications相关推荐

  1. mysql 数据库 安全_如何确保您MySQL数据库安全

    mysql 数据库 安全 我们开始之前的一些基本信息: (Some basic information before we get started:) Source: Center for Inter ...

  2. MySQL数据库密码破解

    研究MySQL数据库的加解密方式,在网络攻防过程中具有重要的意义:试想一旦获取了网站一定的权限后,如果能够获取MySQL中保存用户数据,通过解密后,即可通过正常途径来访问数据库:一方面可以直接操作数据 ...

  3. mysql 客户端_MySQL客户端攻击链的探索

    原创:Kale合天智汇 原创投稿活动:重金悬赏 | 合天原创投稿等你来 0X00 前言 前言 实验室的大佬在Tsec分享了一个mysql的议题,正好我对此也有兴趣,所以就写一篇关于mysql攻击链的文 ...

  4. mysql 快捷查询数据库各表占用空间,mysql查看索引占用空间,mysql查看数据占用空间

    先贴一张图! 第一步,"很重要" 在mysql中,有一个创建之初自带的库information_schema,这个库中包含着数据库相关信息,查询数据占用空间就是使用该库,所以首先进 ...

  5. mysql并发更新数据,多用户并发修改数据解决方案。

    mysql并发更新数据,多用户并发修改数据解决方案. 在系统中,有一些如余额.资产.积分的数据,是要保证数据一致性的.如,一个人使用两个设备同时进行消费操作,如何保证数据一致性的问题. 我们一起来思考 ...

  6. mysql查询字段大小写结果相同,mysql大小写查询不敏感,mysql5.7查询不区分大小写解决方案。

    下面有两条sql,主键查询,在mysql中查询到的结果相同. SELECT* FROM USER WHEREid = 'EM58hdK4nXC';SELECT* FROM USER WHEREid = ...

  7. 数据库中自定义排序规则,Mysql中自定义字段排序规则,Oracle中自定义字段排序规则,decode函数的用法,field函数的用法

    数据库中自定义排序 场景:有一张banner表,表中有一个status字段,有0, 1, 2三个状态位,我想要 1,0,2的自定义排序(这里是重点),然后再进行之上对sequence字段进行二次排序( ...

  8. mybatis查询报错:com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from string

    mybatis查询报错: com.mysql.cj.exceptions.DataConversionException: Cannot determine value type from strin ...

  9. docker一步安装mysql,docker的魅力就在于此

    正常来说,使用docker安装东西的步骤是serach它有没有,然后pull它 这里其实只要一步(如果你没有安装过.没有端口占用等问题的话!!) $ docker run -d -p 3306:330 ...

最新文章

  1. 韩国《流感》真实上演,三星、海力士中招,全球半导体版图生变?
  2. 用python画月亮的代码-用 Python 画一个超级月亮
  3. 递归,记忆化搜索,(棋盘分割)
  4. redis缓存策略小结
  5. 如何搭建高接通率的音视频场景?
  6. 一个application多个 URL
  7. Python压平嵌套列表的一种方法
  8. AD9833 实验总结
  9. java中modifier_Ruby中的private modifier与Java中的对比
  10. 透过细节看日本(转)
  11. CSAPP buflab 实验报告
  12. OMV安装可道云kodexplorer网盘
  13. 快问快答,MySQL面试夺命20问
  14. 【Python实例】Python五分钟码出—女神颜值打分系统(人脸识别)!
  15. 2020成电计算机考研
  16. Game Programming with DirectX -- 08[Mesh]
  17. IT宅男利用Python网络爬虫获取有道翻译手机版翻译接口
  18. ipa文件包获取服务器地址,ipa文件包获取服务器地址
  19. 2018 大厂高级前端面试题汇总
  20. jquery浮层居中

热门文章

  1. Tomcat调优总结
  2. 浅谈PHP代码执行的大致流程(opcode)
  3. TTL的含义是什么?
  4. 软件工程第六章——详细设计
  5. Unity 之 ShaderGraph Utility节点解析汇总
  6. ehviewer_EhViewer官方下载_EhViewer最新版app下载 安卓版v1.7.1 - 浪浪下载
  7. 【中序、后序遍历序列】【前序、中序遍历序列】构造二叉树
  8. jQuery入门(1)
  9. 1.python-web
  10. MYSQL彻底卸载(步步图解)