云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

服务器_腾讯云招聘_排行榜

小七 141 0

我们如何将HTTPS带到云端(第1部分)

CloudFlare的任务是让我们所有的客户都能访问HTTPS。它为他们的网站提供了安全性,提高了搜索引擎的排名,提高了HTTP/2的性能,并提供了对诸如地理位置等浏览器功能的访问,而这些功能正被弃用于纯文本HTTP。有了通用SSL或类似功能,现在只需单击一下按钮就可以对网站进行加密。不幸的是,正如前一篇博文中所描述的,这只是问题的一半。为了确保页面是安全的并且不能被第三方控制或窃听,浏览器必须确保不仅页面本身,而且它的所有依赖项都通过安全通道加载。不满足此要求的页面元素称为混合内容,可能导致整个页面被报告为不安全,甚至完全被阻止,从而破坏最终用户的页面。我们能做些什么?当我们构思自动HTTPS重写项目时,我们的目标是在不破坏客户网站的情况下自动减少客户网页上混合内容的数量,并且在接收到正在动态重写的页面时,不会出现终端用户明显的延迟。一种简单的方法是将链接重写为https://或者让浏览器使用Upgrade-insecurerequests指令来完成。不幸的是,这种方法是非常脆弱和不安全的,除非你确定每个单独的HTTP子资源也可以通过HTTPS获得。在协议升级后,它可以在完全相同的域和路径上使用(通常情况下,您可能认为情况并非如此)。如果这两个条件中的任何一个未得到满足,那么最终会将资源重写为不存在的url并破坏重要的页面依赖关系。因此,我们决定看看现有的解决方案。这些问题是如何解决的?许多安全意识强的人使用HTTPS Everywhere浏览器扩展来避免这类问题。HTTPS Everywhere包含一个由Electronic Frontier Foundation维护良好的数据库,该数据库包含流行网站的各种映射,这些网站只有在不破坏页面的情况下才能安全地将资源的HTTP版本重写为HTTPS。然而,大多数用户要么不知道,要么甚至不能使用它,例如在移动浏览器上。CC BY 2.0图片作者:Jared Tarbell所以我们决定改变一下模型。我们将在CloudFlare反向代理中重新编写url,而不是在浏览器中重新编写url。通过利用服务器端现有的数据库,网站所有者可以打开它,他们的所有用户都将立即从HTTPS重写中获益。事实上,它是自动的,这一点特别有用,对于那些有用户生成内容的网站来说,查找和修复所有插入的不安全的第三方内容的情况并非易事。在我们的规模下,我们显然不能使用现有的JavaScript重写器。浏览器扩展在用户打开网站时可以懒散地查找、匹配和缓存规则,其性能挑战与每秒处理数百万请求的CDN服务器的性能挑战截然不同。我们通常也没有机会在它们到达缓存之前重写它们,因为许多页面是在源服务器上动态生成的,并直接通过我们到达客户机。这意味着,为了利用数据库,我们需要了解现有的实现是如何工作的,并以本机库的形式创建我们自己的实现,在我们的负载下可以毫无延迟地工作。让我们在这里做同样的事情。HTTPS Everywhere如何知道要重写什么?HTTPS Everywhere规则集可以在官方存储库的src/chrome/content/rules文件夹中找到。它们被组织成XML文件,每个文件都有自己的主机集(很少有例外)。这允许具有基本技术技能的用户自己编写和向数据库提供缺少的规则。每个规则集都是以下结构的XML文件:在编写本文时,HTTPS Everywhere数据库由~22K个此类规则集组成,这些规则集涵盖~113K个域通配符,以及~32K重写规则和排除规则。出于性能原因,我们不能将所有这些规则集xml保存在内存中、遍历节点、检查每个通配符、根据特定的字符串格式执行替换等等。所有这些工作都会导致页面处理的延迟,并增加服务器上的内存消耗。这就是为什么我们必须对每种类型的节点执行一些编译时技巧,以确保从第一个请求开始对任何用户都平滑而快速地重写。让我们遍历这些节点,看看在每个特定情况下可以做些什么。目标域首先,我们得到目标元素,这些元素描述当前规则集可能覆盖的域通配符。如果使用通配符,则可以是左侧或右侧。左侧通配符如*。示例.org包含任何具有示例.org作为后缀-不管你有多少子域级别。右侧通配符示例。*只覆盖一个级别,这样就不会意外捕获具有相同开头但有一个意外域级别的子域。例如,Google规则集使用Google.*通配符,它应该匹配谷歌, 谷歌.ru, 谷歌.es等等,但不是google.mywebsite.com.请注意,由于通配符可能重叠,单个主机可以由多个不同的规则集覆盖,因此应该向重写器提供整个数据库,以便找到正确的替换项。不过,匹配主机名可以立即将所有22000个规则集减少到只有3-5个,我们可以更轻松地处理这些规则集。当然,在运行时逐个匹配通配符是可能的,但是对于~113K个域通配符来说效率非常低(而且,正如我们前面所提到的,一个域可以匹配多个规则集,因此我们甚至不能提前退出)。我们需要找到更好的方法。CC BY 2.0 vige图像我们使用Ragel在代码的其他部分构建快速lexer。Ragel是一个状态机编译器,它接受用自己的语法描述的语法和操作,并以给定的编程语言生成源代码作为输出。我们决定在这里也使用它,并编写了一个脚本,从我们的通配符集生成一个Ragel语法。反过来,Ragel将其转换为状态机的C代码,该状态机能够遍历url的字符,匹配主机,并在每个找到的规则集上调用自定义处理程序。这就引出了另一个有趣的问题。在编写113K个域通配符时,我们有4.7K个域有左通配符,小于200个域通配符有右通配符。Left通配符在状态机(包括正则表达式)中非常昂贵,因为它们在编译过程中会导致DFA空间爆炸,因此Ragel被卡住了10多分钟,没有给出任何结果—试图分析所有的*。添加前缀并合并所有可能的状态,从而生成一个复杂的树。相反,如果我们选择从主机的末尾看,我们可以大大简化状态树(因为现在只需要单独检查200个通配符,而不是4.7K),从而将编译时间缩短到20秒以下。让我们举一个过于简单的例子来理解两者的区别。例如,我们有以下目标通配符(3个左通配符对1个右通配符和1个简单主机):如果我们直接从以下内容构建Ragel状态机:%%{主机;主机部件=(alnum |[\-])+;主要:=(任何+'。谷歌' |任何+'。google.co.uk' |任何+'。谷歌.es' |"谷歌",主持人|'google.com.ua');}%%我们将得到以下状态图:你可以看到,这个图形已经非常复杂,因为每个起始字符,甚至g都是"google"和"and"的显式起始字符谷歌'字符串,仍然需要同时进入any+匹配。即使你已经解析了谷歌。作为主机名的一部分,它仍然可以正确匹配任何给定的通配符google.google.com, google.google.co.公司.英国,谷歌.google.es, 谷歌.tech或者google.com.ua. 这已经放大了状态机的复杂性,我们只举了一个过于简单的例子,这里有三个左通配符。但是,如果我们只是颠倒每个规则,以便从末尾开始输入字符串:%%{主机;主机部件=(alnum |[\-])+;主要:=('艾尔古莫克.' |'库奥克埃尔古格.' |'埃尔古东南部.' |主机部分".elgoog"|'奥莫克埃尔古');}%%我们可以得到更简单的图形,从而显著减少图形构建和匹配时间:所以现在,我们需要做的就是遍历URL中的主机部分,在之后停止,然后从这里向后启动机器。无需浪费内存中的字符串反转,因为Ragel为自定义数据访问表达式提供了getkey指令,我们可以在匹配结束斜杠后使用该指令以反向顺序访问字符。以下是整个过程的动画:在我们匹配了主机名并找到了可能适用的规则集之后,我们需要确保我们不会重写那些不能通过HTTPS访问的url。除外责任排除元素正是为这个目标服务的。在应用任何实际规则之前,重写器需要针对所有排除模式进行测试。否则,存在问题或无法通过HTTPS提供服务的路径将被错误重写,并可能破坏网站。我们不关心匹配的组,也不关心哪一个特定的正则表达式是匹配的,所以作为一个额外的优化,我们不需要逐个检查它们,而是将所有排除模式合并到