暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MYSQL 协议 (5) SSL连接 (含mysql流量镜像脚本)

之前讲了mysql的连接, 但是排除了SSL的情况. 这次就来看看有SSL的情况咋连接

连接过程

连接过程比较简单, 就是发送密码之前,告诉server我要使用ssl

client ->>  server : connect
server ->>  client : salt
client -->>  server : SSL(36byte)
client ->>  server : password
server ->> client: OK/ERR

image.png

SSL包

就是正常CLIENT_PROTOCOL_41包的前面部分(前32字节, 加上header一共36字节)
image.png

测试

本次就不光连接mysql server了, 没得意思, 我们模拟个mysql server , 转发client发来的数据, 同时把数据再克隆一份到其它服务器, 但是本次实验没得其它服务器, 所以就print出来瞧瞧吧

原理

image.png
和之前的差不多, 只是多了个支持SSL

使用

self.server #REAL SERVER 就是真实的mysql服务器
self.host 监听地址
self.port 监听端口
self.cert 证书 使用的mysql自己的, 也可以使用openssl去生成
self.key

官方生成证书教程: https://dev.mysql.com/doc/refman/5.7/en/creating-ssl-files-using-openssl.html
image.png

执行脚本

python mysql_monitor.py

image.png
注: TLS继承自SSL

看下我们Print出来的流量

S->C : 表示该数据包是真实服务器发往客户端的(实际上是我们转发的)
C->S : 表示该数据包是客户端发往真实服务器的(实际上是我们转发的)
后面的序号表示 seq.
seq在每个query里面都会重置为0,  最大255, 超过255的就又从0开始( seq %= 255)

image.png
非ssl连接也是可以的, 这里就不演示了.

这里看到的是明文是因为, 我们读取包之后就已经解析为明文了, 方便处理数据. 如果你使用抓包软件(tcpdump/wireshark)之类的看到的就是加密之后的数据
image.png

总结

  1. 封装SSL
		if len(bdata) < 38: #封装为SSL (32+4)
			#封装 客户端的SSL (因为相对于client, 这是server角色)
			context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
			context.load_cert_chain(certfile=self.cert, keyfile=self.key)
			conn = context.wrap_socket(conn, server_side=True)
			client_rf = conn.makefile('rb')

			#封装 到server的SSL
			sock = ssl.wrap_socket(sock)
			server_rf = sock.makefile('rb')
  1. 如果使用的是makefile的话, 注意封装完ssl后要重新makefile, 不然会报错pipe broken

附脚本

可以自定义输出, 比如包大小, 包类型等.

import struct from threading import Thread from multiprocessing import Process import socket import time import sys import ssl def btoint(bdata,t='little'): return int.from_bytes(bdata,t) def read_pack(rf): pack_header = rf.read(4) if len(pack_header) < 4: print(pack_header,' bye!') sys.exit(2) btrl, btrh, packet_seq = struct.unpack("<HBB", pack_header) pack_size = btrl + (btrh << 16) bdata = rf.read(pack_size) return pack_header+bdata class mmonitor(object): def __init__(self): self.host = '0.0.0.0' self.port = 3306 self.server = ('192.168.101.21',3308,) self.cert = '/data/mysql_3306/mysqldata/server-cert.pem' self.key = '/data/mysql_3306/mysqldata/server-key.pem' def handler_msg(self,rf,sock,f): #print(f'{f} start') while True: bdata = read_pack(rf) sock.sendall(bdata) print(f'{f}',btoint(bdata[3:4]),bdata) def handler(self,conn,addr): sock = socket.create_connection((self.server[0], self.server[1])) server_rf = sock.makefile('rb') bdata = read_pack(server_rf) conn.sendall(bdata) print('S->C: ',btoint(bdata[3:4]),bdata) client_rf = conn.makefile('rb') bdata = read_pack(client_rf) print('C->S: ',btoint(bdata[3:4]),bdata) sock.sendall(bdata) if len(bdata) < 38: #封装为SSL (32+4) #print('SSL') #封装客户端的SSL (因为相对于client, 这是server角色) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain(certfile=self.cert, keyfile=self.key) conn = context.wrap_socket(conn, server_side=True) client_rf = conn.makefile('rb') #封装到server的SSL sock = ssl.wrap_socket(sock) server_rf = sock.makefile('rb') t1 = Process(target=self.handler_msg,args=(client_rf,sock,'C->S: ')) #监控客户端数据, 然后发往server端 t2 = Process(target=self.handler_msg,args=(server_rf,conn,'S->C: ')) t1.start() t2.start() t1.join() t2.join() def init(self): socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_server.bind((self.host, self.port)) socket_server.listen(12345) #设置连接数 self.socket_server = socket_server accept_client_thread = Thread(target=self.accept_client,) accept_client_thread.start() accept_client_thread.join() def accept_client(self,): while True: conn, addr = self.socket_server.accept() p = Process(target=self.handler,args=(conn,addr),) p.start() aa = mmonitor() aa.init()
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论