背景:nginx的https支持使用的是openssl库,对于大部分线上请求都能正确处理,可以大部分需求。但是在go语言中的https功能并未经过长期的考验,没有经过大项目的检验,所以一致性尤为重要。
方法:1、升级nginx中的代码,从线上抽样打印ssl的ClientHello信息。
2、分析日志获取ClientHello信息。
3、修改openssl s_client 代码,能够加载ClientHello信息。
从线上抽样打印ssl的ClientHello信息
这里主要讲述ngxin中如何对数据进行收集和openssl s_client 代码的修改。
关于nginx中需要在ngx_http_request.c中ngx_http_ssl_handshake函数,在ngx_ssl_create_connection函数调用成功后添加如下逻辑:
调用SSL_set_msg_callback来设置回调函数ngx_ssl_handshark_msg,自己可以实现ngx_ssl_handshark_msg的具体处理逻辑。
然后设置回调函数使用的参数:SSL_set_msg_callback_arg(c->ssl->connection, ¶m_cb);
加载ClientHello信息
在 s23_clnt.c 文件的函数 ssl23_client_hello 中的657行后添加了修改client hello信息代码:
以为当前线上没有ssl3一下的请求,所以我们这里只修改了ssl3的相关处理。
这里修改client hello信息的时候,不要修改random的信息。因为random的信息有时间信息,我们录制的client hello信息中的random信息多半是昨天以前的,所以如果直接用这样的random信息会出错的,所以一定不要修改这部分信息,其他信息完全拷贝就行了。修改完以后一定要修改s->init_num和s->init_off的数据。
为了能够通过命令行指定我们的client hello信息,我们对参数的处理进行了修改。
当前命令行的用法是:
openssl s_client -host 127.0.0.1 -port 1280 -client_hello 1603010050123412Ab231234123412341234123434123412341234……
切记要是用client_hello这个功能, 就不要再指定 ssl或tls的版本了,因为一旦指定版本,就不在使用ssl23_client_hello 这个默认函数了,那么我们修改的逻辑就走不到了。
相关修改ClientHello信息的代码:
s23_clnt.c
119 extern unsigned char *client_hello = NULL;
120 static int atoi_hex(const unsigned char data) {
121 int ret = -1;
122
123 if (data >= '0' && data <= '9') {
124 ret = data - '0';
125 }
126
127 if (data >= 'A' && data <= 'F') {
128 ret = data - 'A' + 10;
129 }
130
131 if (data >= 'a' && data <= 'f') {
132 ret = data - 'a' + 10;
133 }
134 return ret;
135 }
136
137 static int atoi_buf_hex(const unsigned char *src_data, size_t src_len, unsigned char *dist_data, size_t dist_len) {
138 unsigned char value = 0;
139 int tmp = 0;
140 size_t i = 0;
141 if (src_len != dist_len * 2) {
142 return -1;
143 }
144
145 for (i = 0; i < dist_len && i * 2 < src_len; ++ i) {
146 tmp = atoi_hex(src_data[i * 2]);
147 if (tmp < 0) {
148 return -1;
149 }
150 value = (unsigned char)tmp;
151
152 tmp = atoi_hex(src_data[i * 2 + 1]);
153 if (tmp < 0) {
154 return -1;
155 }
156 value = value << 4 | tmp;
157 dist_data[i] = value;
158 }
159 return 0;
160 }
161
162 static int fill_client_hello_info(SSL *s, const unsigned char *data, size_t len) {
163 unsigned char *buf = NULL;
164 size_t offset = (32 + 11) * 2;
165 if (NULL == s || NULL == s->init_buf || NULL == s->init_buf->data) {
166 printf("error general in fill_client_hello_info");
167 return -1;
168 }
169
170 buf = s->init_buf->data;
171 if (atoi_buf_hex(&data[0], 22, buf, 11)) {
172 return -1;
173 }
174
175 if (atoi_buf_hex(&data[offset], len - offset, &buf[offset / 2], (len - offset) / 2) < 0) {
176 return -1;
177 }
178
179 s->init_num = len / 2;
180 s->init_off = 0;
181
182 return 0;
183 }
有疑问加站长微信联系(非本文作者)