CS两端TLS版本不适配导致Connection reset问题

欢迎访问陈同学博客原文

问题背景

近期平台在公司的一个出口IP流量偶尔抖动,在与运营商扯皮无结果后,IT帮忙开了一条新的专线。我们需要把域名在公网的DNS指向新的出口IP。

下面是简图:
image
  • 旧:流量经公网IP 126,采用端口映射直接到平台的代理机,然后再转发给后端具体的业务代理。
  • 新:流量经公网IP 189,然后经公司统一代理(Proxy A),由代理把流量转发到我们自己的代理上。

切换DNS后,部署在云服务器上的应用利用Http Client访问部署在公司内网的服务时,出现 java.net.SocketException: Connection reset 异常。

部分stacktrace如下,Http Client在建立连接时出现了问题。

Caused by: java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:196)
    at java.net.SocketInputStream.read(SocketInputStream.java:122)
    at sun.security.ssl.InputRecord.readFully(InputRecord.java:442)
    at sun.security.ssl.InputRecord.read(InputRecord.java:480)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:934)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)

问题排查

  • 因应用未更新,仅切换了DNS,这是唯一变量。做了个试验,将流量切换回旧的 x.x.x.126 后,一切正常。

    => 初步确定问题是由新线路与旧线路有些不同导致!

  • 领导特意提醒:公司统一代理只支持TLSv1.1、TLSv1.2。我在本地测试,加了系统参数 -Dhttps.protocols=TLSv1.2,发现还是出同样问题。

    => 于是,认为这个问题可能和TLS版本无关

  • curlpostman 发起相同请求,一切正常。

    => 那应该就是Http Client 这里的问题了

  • 由于我们对Http Client做了封装,简化了使用(简称A项目)。因此,新建了个项目(简称B项目),直接用 Http Client 发起请求,发现竟然请求成功了。

    => 真是不可思议,那两个项目的差别是什么?一下也没想起来。

  • 因为是网络问题,因此,A、B项目都加上系统参数 -Djavax.net.debug=all,希望能抓住点蛛丝马迹。一运行,还真不一样。

    A项目是jdk1.7,建立连接时报文有:ClientHello, TLSv1

    B项目是jdk1.8,建立连接时报文有:ClientHello, TLSv1.2

    此时,才想起,这是一个旧项目,用的是jdk1.7。

至此,问题解决。是因为公司代理只支持TLSv1.1、TLSv1.2,而客户端发起请求时用的是TLSv1,服务端直接拒绝握手。虽然无法升级jdk版本,但可以指定http client使用的TLS协议版本。

解决问题还是比较简单,下面借助于问题学习下涉及到的知识。

晓波补充:C不止于Java Client,像浏览器等其他client也会出现类似问题。问题可以归纳为CS两端TLS协议版本不适配。

拓展学习

什么是 connection reset ?

指服务端因某种原因关闭了连接,而此时客户端依然在读取数据,此时服务端会返回复位标识 RST,客户端就会提示:java.net.SocketException: Connection reset

造成 connection reset 原因很多,本文的问题是因TLS协议问题导致。

JDK1.7与1.8在TLS协议方面的区别?

jdk1.7 默认是TLSv1,但支持TLSv1.1、TLSv1.2

jdk1.8 默认是TLSv1.2.

通过以下代码可以查看受支持的协议(supported protocols)和启用的协议(enabled protocols),可以从受支持的协议中进行选择并启用。

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);

SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket();

String[] protocols = socket.getSupportedProtocols();
System.out.println(Arrays.asList(protocols));

protocols = socket.getEnabledProtocols();
System.out.println(Arrays.asList(protocols));

贴一下 Oracle 的一篇blog: Diagnosing TLS, SSL, and HTTPS,摘取几个JDK版本中TLS协议信息:

JDK 8 (March 2014 to present) JDK 7 (July 2011 to present) JDK 6 (2006 to end of public updates 2013)
TLS Protocols TLSv1.2 (default) <br />TLSv1.1<br />TLSv1<br />SSLv3 TLSv1.2<br />TLSv1.1<br />TLSv1 (default) <br />SSLv3 TLS v1.1 (JDK 6 update 111 and above) <br />TLSv1 (default) <br />SSLv3

为什么-Dhttps.protocols=TLSv1.2不生效?

最开始就怀疑是TLS协议问题,但因设置该系统参数无效,导致忽略了这个因子,最后却证实依然是这个问题。那为什么这个参数不生效?

参考 Setting TLSv1.2 in https.protocols not workingDiagnosing TLS, SSL, and HTTPS

发现 https.protocols 环境变量只对 HttpsURLConnection 有效,下面是两个相关参数:

  • javax.net.debug

    打印connection相关信息,例如: -Djavax.net.debug=all-Djavax.net.debug=ssl:handshake

  • https.protocols

    控制使用 HttpsURLConnectionURL.openStream() 获取https连接的Java Clients 的协议版本。

    例如:-Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2

为什么OkHttp在jdk1.7没问题?

后续我又用了OkHttp替换Http Client,发现一切OK。OkHttp默认会使用TLSv1.2。OkHttp只知道但没用过,看了下发现使用还挺方便,如果是新项目,可以使用OkHttp替换Http Client。

关于 TLS1.0、1.1、1.2、1.3

数据来自 PCI DSS合规标准:禁用不安全的TLS 1.0

TLS各版本的信息稍微了解一下。

  • TLS 1.0于1999年发行,至今将近有20年。对于目前的互联网技术,TLS 1.0的存在可以说就是一种安全隐患。因为TLS 1.0易受各种攻击(如BEAST和POODLE)已有多年,除此之外,支持较弱加密,对当今网络连接的安全已失去应有的保护效力。因此,从去年开始,众多平台、安全企业纷纷放弃。
  • 2017年的年中,微软强烈建议企业、及其客户或者合作伙伴禁用已经出现问题的老旧版本TLS 1.0及TLS 1.1。
  • 2018年2月,GitHub停止支持弱加密标准,其中就包括弃用TLS 1.0及1.1协议。
  • 2018年4月1日,DigiCert禁用TLS 1.0/1.1,只支持TLS 1.2和更高版本。
  • 2018年6月21日,GlobalSign 将禁用 TL1.0 和 TLS1.1
  • 2018年6月30日,PCI 安全标准要求各大网站停止支持TLS1.0
  • 2018年8月10日,互联网工程任务组(IETF)发布了最新版本的传输层安全TLS——TLS 1.3,相比之前所有的版本,TLS 1.3顺应了目前互联网的需求,在安全性和响应速度性能方面作了更进一步的提升。TLS 1.3的出现,将会加快淘汰TLS 1.0的速度。

防人之心不可无

对于小团队来说,一方面由于安全意识薄弱,且没有专门的安全团队,精力全部扑在业务上;另一方面,这些团队的产品基本也不在攻击范围之内,没有什么攻击价值。

但没有发生危险不代表没有危险,该来的总会来。因此,对于开展的新项目,还是禁用TLS1.0、1.1,只使用TLS1.2,迎接TLS1.3的普及!


欢迎关注陈同学的公众号,一起学习,一起成长

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容