@EricaHe
2015-08-27T14:38:51.000000Z
字数 6823
阅读 1028
论文笔记
SSL
作者:Martin Georgiev,Subodh Iyengar,Suman Jana,Rishita Anubhai,Dan Boneh,Vitaly Shmatikov
单位:The University of Texas at Austin,Stanford University
在许多要求高安全性的应用和库中,SSL都没能被很好的实施,使得其安全性下降,这之中的根本原因在于一些SSL的API设计非常糟糕,给了程序员许多令人困惑的设置和选项,从而使得程序员对许多参数、选项、返回值等等该如何设置产生了误解。论文主要研究了在一些无浏览器的软件中SSL的使用情况,并重点关注了客户端对服务器端的认证,对各类库错误使用SSL的情况进行了具体地列举,在最后也给出了一些意见与建议。
一个活跃的中间人攻击者
本文主要考虑以下2个方面:
OpenSSL
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的中一个不起眼的位置看到关于这一点的提醒。
Apache HttpClient
Weberknecht
cURL
PHP
Python
Cloud Client APIs:
各种云端服务,如将用户的数据储存到云端等
商业支付SDKs:
电子商务网站等,如PayPal和亚马逊快捷支付服务之间的通信,这些都是在服务器后端通过SSL通信完成的
Web-services Middleware
手机广告:
AdMob服务,显示的广告与app提供商的账户绑定(从而获得广告收入等),app提供商的站点与AdMob服务器的通信因为包含有提供商的凭据,所以必须用SSL保护。
实施中间人攻击的服务器有两个:一个是由Java写成的,用的是JKS keystore来管理攻击者的证书和密钥;另一个则由C写成,使用OpenSSL来认证和管理密钥。
Fiddler被用作代理来获取连接请求。
如果出现了一个从未获得过的连接请求,则创建一个新的证书,其common name与所要求的名字相一致,并将它添加到仓库;否则就直接使用已有的。
中间人使用了几份不同的证书:
1. 一个自签名的证书,common name与host和client尝试传输的相同
2. 一个自签名的证书,但common name是不正确的
3. 一个合法的证书,由一个被信任的CA颁发给一个叫AllYourSSLAreBelongTo.us的域名
如果使用以上任何一个证书,能够与客户端成功建立起SSL连接,则需要进一步的分析与调查。
在src\Amazon\FOPS\Client.php文件中存在以下代码:
curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curlHandle, CURLOPT_SSL_VERIFYHOST, true);
...
// Execute the request
...
正如之前提到的,CURLOPT_SSL_VERIFYHOST应该被设置为2,而不是true,后者会被程序解读为1,从而导致没有能够检查获得的名字与主机名是否匹配。
这样的问题在src\Amazon\IpnReturnUrlValidation\SignatureUtilsForOutbond.php也有暴露,使得URL认证功能被破坏。
一开始,PayPal Payments Standard SDK关闭了所有验证:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
...
// Execute the request
...
后来他们修正了这个问题,然而依然不对:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);
...
// Execute the request
...
这个问题就和上面一样,在PayPal Invoicing也有相类似的错误:
public function setHttpTrustAllConnection($trustAllConnection)
{
$this->curlOpt[CURLOPT_SSL_VERIFYPEER] = !$trustAllConnection;
$this->curlOpt[CURLOPT_SSL_VERIFYHOST] = !$trustAllConnection;
}
$curlOpts = array( ...
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_SSL_VERIFYHOST => 2
虽然CURLOPT_SSL_VERIFYHOST设置对了,但在CURLOPT_SSL_VERIFYPEER被设置为FALSE的情况下,CURLOPT_SSL_VERIFYHOST不起到任何作用。
ret = gnutls_certificate_verify_peers2(handle->gnutls_state,
&tls_status);
if (ret < 0) {
int flag_continue = 1;
char *msg2;
if (tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
msg2 = gettext("no issuer is found");
} else if (tls_status & GNUTLS_CERT_SIGNER_NOT_CA) {
msg2 = gettext("issuer is not a CA");
} else if (tls_status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
msg2 = gettext("the certificate has no known issuer");
} else if (tls_status & GNUTLS_CERT_REVOKED) {
msg2 = gettext("the certification has been revoked");
} else {
msg2 = gettext("teh certificate is not trusted");
}
...
}
Lynx使用了GnuTLS来认证SSL证书,而GnuTLS中的一个函数,gnutls_certificate_verify_peers2的返回值情况非常复杂,从而造成了错误。
这个函数,如果是证书认证失败(如证书不完全或者没有找到证书),会通过设置tls_status的值来表明,并返回一个负值,但是对于一些特定的错误(如自签名证书等等),这个函数会返回0,从而导致这一段代码被跳过。
另外,尽管这一段代码对GNUTLS_CERT_SIGNER_NOT_FOUND进行了2遍一模一样的检查,但当该值是true时,没有一次检查能够被成功执行。
Apache HttpClient3.1是目前使用最广泛的版本,而在这个版本中,它使用了JSSE的SSLSocketFactory,并且没有自行对主机名进行验证。
在后来的4.*版本中修复了这一问题,然而依然存在问题。
这两点使得Trillian会接受任何的SSL证书,无法抵御中间人攻击。
Rackspace是一个iOS的开源应用。它使用了OpenStack iOS cloud client framework来确立HTTPS连接。
TextSecure是一个安卓应用,用来加密SMS和MMS消息。
它在443端口试图使用https时,用了DEFAULT_SCHEME_NAME变量,然而在该段代码中,这个变量是“http”而不是“https”,这使得连接时信息是通过http传递,而不是https(虽然目前可能无法利用这个漏洞)。
用了SSLSocketFactory却没有自行对主机名进行认证。
Pusher的安卓库中有Weberknecht,这个在之前的内容中有提到过,Weberknecht也是用了SSLSocketFactory却没有自行对主机名进行认证的,所以这种错误也被继承了下来。
在这之中必须注意,任何使用了任意上述Web-service框架的软件都无法抵御中间人攻击。
这一块主要是对于fsockopen的误用,在之前谈PHP的时候也提到过。
举例:
// post back to PayPal utility to validate
...
$fp = fsockopen('ssl://www.paypal.com', 443, $errno, $errstr, 30);
这是ZenCart中使用PayPal支付模块的代码,在许多其他购物卡或应用的支付过程中也有出现,开发者并没有意识到fsockopen不包含任何认证过程。
Python中的问题也在很多应用中有体现。
这个与其说是技术上的问题,更多的是来源于开发者自身安全意识的薄弱,许多人在开发过程中,在SSL上碰到一些问题,而往往在这个时候,关闭证书认证成了最简单的解决方式。
除此之外还有一些因为疏忽而造成的问题,使得证书认证环节被破坏。
Chase手机银行
在进行检查之前就return了。
Apache Libcloud
正则表达错误,使得oogle.com可以和google.com相匹配
Amazon Elastic Load Balancing API Tools
Shopping carts
osCommerce, ZenCart, Ubercart, PrestaShop这些几乎都取消了证书认证。只有osCommerce和PrestaShop的Google模块是正确的。
AdMob
Google的AdMob提供了示例代码,然而在示例代码中就关闭了证书认证功能。中间人攻击者从而可以获得所有开发者的谷歌服务。
一些Android应用
Groupon Redemption使证书认证失效了2次:hostname verifier是“allow all”,并且绑定了一份空白的trust manager。
Breezy,使主机名认证失效并重写了trust manager
ACRA,一个Android库,重写了trust manager,所有使用了该库的app都无法抵御中间人攻击。
AIM
FilesAnywhere
在SSL连接中接受自签名或者第三方签名的证书
论文证明了许多标准SSL库经常错误地实现SSL证书认证,甚至根本没有认证,这些错误被许多对安全有着极高要求的应用和软件所继承,使得这些应用或软件无法抵御中间人攻击
论文也对如何更加安全地在非浏览器的软件中使用SSL连接提出了一些建议:如1)进行更完善的黑盒测试和代码分析;2)设计更加严谨的认证技术和代码语言,从而使得自动检查SSL使用的正确性得到实现,并且减少对于一些重要参数和设置的误读;3)为SSL和其他网络安全协议设计更好的API。