HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(二)

HTTP strict transport security (HSTS) is defined in
http://tools.ietf.org/html/ietf-websec-strict-transport-sec

HTTP-based dynamic public key pinning (HPKP) is defined in
http://tools.ietf.org/html/ietf-websec-key-pinning.

深入理解需参考chromuim设计文档:

https://www.chromium.org/developers/design-documents

(中文版)https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//General_Architecture/Threading.html

chromuim在线源码:https://chromium.googlesource.com/chromium/src/+/58.0.3025.2/

------------------------------------------------分割线---------------------------------------------------------------------------------

通过观察文件名,发现涉及的文件主要是net目录下的:

Transport_security_persister.cc

Transport_security_persister.h

Transport_security_state.cc

Transport_security_state.h

Url_request_http_job.cc

在开始之前,需要从源码层面了解chromuim的网络栈部分,可参考《WebKit技术内幕》作者朱永盛的博文http://blog.csdn.net/milado_nju/article/details/9255563以及官网网络栈部分的文档http://www.chromium.org/developers/design-documents/network-stack。简单来说,发送网络请求基本都是通过URLRequest类,再根据不同协议选择不同的工厂,如HTTP为URLRequestHttpJob,由于HSTS针对HTTP,所以也只需关注URLRequestHttpJob;另一个重要的类是URLRequestContext,它包含其他完成URL请求的上下文信息,如cookie、主机解析、缓存以及HSTS信息等;许多URLRequest对象共享一个URLRequestContext。本文采用好理解的“自上而下”顺序来进行总结分析,但是实际中由于作者对chromuim完全没有认识,其实是从源码中搜索关键词看注释再查找引用一步一步摸索的。

类URLRequestHttpJob的Factory方法在创建实例前,调用了Url_request_http_job.cc中的MaybeInternallyRedirect()函数对是否要进行升级HSTS进行了判断,通过名字可以猜出chromuim是采用内部重定向的方式来实现HTTP到HTTPS升级的,这跟使用开发者工具调试时观察到的请求一致。

MaybeInternallyRedirect函数中,根据request->url()来判断该域名是否应该升级HSTS,若hsts->ShouldUpgradeToSSL返回false,就返回nullptr,不用重定向;否则,使用Replacements类,根据request->url()的协议,来进行替换为https或wss(即websocket),再返回307状态码的URLRequestRedirectJob类。因此,URLRequestHttpJob返回URLRequestRedirectJob而非正常的URLRequestHttpJob,进行重定向来升级协议。由此可见,hsts的ShouldUpgradeToSSL方法就至关重要了,是否升级https全看它的返回值了。

hsts是从request->context()->transport_security_state()取出,通过查看源码,即是URLRequest中的URLRequestContext指针类型变量context_中的TransportSecurityState*类型的transport_security_state变量。这句话有点绕,其实就是从URLRequestContext取出了保存HSTS信息的变量,该变量(260行)类型为TransportSecurityState*,TransportSecurityState就是实现HSTS机制的重点了。另外,源码指针的实现也呼应了开始提到的多URLRequest对一个URLRequestContext。

