连接过程概述
MySQL客户端和服务端建立连接的过程可以概括为:
- server 监听端口
- client 向server建立TCP连接
- server 向client发送挑战码报文(mysql认证采用的是CHAP 协议,即挑战握手认证协议,挑战码用于加密client输入的密码,从而避免了网络传输明文密码)
- client 使用挑战码加密密码,将加密后的密码包含在回包中,发送给server
- server 根据client的回包解密,检查是否与预期的结果相同,给client发送ok包或error包
客户端连接认证流程
其中客户端连接认证流程如下:
从sql_connection.cc下的thd_perpare_connection开始看起。
|-->thd_perpare_connection(权限认证)
……|-->lex_start(在准备和执行每个查询之前调用lex_start(),创建select_lex和select_lex_unit对象)
……|-->login_connection(识别用户,并报告错误)
…………|-->set_read_timeout(设置网络接收的超时时间)
…………|-->set_write_timeout(设置网络发送的超时时间)
…………|-->check_connection(执行握手、授权客户端并更新thd ACL变量)
………………|-->如果是TCP/IP方式连接:
acl_check_host检查host或ip是否匹配
………………|-->如果是socket方式连接:
设置host,默认为localhost
………………|-->设置为keepalive
………………|-->分配net_buffer_length,为SESSION变量,表示TCP/IP和socket通信缓冲区大小,设定的大一点可以加快导入速度
………………|-->认证前审计检查
………………|-->acl_authenticate密码认证
……………………|-->do_auth_once使用默认插件执行第一次身份验证尝试。
这会发送服务器握手数据包,读取带有用户名的客户端回复,并在每个人都使用了正确的插件时执行身份验证(具体展开见后文)
………………|-->认证后审计检查
…………|-->发送状态信息到客户端
…………|-->发送ok包
……|-->初始化thd,准备处理客户端请求
acl_authenticate密码认证流程如下:
函数位于sql_authentication.cc下
|-->acl_authenticate
……|-->do_auth_once使用默认插件执行第一次身份验证尝试。
这会发送服务器握手数据包,读取带有用户名的客户端回复,并在每个人都使用了正确的插件时执行身份验证
…………|-->authenticate_user
………………|-->指向native_password_authenticate
……………………|-->generate_user_salt服务端生成挑战码
……………………|-->write_packet握手认证阶段(客户端与服务器建立连接后进行),服务端发送初始化报文到客户端
…………………………|-->指向server_mpvio_write_packet
………………………………|-->send_server_handshake_packet
tips:权能标志:用于与客户端协商通讯方式。客户端收到服务器发来的初始化报文后,会对服务器发送的权能标志进行修改,保留自身所支持的功能,然后将权能标返回给服务器,从而保证服务器与客户端通讯的兼容性。
……………………………………|-->protocol_classic.cc-->my_net_write发送报文给客户端
……………………|-->read_packet服务端接收并解析客户端发送的登录认证信息报文
…………………………|-->指向server_mpvio_read_packet
………………………………|-->read_packet读取客户端报文
……………………………………|-->my_net_read
……………………|-->parse_client_handshake_packet解析客户端报文
…………………………|-->客户端报文格式在client.c-->send_client_replay_packet中
……………………|-->check_scramble检查密码是否正确。pkt:接收的客户端加密后的密码;scramble:扰乱;salt:该用户加密后的真实密码
…………………………|-->password.c下的check_scramble。scramble_arg:接收的客户端加密后的密码;message:扰乱;hash_stage2:该用户加密后的真实密码
………………………………|-->check_scramble_sha1检查加扰消息是否与密码对应;服务器使用该功能来检查接收到的回复是否真实。
……………………………………|-->buf=sha1(message,hash_stage2)
……………………………………|-->buf=xor(buf,scramble_arg)
……………………………………|-->hash_stage2_reassured=sha1(buf)
……………………………………|-->比较(hash_stage2,hash_stage2_reassured)
服务端客户端加密解密原理梳理
客户端
{
recv(scramble)
client_A=sha1(password)
client_B=sha1(client_A)
C=xor(client_A,sha1(scramble,client_B))
}
服务端
{
recv(C)
server_A = xor(C,sha1(scramble,real_B))
备注:real_B=sha1(sha1(real_password))
server_B=sha1(server_A)
memcmp(real_B,server_B)
}
即: 1、客户端对密码做2次hash,在加上扰乱、异或得到C,把C发送给服务器 2、服务器利用A^B^B=A的原理,得到server_A,如果client_B=real_B的话,那么server_A其实是等于client_A的,也就是说,对server_A做1次hash得到的server_B是等于real_B的