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),对发现逻辑漏洞帮助不大,没有耐心的我就先略过算法细节分析。

  1. bool TransportSecurityState::GetDynamicSTSState(const std::string& host,
  2. STSState* result) {
  3. DCHECK(CalledOnValidThread());
  4.  
  5. const std::string canonicalized_host = CanonicalizeHost(host);
  6. if (canonicalized_host.empty())
  7. return false;
  8.  
  9. base::Time current_time(base::Time::Now());
  10.  
  11. for (size_t i = ; canonicalized_host[i]; i += canonicalized_host[i] + ) {
  12. std::string host_sub_chunk(&canonicalized_host[i],
  13. canonicalized_host.size() - i);
  14. STSStateMap::iterator j = enabled_sts_hosts_.find(HashHost(host_sub_chunk));
  15. if (j == enabled_sts_hosts_.end())
  16. continue;
  17.  
  18. // If the entry is invalid, drop it.
  19. if (current_time > j->second.expiry) {
  20. enabled_sts_hosts_.erase(j);
  21. DirtyNotify();
  22. continue;
  23. }
  24.  
  25. // If this is the most specific STS match, add it to the result. Note: a STS
  26. // entry at a more specific domain overrides a less specific domain whether
  27. // or not |include_subdomains| is set.
  28. if (current_time <= j->second.expiry) {
  29. if (i == || j->second.include_subdomains) {
  30. *result = j->second;
  31. result->domain = DNSDomainToString(host_sub_chunk);
  32. return true;
  33. }
  34.  
  35. break;
  36. }
  37. }
  38.  
  39. return false;
  40. }

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. Delphi 中Format的字符串格式化使用说明(转)

    源:Delphi 中Format的字符串格式化使用说明(转) 一.Format函数的用法 Format是一个很常用,却又似乎很烦的方法,本人试图对这个方法的帮助进行一些翻译,让它有一个完整的概貌,以供 ...

  2. css(一)-- 概述以及引入方式

    概述 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言.CSS不仅可 ...

  3. python web开发基本概念

    参考了廖雪峰的Python博客. web请求顺序: 浏览器发送一个http请求 服务器收到请求后,生成一个html文档. 服务器将html文档作为http相应的body发送给浏览器 浏览器收到http ...

  4. tools_list

    http://files.cnblogs.com/files/yansc/ExportQingtaoImage.rar

  5. leetcode--003 LRU cache

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABHAAAACmCAIAAAA9PO+sAAAgAElEQVR4nO3du3HbytvH8X8zqoB12A ...

  6. 前段验证框架 formValidator

    http://blog.csdn.net/lorinzhang/article/details/7283557

  7. select into from 和 insert into select 的用法

    SELECT INTO 和 INSERT INTO SELECT 两种表复制语句 Insert是T-sql中常用语句,Insert INTO table(field1,field2,...) valu ...

  8. Ajax Not Found,asp.net mvc 中

    x前台代码: <script type="text/javascript"> $(document).ready(function () { $("#btnS ...

  9. 国民身份证号码校验之“C#/Winform方法实现+案例分析”

    根据[中华人民共和国国家标准 GB 11643-1999]中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成.排列顺序从左至右依次为:六位数字地址码,八位数字出 ...

  10. HDU-1864-最大报销额

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1864 这题开始题意没搞清楚,就做题了,导致浪费了很多的时间,不应该啊, 注意事项:每张发票上,单项物品 ...