The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software(Simplified ver.)
论文笔记
SSL
作者:Martin Georgiev,Subodh Iyengar,Suman Jana,Rishita Anubhai,Dan Boneh,Vitaly Shmatikov
单位:The University of Texas at Austin,Stanford University
Abstract & Introduction
在许多要求高安全性的应用和库中,SSL都没能被很好的实施,使得其安全性下降,这之中的根本原因在于一些SSL的API设计非常糟糕,给了程序员许多令人困惑的设置和选项,从而使得程序员对许多参数、选项、返回值等等该如何设置产生了误解。论文主要研究了在一些无浏览器的软件中SSL的使用情况,并重点关注了客户端对服务器端的认证,对各类库错误使用SSL的情况进行了具体地列举,在最后也给出了一些意见与建议。
Overview of SSL
1. 威胁模型
一个活跃的中间人攻击者
- 攻击者无法获得合法服务器的私钥
- 攻击者无法控制任何CA
- 攻击者无法伪造证书
2. SSL证书认证
本文主要考虑以下2个方面:
SSL Abstractions
1. SSL Libraries
OpenSSL
- OpenSSL只提供证书链验证,主机名验证环节由应用自己,或一些data-transport包完成
- 证书链的验证可以通过回调函数或者一些设定参数(如"verify depth"和"verify mode")进行自定义
- OpenSSL存在函数返回值上的混乱。如,在ssl_connect这一函数(用来建立握手)中,有一部分的错误是通过ssl_connect的返回值来表达的,而有一部分错误ssl_connect会返回OK,具体错误必须通过ssl_get_verify_result函数来获得,(这一点在GnuTL中也有相类似的体现)
JSSE
JSSE中有一个底层的API叫SSLSocketFactory,它会根据SSL客户端设定的不同而采用不同方式来进行主机名验证。
在这个API中有一个函数叫checkIdentity,是用来进行主机名验证的,该函数会通过参数algorithm,来决定采用HTTPS还是LDAP进行操作,如果参数algorithm为NULL或为空,则跳过该步骤,并且不产生任何的提示。
于是这就产生了一个问题,尽管在HttpsClient和HttpsURLConnection在创建SSL客户端时会要求调用SetHostnameVerification来设定algorithm参数,一些应用软件通过原始的SSLSocketFactory函数来创建SSL客户端时,algorithm参数默认是NULL,于是主机名验证就被静默跳过了,而这一点却没有在API的文档中提出,只在JSSE reference guide的中一个不起眼的位置看到关于这一点的提醒。
2. Data-transporter libraries
Apache HttpClient
- 使用了JSSE中的SSLSocketFactory来建立SSL连接
- 在描述HTTP(S)连接时,使用了HttpHost的数据结构
Weberknecht
- 使用了SSLSocketFactory,却没有自行进行主机名验证
cURL
- 参数类型混乱。如,VERIFYPEER参数是一个布尔值,而另一个与其非常相似的VERIFYHOST参数却是一个整数值(1是检查SSL证书中一个common name是否存在,2是不仅仅检查,并且验证这是否与提供的主机名相匹配)。即便开发者没有对这些参数的作用产生误读,但却很有可能把VERIFYHOST设置为TRUE,而这就会被程序理解为1,从而产生灾难性的后果。
PHP
- fsockopen函数不包含任何的证书检查功能,仅仅只是创建了一个可用于SSL连接的socket,却经常被开发者用来建立SSL连接
- PHP提供了cURL的默认设置来创建SSL连接,这个应该是安全的,但开发者经常错误地对cURL进行设置,覆盖了原有的默认值,从而破坏了证书认证过程。
Python
- urllib,urllib2和httplib可以连接SSL服务器,但并没有检查证书,尽管这一点在python文档中的urllib页面上被高亮指出,许多开发者依然在一些要求高安全性的应用中使用它们
- python也有ssl模块,但它仅仅只检查证书链,却不会对主机名进行检验,应用必须自行完成这一内容。而在python3中,ssl模块引入了match_hostname这一方法,但该方法必须被显式地调用才可以。
Experimental Testbed
- 一个自签名的证书,common name与host和client尝试传输的相同
- 一个自签名的证书,但common name是不正确的
- 一个合法的证书,由一个被信任的CA颁发给一个叫AllYourSSLAreBelongTo.us的域名
如果使用以上任何一个证书,能够与客户端成功建立起SSL连接,则需要进一步的分析与调查。
对于实验结果而言,基本可分为以下几类
- Misunderstanding the SSL API
- Using Insecure Middleware
- Using Insecure SSL Libraries
- Breaking Or Disabling Certificate Validation
Suggestion
For application developers
- 使用fuzzing和adversarial testing,从而了解应用获得错误的SSL证书时的表现
- 在测试自签名和不可信的证书时,不要修改代码使得证书认证失效,以防忘记改回来
- 如果想要安全地建立起SSL连接,不要去依赖库的默认值,总是显式地进行安全设置
For SSL library developers
- 使SSL库的API更加意图明确,语义清晰
- 不要将管理SSL连接的责任推给应用
- 设计一份清晰的、前后一致的错误信息接口
Conclusion
论文证明了许多标准SSL库经常错误地实现SSL证书认证,甚至根本没有认证,这些错误被许多对安全有着极高要求的应用和软件所继承,使得这些应用或软件无法抵御中间人攻击
论文也对如何更加安全地在非浏览器的软件中使用SSL连接提出了一些建议:如1)进行更完善的黑盒测试和代码分析;2)设计更加严谨的认证技术和代码语言,从而使得自动检查SSL使用的正确性得到实现,并且减少对于一些重要参数和设置的误读;3)为SSL和其他网络安全协议设计更好的API。