Transport_security_state.h中可以看到TransportSecurityState的定义,它驻留在内存中追踪哪个域名启用了HSTS和PKP,并且用SetDelegate方法注册了一个代理来存储状态到硬盘中。为了解一个类,发现从其对应的单元测试文件可以得到一个整体的感性认识,单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。从单元测试文件Transport_security_state_unittest.cc中可以看出该类的简单用法,以及作者已经考虑到的一些避免HSTS被绕过的情况。比如这里就考虑了域名末尾加“.”和不加“.”在本策略中是一样的。并且,除了subdomain选项和preload外, 还有一些极端情况,如下所述。(Google C++ Testing Framework可参考这个网址http://developer.51cto.com/art/201108/285290.htm)

(1)防止拒绝服务的产生(比如添加一个域名为“.”的情况),

(2)对大小写不敏感(Google.com与GooGLe.CoM是一样的),

(3)规则冲突覆盖以更具体的为准(example.com的subdomain为true,而foo.example.com的subdomain为false,那么sub.foo.example.com的HSTS情况应为false;这点浏览器的实现情况应引起网站管理员的注意,可测试网站有没有这样的漏洞,RFC标准里我不记得提到),

(4)若设置了subdomain,不规范子域名也应使用HSTS(如2\x01.foo.example.test返回true),

(5)域名删除的情况。

下面的这个810行的测试没看懂,为何exampl1.com一会儿期望为true,一会儿为false?猜想可能是在测试单元测试功能有没有正常工作吧。

--------------------------------------------------分割线-------------------------------------------------

由单元测试和之前的源码分析以及源码中的注释可以得到,TransportSecurityState的ShouldUpgradeToSSL是判断该URL是否应该升级HTTPS的方法。查看ShouldUpgradeToSSL方法,发现是根据STSState类型变量的ShouldUpgradeToSSL()来返回布尔值的,从名称看依次检查了动态HSTS和预置的HSTS(即preload,如www.google.com已经预置在浏览器里了)。

先看类STSState,他是一个内部类,实现非常简单,注释说明它描述了HSTS的状态,属性包括过期时间、include_subdomains标识、域名等,根据host来由GetDynamicSTSState和GetStaticDomainState更新。首先来看GetDynamicSTSState,该函数根据host查询保存的信息,来对result赋值,并且特别强调了以更具体的结果为准;通过源码,其进行了host的规范化,然后用迭代器和enabled_sts_hosts_以及hash后的host(其实本地存储的HSTS信息文件中域名只有哈希后的值,为了隐私)来进行查询,并判断了查询结果中当前时间是否超过了过期时间,对结果result进行赋值为j->second,这样STSState就有值可以判断ShouldUpgradeToSSL了。ShouldUpgradeToSSL方法仅仅是比较upgrade_mode属性是否为MODE_FORCE_HTTPS。由此可见,STSStateMap类型的enabled_sts_hosts_就是本地维护HSTS信息的变量了,由此将我们引向了STSStateMap类。其实,若要发现实现细节方面的漏洞就需要详细看实现查找的算法了(CanonicalizeHost),对发现逻辑漏洞帮助不大,没有耐心的我就先略过算法细节分析。

bool TransportSecurityState::GetDynamicSTSState(const std::string& host,
STSState* result) {
DCHECK(CalledOnValidThread()); const std::string canonicalized_host = CanonicalizeHost(host);
if (canonicalized_host.empty())
return false; base::Time current_time(base::Time::Now()); for (size_t i = ; canonicalized_host[i]; i += canonicalized_host[i] + ) {
std::string host_sub_chunk(&canonicalized_host[i],
canonicalized_host.size() - i);
STSStateMap::iterator j = enabled_sts_hosts_.find(HashHost(host_sub_chunk));
if (j == enabled_sts_hosts_.end())
continue; // If the entry is invalid, drop it.
if (current_time > j->second.expiry) {
enabled_sts_hosts_.erase(j);
DirtyNotify();
continue;
} // If this is the most specific STS match, add it to the result. Note: a STS
// entry at a more specific domain overrides a less specific domain whether
// or not |include_subdomains| is set.
if (current_time <= j->second.expiry) {
if (i == || j->second.include_subdomains) {
*result = j->second;
result->domain = DNSDomainToString(host_sub_chunk);
return true;
} break;
}
} return false;
}

STSStateMap并不神秘,其实就是map<std::string, STSState>。那么具体是哪些函数对这个信息进行维护呢,通过对enabled_sts_hosts_查找引用,共12处,分布在EnableSTSHost、DeleteDynamicDataForHost、ClearDynamicData、DeleteAllDynamicDataSince、AddOrUpdateEnabledSTSHosts。

EnableSTSHost首先对host进行规范化,然后根据state.ShouldUpgradeToSSL()是否应该升级https来决定是存入信息还是删除信息,存入的时候可以看到,保存的不是host,而是哈希后的host;该方法中已经使用了ShouldUpgradeToSSL,可见state内已经有信息,这一步只是进行存储的操作,还需要查看引用是谁调用EnableSTSHost传入了state。通过查找引用,发现是AddHSTSInternal方法,决定ShouldUpgradeToSSL返回值的upgrade_mode来自于该方法的参数,还需要继续寻找调用者。(感觉这俩方法合并效率会更高些)AddHSTSHeader根据解析HSTS头的ParseHSTSHeader函数返回的max_age变量InSeconds()==0来判断是否应该强制升级HTTPS,这种时间等于0的做法容易产生问题,负数、溢出是否影响?判断有无这种漏洞就再次需要详细看实现算法细节了。

URLRequestHttpJob::ProcessStrictTransportSecurityHeader调用了AddHSTSHeader,实现中可以看到,浏览器仅接受HTTPS且没有证书错误的HSTS,从而避免恶意服务器拒绝服务其他域名;域名是IP地址也不会接受HSTS;有多个HSTS头则只接受第一个。注意到传入AddHSTSHeader作为host参数的是request_info_.url.host(),而这些变量不是通过参数传入而是类自身的变量,所以没必要再继续向上追。(当然缺乏经验的我肯定会犯错,向上追了好几步。ProcessStrictTransportSecurityHeader()被URLRequestHttpJob::NotifyHeadersComplete()调用,而调用NotifyHeadersComplete()的地方查找调用看似很多,好像处理头部时都会调用,只要注意限制在URLRequestHttpJob类,就发现其实只有一个调用者,SaveCookiesAndNotifyHeadersComplete,再网上追查两步就到了URLRequestHttpJob类的回调机制。但由于是检查HSTS头部时检查的ssl证书等信息,并且不存在其他路径到添加添加HSTS信息的函数,所以攻击者也难以通过别的通道来拒绝服务其他域名;本文只关心HSTS带来的问题,故没有必要再向上追寻,要寻找拒绝服务攻击可能性,关键就在于传入AddHSTSHeader的值了)

如果这个request_info_.url变量和ssl_info变量出问题:规范化后域名canonicalized_host哈希值碰撞(采用的SHA256几乎不可破)、和当前域名对应错误(若这个有问题那么cookie什么的就可以全乱套了,感觉出问题几率应该不大)、规范化后的域名和其他域名能一致(好多数据结构要看);则攻击者就可以操作其他域名HSTS信息。URLRequestHttpJob中request_info_.url是URLRequestHttpJob::Start()中赋值request_info_.url = request_->url();request_继承自父类URLRequestJob,指向了创建这个job的URLRequest。

到此为止,我们对HSTS实现的基本流程有了一定了解,但这仅仅是对管理enabled_sts_hosts_的EnableSTSHost函数追踪所学习到的,还有DeleteDynamicDataForHost、ClearDynamicData、DeleteAllDynamicDataSince、AddOrUpdateEnabledSTSHosts还有待继续学习。最后总结出如下两个图:

By Ascii0x03

转载请注明出处:http://www.cnblogs.com/ascii0x03/p/6375825.html

HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(一)的更多相关文章

  1. HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(二)

    HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(一) 下面来查看其他对保存HSTS信息的enabled_sts_hosts ...

  2. Nginx-HTTP Strict Transport Security(HSTS)

    HTTP Strict Transport Security(HSTS) HTTP Strict Transport Security(通常简称为HSTS)是一个安全功能,它告诉浏览器只能通过HTTP ...

  3. HTTP Strict Transport Security (HSTS) in ASP.NET Core

    本文是<9012年了,还不会Https>的后篇,本文着重聊一聊 HTTP Strict Transport Security协议的概念和应用. 启用HTTPS还不够安全 站点通过HTTPS ...

  4. Spring Security(四) —— 核心过滤器源码分析

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...

  5. HTTP Strict Transport Security实战详解

    HTTP Strict Transport Security (通常简称为HSTS) 是一个安全功能,它告诉浏览器只能通过HTTPS访问当前资源, 禁止HTTP方式. 0×01. Freebuf百科: ...

  6. nginx配置Strict Transport Security

    一个网站接受一个HTTP的请求,然后跳转到HTTPS,用户可能在开始跳转前,通过没有加密的方式和服务器对话,比如,用户输入http://zt.test.com或者直接zt.test.com.这样存在中 ...

  7. HTTP Strict Transport Security (通常简称为HSTS)

    HTTP Strict Transport Security (通常简称为HSTS) 是一个安全功能,它告诉浏览器只能通过HTTPS访问当前资源, 禁止HTTP方式. Freebuf百科:什么是Str ...

  8. HTTP Strict Transport Security

    HTTP Strict Transport Security (通常简称为HSTS) 是一个安全功能,它告诉浏览器只能通过HTTPS访问当前资源, 禁止HTTP方式. 作用 一个网站接受一个HTTP的 ...

  9. Spring Security(3):配置与自动配置的介绍及源码分析

    基于注解的配置(Java Configuration)从Spring Security 3.2开始就已经支持,本篇基于Spring boot注解的配置进行讲解,如果需要基于XML配置(Security ...

随机推荐

  1. [SQLite]SQL语法

    SQLite常用SQL语句 创建表格 sql="CREATE TABLE IF NOT EXISTS MusicList (id integer primary key AutoIncrem ...

  2. webservice-概念性学习(一)

    以下是本人原创,如若转载和使用请注明转载地址.本博客信息切勿用于商业,可以个人使用,若喜欢我的博客,请关注我,谢谢!博客地址 学习webservice之前呢,我想说我们先学习以下的知识,对你以后的学习 ...

  3. 第三弹:ZFNet

    在所有深度网络中,卷积神经网和图像处理最为密切相关,卷积网在很多图片分类竞赛中都取得了很好的效果,但卷积网调参过程很不直观,很多时候都是碰运气.为此,卷积网发明者Yann LeCun的得意门生Matt ...

  4. Python字符串的encode与decode研究心得——解决乱码问题

    转~Python字符串的encode与decode研究心得——解决乱码问题 为什么Python使用过程中会出现各式各样的乱码问题,明明是中文字符却显示成“/xe4/xb8/xad/xe6/x96/x8 ...

  5. 如何在我自己的web 项目的jsp页面中添加链接,直接让别人通过内网在我的电脑上下载文件

    今天接到一个任务,将昨天年会的视频,音频,图片等放在公司自己的服务器上,使连接同一个路由器的(即同一个内网)的同事可以通过内网下载视频(通过内网下载,可以提高下载速度). 备注:本次用的是tomcat ...

  6. 【转】实用 .htaccess 用法大全

    这里收集的是各种实用的 .htaccess 代码片段,你能想到的用法几乎全在这里. 免责声明: 虽然将这些代码片段直接拷贝到你的 .htaccess 文件里,绝大多数情况下都是好用的,但也有极个别情况 ...

  7. lua 函数

    1.函数只有一个参数,且该参数为table 或 字符串时,调用函数可以省略() print"hello world" 同 print("hello world" ...

  8. MS SQL Server数据库修复/MDF数据文件数据恢复/MDF质疑/mdf无法附加

    微软的SQL Server 数据库最常用的有两种类型的文件: 1.主要数据文件,文件后缀一般是.MDF: 2.事务日志文件,文件后缀一般是.LDF. 用户数据表.视图.存储过程等等数据,都是存放在MD ...

  9. Elasticsearch基础教程

    Reference: http://blog.csdn.net/cnweike/article/details/33736429 基础概念 Elasticsearch有几个核心概念.从一开始理解这些概 ...

  10. java设计模式笔记(1)-适配器模式

    适配器的定义 适配器就是一个接口转换器,它可以是一个独立的硬件接口设备,允许硬件或电子接口与其它硬件或电子接口相连,也可以是信息接口.比如:电源适配器.三角架基座转接部件.USB与串口的转接设备等. ...