第一篇 世界观安全
第1章 我的安全世界观
互联网本来是安全的,自从有了研究安全的人之后,互联网就变得不安全了。
1.1 Web安全简史
起初,研究计算机系统和网络的人,被称为“Hacker”,他们对计算机系统有着深入的理解,因此往往能够发现其中的问题。“Hacker”在中国按照音译,被称为“黑客”。在计算机安全领域,黑客是一群破坏规则、不喜欢被拘束的人,因此总想着能够找到系统的漏洞,以获得一些规则之外的权力。
对于现代计算机系统来说,在用户态的最高权限是root(administrator),也是黑客们最渴望能够获取的系统最高权限。“root”对黑客的吸引,就像大米对老鼠的吸引,美女对色狼的吸引。
不想拿到“root”的黑客,不是好黑客。漏洞利用代码能够帮助黑客们达成这一目标。黑客们使用的漏洞利用代码,被称为“exploit”。在黑客的世界里,有的黑客,精通计算机技术,能自己挖掘漏洞,并编写exploit;而有的黑客,则只对攻击本身感兴趣,对计算机原理和各种编程技术的了解比较粗浅,因此只懂得编译别人的代码,自己并没有动手能力,这种黑客被称为“Script Kids”,即“脚本小子”。在现实世界里,真正造成破坏的,往往并非那些挖掘并研究漏洞的“黑客”们,而是这些脚本小子。而在今天已经形成产业的计算机犯罪、网络犯罪中,造成主要破坏的,也是这些“脚本小子”。
1.1.1 中国黑客简史
中国黑客的发展分为几个阶段,到今天已经形成了一条黑色产业链。
笔者把中国黑客的发展分为了:启蒙时代、黄金时代、黑暗时代。
首先是启蒙时代,这个时期大概处在20世纪90年代,此时中国的互联网也刚刚处于起步阶段,一些热爱新兴技术的青年受到国外黑客技术的影响,开始研究安全漏洞。启蒙时代的黑客们大多是由于个人爱好而走上这条道路,好奇心与求知欲是驱使他们前进的动力,没有任何利益的瓜葛。这个时期的中国黑客们通过互联网,看到了世界,因此与西方发达国家同期诞生的黑客精神是一脉相传的,他们崇尚分享、自由、免费的互联网精神,并热衷于分享自己的最新研究成果。
接下来是黄金时代,这个时期以中美黑客大战为标志。在这个历史背景下,黑客这个特殊的群体一下子几乎吸引了社会的所有眼球,而此时黑客圈子所宣扬的黑客文化以及黑客技术的独特魅力也吸引了无数的青少年走上这条道路。自此事件后,各种黑客组织如雨后春笋般冒出。此阶段的中国黑客,其普遍的特点是年轻,有活力,充满激情,但在技术上也许还不够成熟。此时期黑客圈子里贩卖漏洞、恶意软件的现象开始升温,同时因为黑客群体的良莠不齐,也开始出现以赢利为目的的攻击行为,黑色产业链逐渐成型。
最后是黑暗时代,这个阶段从几年前开始一直延续到今天,也许还将继续下去。在这个时期黑客组织也遵循着社会发展规律,优胜劣汰,大多数的黑客组织没有坚持下来。在上一个时期非常流行的黑客技术论坛越来越缺乏人气,最终走向没落。所有门户型的漏洞披露站点,也不再公布任何漏洞相关的技术细节。
伴随着安全产业的发展,黑客的功利性越来越强。黑色产业链开始成熟,这个地下产业每年都会给互联网造成数十亿的损失。而在上一个时期技术还不成熟的黑客们,凡是坚持下来的,都已经成长为安全领域的高级人才,有的在安全公司贡献着自己的专业技能,有的则带着非常强的技术进入了黑色产业。此时期的黑客群体因为互相之间缺失信任已经不再具有开放和分享的精神,最为纯粹的黑客精神实质上已经死亡。
整个互联网笼罩在黑色产业链的阴影之下,每年数十亿的经济损失和数千万的网民受害,以及黑客精神的死亡,使得我们没有理由不把此时称为黑暗时代。也许黑客精神所代表的Open、Free、Share,真的一去不复返了!
1.1.2 黑客技术的发展历程
从黑客技术发展的角度看,在早期,黑客攻击的目标以系统软件居多。一方面,是由于这个时期的Web技术发展还远远不成熟;另一方面,则是因为通过攻击系统软件,黑客们往往能够直接获取root权限。这段时期,涌现出了非常多的经典漏洞以及“exploit”。比如著名的黑客组织TESO,就曾经编写过一个攻击SSH的exploit,并公然在exploit的banner中宣称曾经利用这个exploit入侵过cia.gov(美国中央情报局)。
下面是这个exploit的一些信息。
root@plac /bin >> ./ssh linux/x86 sshd1 exploit by zip/TESO (zip@james.kalifornia.com) - ripped from openssh 2.2.0 src greets: mray, random, big t, sh1fty, scut, dvorak ps. this sploit already owned cia.gov :/ **please pick a type** Usage: ./ssh host [options] Options: -p port -b baseBase address to start bruteforcing distance, by default 0x1800, goes as high as 0x10000 -t type -d debug mode -o Add this to delta_min types: 0: linux/x86 ssh.com 1.2.26-1.2.31 rhl 1: linux/x86 openssh 1.2.3 (maybe others) 2: linux/x86 openssh 2.2.0p1 (maybe others) 3: freebsd 4.x, ssh.com 1.2.26-1.2.31 rhl
有趣的是,这个exploit还曾经出现在著名电影《黑客帝国2》中:
电影《黑客帝国2》
放大屏幕上的文字可以看到:
电影《黑客帝国2》中使用的著名exploit
在早期互联网中,Web并非互联网的主流应用,相对来说,基于SMTP、POP3、FTP、IRC等协议的服务拥有着绝大多数的用户。因此黑客们主要的攻击目标是网络、操作系统以及软件等领域,Web安全领域的攻击与防御技术均处于非常原始的阶段。
相对于那些攻击系统软件的exploit而言,基于Web的攻击,一般只能让黑客获得一个较低权限的账户,对黑客的吸引力远远不如直接攻击系统软件。
但是时代在发展,防火墙技术的兴起改变了互联网安全的格局。尤其是以Cisco、华为等为代表的网络设备厂商,开始在网络产品中更加重视网络安全,最终改变了互联网安全的走向。防火墙、ACL技术的兴起,使得直接暴露在互联网上的系统得到了保护。
比如一个网站的数据库,在没有保护的情况下,数据库服务端口是允许任何人随意连接的;在有了防火墙的保护后,通过ACL可以控制只允许信任来源的访问。这些措施在很大程度上保证了系统软件处于信任边界之内,从而杜绝了大部分的攻击来源。
2003年的冲击波蠕虫是一个里程碑式的事件,这个针对Windows操作系统RPC服务(运行在445端口)的蠕虫,在很短的时间内席卷了全球,造成了数百万台机器被感染,损失难以估量。在此次事件后,网络运营商们很坚决地在骨干网络上屏蔽了135、445等端口的连接请求。此次事件之后,整个互联网对于安全的重视达到了一个空前的高度。
运营商、防火墙对于网络的封锁,使得暴露在互联网上的非Web服务越来越少,且Web技术的成熟使得Web应用的功能越来越强大,最终成为了互联网的主流。黑客们的目光,也渐渐转移到了Web这块大蛋糕上。
实际上,在互联网安全领域所经历的这个阶段,还有另外一个重要的分支,即桌面软件安全,或者叫客户端软件安全。其代表是浏览器攻击。一个典型的攻击场景是,黑客构造一个恶意网页,诱使用户使用浏览器访问该网页,利用浏览器中存在的某些漏洞,比如一个缓冲区溢出漏洞,执行shellcode,通常是下载一个木马并在用户机器里执行。常见的针对桌面软件的攻击目标,还包括微软的Office系列软件、Adobe Acrobat Reader、多媒体播放软件、压缩软件等装机量大的流行软件,都曾经成为黑客们的最爱。但是这种攻击,和本书要讨论的Web安全还是有着本质的区别,所以即使浏览器安全是Web安全的重要组成部分,但在本书中,也只会讨论浏览器和Web安全有关的部分。
1.1.3 Web安全的兴起
Web攻击技术的发展也可以分为几个阶段。在Web 1.0时代,人们更多的是关注服务器端动态脚本的安全问题,比如将一个可执行脚本(俗称webshell)上传到服务器上,从而获得权限。动态脚本语言的普及,以及Web技术发展初期对安全问题认知的不足导致很多“血案”的发生,同时也遗留下很多历史问题,比如PHP语言至今仍然只能靠较好的代码规范来保证没有文件包含漏洞,而无法从语言本身杜绝此类安全问题的发生。
SQL注入的出现是Web安全史上的一个里程碑,它最早出现大概是在1999年,并很快就成为Web安全的头号大敌。就如同缓冲区溢出出现时一样,程序员们不得不日以继夜地去修改程序中存在的漏洞。黑客们发现通过SQL注入攻击,可以获取很多重要的、敏感的数据,甚至能够通过数据库获取系统访问权限,这种效果并不比直接攻击系统软件差,Web攻击一下子就流行起来。SQL注入漏洞至今仍然是Web安全领域中的一个重要组成部分。
XSS(跨站脚本攻击)的出现则是Web安全史上的另一个里程碑。实际上,XSS的出现时间和SQL注入差不多,但是真正引起人们重视则是在大概2003年以后。在经历了MySpace的XSS蠕虫事件后,安全界对XSS的重视程度提高了很多,OWASP 2007 TOP 10威胁甚至把XSS排在榜首。
伴随着Web 2.0的兴起,XSS、CSRF等攻击已经变得更为强大。Web攻击的思路也从服务器端转向了客户端,转向了浏览器和用户。黑客们天马行空的思路,覆盖了Web的每一个环节,变得更加的多样化,这些安全问题,在本书后续的章节中会深入地探讨。
Web技术发展到今天,构建出了丰富多彩的互联网。互联网业务的蓬勃发展,也催生出了许多新兴的脚本语言,比如Python、Ruby、NodeJS等,敏捷开发成为互联网的主旋律。而手机技术、移动互联网的兴起,也给HTML 5带来了新的机遇和挑战。与此同时,Web安全技术,也将紧跟着互联网发展的脚步,不断地演化出新的变化。
1.2 黑帽子,白帽子
正如一个硬币有两面一样,“黑客”也有好坏之分。在黑客的世界中,往往用帽子的颜色来比喻黑客的好坏。白帽子,则是指那些精通安全技术,但是工作在反黑客领域的专家们;而黑帽子,则是指利用黑客技术造成破坏,甚至进行网络犯罪的群体。
同样是研究安全,白帽子和黑帽子在工作时的心态是完全不同的。
对于黑帽子来说,只要能够找到系统的一个弱点,就可以达到入侵系统的目的;而对于白帽子来说,必须找到系统的所有弱点,不能有遗漏,才能保证系统不会出现问题。这种差异是由于工作环境与工作目标的不同所导致的。白帽子一般为企业或安全公司服务,工作的出发点就是要解决所有的安全问题,因此所看所想必然要求更加的全面、宏观;黑帽子的主要目的是要入侵系统,找到对他们有价值的数据,因此黑帽子只需要以点突破,找到对他们最有用的一点,以此渗透,因此思考问题的出发点必然是有选择性的、微观的。
从对待问题的角度来看,黑帽子为了完成一次入侵,需要利用各种不同漏洞的组合来达到目的,是在不断地组合问题;而白帽子在设计解决方案时,如果只看到各种问题组合后产生的效果,就会把事情变复杂,难以细致入微地解决根本问题,所以白帽子必然是在不断地分解问题,再对分解后的问题逐个予以解决。
这种定位的不对称,也导致了白帽子的安全工作比较难做。“破坏永远比建设容易”,但凡事都不是绝对的。要如何扭转这种局面呢?一般来说,白帽子选择的方法,是克服某种攻击方法,而并非抵御单次的攻击。比如设计一个解决方案,在特定环境下能够抵御所有已知的和未知的SQL Injection问题。假设这个方案的实施周期是3个月,那么执行3个月后,所有的SQL Injection问题都得到了解决,也就意味着黑客再也无法利用SQL Injection这一可能存在的弱点入侵网站了。如果做到了这一点,那么白帽子们就在SQL Injection的局部对抗中化被动为主动了。
但这一切都是理想状态,在现实世界中,存在着各种各样不可回避的问题。工程师们很喜欢一句话:“No Patch For Stupid! ”,在安全领域也普遍认为:“最大的漏洞就是人!”。写得再好的程序,在有人参与的情况下,就可能会出现各种各样不可预知的情况,比如管理员的密码有可能泄露,程序员有可能关掉了安全的配置参数,等等。安全问题往往发生在一些意想不到的地方。
另一方面,防御技术在不断完善的同时,攻击技术也在不断地发展。这就像一场军备竞赛,看谁跑在前面。白帽子们刚把某一种漏洞全部堵上,黑帽子们转眼又会玩出新花样。谁能在技术上领先,谁就能占据主动。互联网技术日新月异,在新技术领域的发展中,也存在着同样的博弈过程。可现状是,如果新技术不在一开始就考虑安全设计的话,防御技术就必然会落后于攻击技术,导致历史不断地重复。
1.3 返璞归真,揭秘安全的本质
讲了很多题外话,最终回到正题上。这是一本讲Web安全的书,在本书中除了讲解必要的攻击技术原理之外,最终重心还是要放在防御的思路和实现的技术上。
在进行具体技术的讲解之前,我们需要先清楚地认识到“安全的本质”,或者说,“安全问题的本质”。
安全是什么?什么样的情况下会产生安全问题?我们要如何看待安全问题?只有搞明白了这些最基本的问题,才能明白一切防御技术的出发点,才能明白为什么我们要这样做,要那样做。
在武侠小说中,一个真正的高手,对武功有着最透彻、最本质的理解,达到了返璞归真的境界。在安全领域,笔者认为搞明白了安全的本质,就好比学会了“独孤九剑”,天下武功万变不离其宗,遇到任何复杂的情况都可以轻松应对,设计任何的安全方案也都可以信手拈来了。
那么,一个安全问题是如何产生的呢?我们不妨先从现实世界入手。火车站、机场里,在乘客们开始正式旅程之前,都有一个必要的程序:安全检查。机场的安全检查,会扫描乘客的行李箱,检查乘客身上是否携带了打火机、可燃液体等危险物品。抽象地说,这种安全检查,就是过滤掉有害的、危险的东西。因为在飞行的过程中,飞机远离地面,如果发生危险,将会直接危害到乘客们的生命安全。因此,飞机是一个高度敏感和重要的区域,任何有危害的物品都不应该进入这一区域。为达到这一目标,登机前的安全检查就是一个非常有必要的步骤。
从安全的角度来看,我们将不同重要程度的区域划分出来:
安全检查的过程按照需要进行过滤
通过一个安全检查(过滤、净化)的过程,可以梳理未知的人或物,使其变得可信任。被划分出来的具有不同信任级别的区域,我们称为信任域,划分两个不同信任域之间的边界,我们称为信任边界。
数据从高等级的信任域流向低等级的信任域,是不需要经过安全检查的;数据从低等级的信任域流向高等级的信任域,则需要经过信任边界的安全检查。
我们在机场通过安检后,想要从候机厅出来,是不需要做检查的;但是想要再回到候机厅,则需要再做一次安全检查,就是这个道理。
笔者认为,安全问题的本质是信任的问题。
一切的安全方案设计的基础,都是建立在信任关系上的。我们必须相信一些东西,必须有一些最基本的假设,安全方案才能得以建立;如果我们否定一切,安全方案就会如无源之水,无根之木,无法设计,也无法完成。
举例来说,假设我们有份很重要的文件要好好保管起来,能想到的一个方案是把文件“锁”到抽屉里。这里就包含了几个基本的假设,首先,制作这把锁的工匠是可以信任的,他没有私自藏一把钥匙;其次,制作抽屉的工匠没有私自给抽屉装一个后门;最后,钥匙还必须要保管在一个不会出问题的地方,或者交给值得信任的人保管。反之,如果我们一切都不信任,那么也就不可能认为文件放在抽屉里是安全的。
当制锁的工匠无法打开锁时,文件才是安全的,这是我们的假设前提之一。但是如果那个工匠私自藏有一把钥匙,那么这份文件也就不再安全了。这个威胁存在的可能性,依赖于对工匠的信任程度。如果我们信任工匠,那么在这个假设前提下,我们就能确定文件的安全性。这种对条件的信任程度,是确定对象是否安全的基础。
在现实生活中,我们很少设想最极端的前提条件,因为极端的条件往往意味者小概率以及高成本,因此在成本有限的情况下,我们往往会根据成本来设计安全方案,并将一些可能性较大的条件作为决策的主要依据。
比如在设计物理安全时,根据不同的地理位置、不同的政治环境等,需要考虑台风、地震、战争等因素。但在考虑、设计这些安全方案时,根据其发生的可能性,需要有不同的侧重点。比如在大陆深处,考虑台风的因素则显得不太实际;同样的道理,在大陆板块稳定的地区,考虑地震的因素也会带来较高的成本。而极端的情况比如“彗星撞击地球后如何保证机房不受影响”的问题,一般都不在考虑之中,因为发生的可能性太小。
从另一个角度来说,一旦我们作为决策依据的条件被打破、被绕过,那么就会导致安全假设的前提条件不再可靠,变成一个伪命题。因此,把握住信任条件的度,使其恰到好处,正是设计安全方案的难点所在,也是安全这门学问的艺术魅力所在。
1.4 破除迷信,没有银弹
在解决安全问题的过程中,不可能一劳永逸,也就是说“没有银弹”。
一般来说,人们都会讨厌麻烦的事情,在潜意识里希望能够让麻烦越远越好。而安全,正是一件麻烦的事情,而且是无法逃避的麻烦。任何人想要一劳永逸地解决安全问题,都属于一相情愿,是“自己骗自己”,是不现实的。
安全是一个持续的过程。
自从互联网有了安全问题以来,攻击和防御技术就在不断碰撞和对抗的过程中得到发展。从微观上来说,在某一时期可能某一方占了上风;但是从宏观上来看,某一时期的攻击或防御技术,都不可能永远有效,永远用下去。这是因为防御技术在发展的同时,攻击技术也在不断发展,两者是互相促进的辩证关系。以不变的防御手段对抗不断发展的攻击技术,就犯了刻舟求剑的错误。在安全的领域中,没有银弹。
很多安全厂商在推销自己产品时,会向用户展示一些很美好的蓝图,似乎他们的产品无所不能,购买之后用户就可以睡得安稳了。但实际上,安全产品本身也需要不断地升级,也需要有人来运营。产品本身也需要一个新陈代谢的过程,否则就会被淘汰。在现代的互联网产品中,自动升级功能已经成为一个标准配置,一个有活力的产品总是会不断地改进自身。
微软在发布Vista时,曾信誓旦旦地保证这是有史以来最安全的操作系统。我们看到了微软的努力,在Vista下的安全问题确实比它的前辈们(Windows XP、Windows 2000、Windows 2003等)少了许多,尤其是高危的漏洞。但即便如此,在2008年的Pwn2own竞赛上,Vista也被黑客们攻击成功。Pwn2own竞赛是每年举行的让黑客们任意攻击操作系统的一次盛会,一般黑客们都会提前准备好0day漏洞的攻击程序,以求在Pwn2own上一举夺魁。
黑客们在不断地研究和寻找新的攻击技术,作为防御的一方,没有理由不持续跟进。微软近几年在产品的安全中做得越来越好,其所推崇的安全开发流程,将安全检查贯穿于整个软件生命周期中,经过实践检验,证明这是一条可行的道路。对每一个产品,都要持续地实施严格的安全检查,这是微软通过自身的教训传授给业界的宝贵经验。而安全检查本身也需要不断更新,增加针对新型攻击方式的检测与防御方案。
1.5 安全三要素
既然安全方案的设计与实施过程中没有银弹,注定是一个持续进行的过程,那么我们该如何开始呢?其实安全方案的设计也有着一定的思路与方法可循,借助这些方法,能够理清我们的思路,帮助我们设计出合理、优秀的解决方案。
因为信任关系被破坏,从而产生了安全问题。我们可以通过信任域的划分、信任边界的确定,来发现问题是在何处产生的。这个过程可以让我们明确目标,那接下来该怎么做呢?
在设计安全方案之前,要正确、全面地看待安全问题。
要全面地认识一个安全问题,我们有很多种办法,但首先要理解安全问题的组成属性。前人通过无数实践,最后将安全的属性总结为安全三要素,简称CIA
安全三要素是安全的基本组成元素,分别是机密性(Confidentiality)、完整性(Integrity)、可用性(Availability)。
机密性要求保护数据内容不能泄露,加密是实现机密性要求的常见手段。
比如在前文的例子中,如果文件不是放在抽屉里,而是放在一个透明的玻璃盒子里,那么虽然外人无法直接取得文件,但因为玻璃盒子是透明的,文件内容可能还是会被人看到,所以不符合机密性要求。但是如果给文件增加一个封面,掩盖了文件内容,那么也就起到了隐藏的效果,从而满足了机密性要求。可见,我们在选择安全方案时,需要灵活变通,因地制宜,没有一成不变的方案。
完整性则要求保护数据内容是完整、没有被篡改的。常见的保证一致性的技术手段是数字签名。
传说清朝康熙皇帝的遗诏,写的是“传位十四子”,被当时还是四阿哥的胤禛篡改了遗诏,变成了“传位于四子”。姑且不论传说的真实性,在故事中,对这份遗诏的保护显然没有达到完整性要求。如果在当时有数字签名等技术,遗诏就很难被篡改。从这个故事中也可以看出数据的完整性、一致性的重要意义。
可用性要求保护资源是“随需而得”。
假设一个停车场里有100个车位,在正常情况下,可以停100辆车。但是在某一天,有个坏人搬了100块大石头,把每个车位都占用了,停车场无法再提供正常服务。在安全领域中这种攻击叫做拒绝服务攻击,简称DoS(Denial of Service)。拒绝服务攻击破坏的是安全的可用性。
在安全领域中,最基本的要素就是这三个,后来还有人想扩充这些要素,增加了诸如可审计性、不可抵赖性等,但最最重要的还是以上三个要素。在设计安全方案时,也要以这三个要素为基本的出发点,去全面地思考所面对的问题。
1.6 如何实施安全评估
有了前面的基础,我们就可以正式开始分析并解决安全问题了。一个安全评估的过程,可以简单地分为4个阶段:资产等级划分、威胁分析、风险分析、确认解决方案。
安全评估的过程
一般来说,按照这个过程来实施安全评估,在结果上不会出现较大的问题。这个实施的过程是层层递进的,前后之间有因果关系。
如果面对的是一个尚未评估的系统,那么应该从第一个阶段开始实施;如果是由专职的安全团队长期维护的系统,那么有些阶段可以只实施一次。在这几个阶段中,上一个阶段将决定下一个阶段的目标,需要实施到什么程度。
1.6.1 资产等级划分
资产等级划分是所有工作的基础,这项工作能够帮助我们明确目标是什么,要保护什么。
我们前面提到安全三要素时,机密性和完整性都是与数据相关的,在可用性的定义里,笔者则用到了“资源”一词。“资源”这个概念描述的范围比数据要更加广阔,但很多时候,资源的可用性也可以理解为数据的可用性。
在互联网的基础设施已经比较完善的今天,互联网的核心其实是由用户数据驱动的——用户产生业务,业务产生数据。互联网公司除了拥有一些固定资产,如服务器等死物外,最核心的价值就是其拥有的用户数据,所以——
互联网安全的核心问题,是数据安全的问题。
这与我们做资产评估又有什么关系呢?有,因为对互联网公司拥有的资产进行等级划分,就是对数据做等级划分。有的公司最关心的是客户数据,有的公司最关心的是员工资料信息,根据各自业务的不同,侧重点也不同。做资产等级划分的过程,需要与各个业务部门的负责人一一沟通,了解公司最重要的资产是什么,他们最看重的数据是什么。通过访谈的形式,安全部门才能熟悉、了解公司的业务,公司所拥有的数据,以及不同数据的重要程度,为后续的安全评估过程指明方向。
当完成资产等级划分后,对要保护的目标已经有了一个大概的了解,接下来就是要划分信任域和信任边界了。通常我们用一种最简单的划分方式,就是从网络逻辑上来划分。比如最重要的数据放在数据库里,那么把数据库的服务器圈起来;Web应用可以从数据库中读/写数据,并对外提供服务,那再把Web服务器圈起来;最外面是不可信任的Internet。
简单网站信任模型
这是最简单的例子,在实际中会遇到比这复杂许多的情况。比如同样是两个应用,互相之间存在数据交互业务,那么就要考虑这里的数据交互对于各自应用来说是否是可信的,是否应该在两个应用之间划一个边界,然后对流经边界的数据做安全检查。
1.6.2 威胁分析
信任域划好之后,我们如何才能确定危险来自哪里呢?在安全领域里,我们把可能造成危害的来源称为威胁(Threat),而把可能会出现的损失称为风险(Risk)。风险一定是和损失联系在一起的,很多专业的安全工程师也经常把这两个概念弄混,在写文档时张冠李戴。现在把这两个概念区分好,有助于我们接下来要提到的“威胁建模”和“风险分析”两个阶段,这两个阶段的联系是很紧密的。
什么是威胁分析?威胁分析就是把所有的威胁都找出来。怎么找?一般是采用头脑风暴法。当然,也有一些比较科学的方法,比如使用一个模型,帮助我们去想,在哪些方面有可能会存在威胁,这个过程能够避免遗漏,这就是威胁建模。
在本书中介绍一种威胁建模的方法,它最早是由微软提出的,叫做STRIDE模型。
STRIDE是6个单词的首字母缩写,我们在分析威胁时,可以从以下6个方面去考虑。
在进行威胁分析时,要尽可能地不遗漏威胁,头脑风暴的过程可以确定攻击面(Attack Surface)。
在维护系统安全时,最让安全工程师沮丧的事情就是花费很多的时间与精力实施安全方案,但是攻击者却利用了事先完全没有想到的漏洞(漏洞的定义:系统中可能被威胁利用以造成危害的地方。)完成入侵。这往往就是由于在确定攻击面时,想的不够全面而导致的。
以前有部老电影叫做《智取华山》,是根据真实事件改编的。1949年5月中旬,打响了“陕中战役”,国民党保安第6旅旅长兼第8区专员韩子佩率残部400余人逃上华山,企图凭借“自古华山一条道”的天险负隅顽抗。路东总队决定派参谋刘吉尧带侦察小分队前往侦察,刘吉尧率领小分队,在当地村民的带领下,找到了第二条路:爬悬崖!克服种种困难,最终顺利地完成了任务。战后,刘吉尧光荣地出席了全国英模代表大会,并被授予“全国特等战斗英雄”荣誉称号。
我们用安全眼光来看这次战斗。国民党部队在进行“威胁分析”时,只考虑到“自古华山一条道”,所以在正路上布重兵,而完全忽略了其他的可能。他们“相信”其他道路是不存在的,这是他们实施安全方案的基础,而一旦这个信任基础不存在了,所有的安全方案都将化作浮云,从而被共产党的部队击败。
所以威胁分析是非常重要的一件事情,很多时候还需要经常回顾和更新现有的模型。可能存在很多威胁,但并非每个威胁都会造成难以承受的损失。一个威胁到底能够造成多大的危害,如何去衡量它?这就要考虑到风险了。我们判断风险高低的过程,就是风险分析的过程。在“风险分析”这个阶段,也有模型可以帮助我们进行科学的思考。
1.6.3 风险分析
风险由以下因素组成:
Risk = Probability * Damage Potential
影响风险高低的因素,除了造成损失的大小外,还需要考虑到发生的可能性。地震的危害很大,但是地震、火山活动一般是在大陆板块边缘频繁出现,比如日本、印尼就处于这些地理位置,因此地震频发;而在大陆板块中心,若是地质结构以整块的岩石为主,则不太容易发生地震,因此地震的风险就要小很多。我们在考虑安全问题时,要结合具体情况,权衡事件发生的可能性,才能正确地判断出风险。
如何更科学地衡量风险呢?这里再介绍一个DREAD模型,它也是由微软提出的。DREAD也是几个单词的首字母缩写,它指导我们应该从哪些方面去判断一个威胁的风险程度。
在DREAD模型里,每一个因素都可以分为高、中、低三个等级。在上表中,高、中、低三个等级分别以3、2、1的分数代表其权重值,因此,我们可以具体计算出某一个威胁的风险值。
以《智取华山》为例,如果国民党在威胁建模后发现存在两个主要威胁:第一个威胁是从正面入口强攻,第二个威胁是从后山小路爬悬崖上来。那么,这两个威胁对应的风险分别计算如下:
走正面的入口:
Risk = D(3) + R(3) + E(3) + A(3) + D(3) = 3+3+3+3+3=15
走后山小路:
Risk = D(3) + R(1) + E(1) + A(3) + D(1) = 3+1+1+3+1=9
如果我们把风险高低定义如下:
高危:12~15分 中危:8~11分 低危:0~7分
那么,正面入口是最高危的,必然要派重兵把守;而后山小路竟然是中危的,因此也不能忽视。之所以会被这个模型判断为中危的原因,就在于一旦被突破,造成的损失太大,失败不起,所以会相应地提高该风险值。
介绍完威胁建模和风险分析的模型后,我们对安全评估的整体过程应该有了一个大致的了解。在任何时候都应该记住——模型是死的,人是活的,再好的模型也是需要人来使用的,在确定攻击面,以及判断风险高低时,都需要有一定的经验,这也是安全工程师的价值所在。类似STRIDE和DREAD的模型可能还有很多,不同的标准会对应不同的模型,只要我们觉得这些模型是科学的,能够帮到我们,就可以使用。但模型只能起到一个辅助的作用,最终做出决策的还是人。
1.6.4 设计安全方案
安全评估的产出物,就是安全解决方案。解决方案一定要有针对性,这种针对性是由资产等级划分、威胁分析、风险分析等阶段的结果给出的。
设计解决方案不难,难的是如何设计一个好的解决方案。设计一个好的解决方案,是真正考验安全工程师水平的时候。
很多人认为,安全和业务是冲突的,因为往往为了安全,要牺牲业务的一些易用性或者性能,笔者不太赞同这种观点。从产品的角度来说,安全也应该是产品的一种属性。一个从未考虑过安全的产品,至少是不完整的。
比如,我们要评价一个杯子是否好用,除了它能装水,能装多少水外,还要思考这个杯子内壁的材料是否会溶解在水里,是否会有毒,在高温时会不会熔化,在低温时是否易碎,这些问题都直接影响用户使用杯子的安全性。
对于互联网来说,安全是要为产品的发展与成长保驾护航的。我们不能使用“粗暴”的安全方案去阻碍产品的正常发展,所以应该形成这样一种观点:没有不安全的业务,只有不安全的实现方式。产品需求,尤其是商业需求,是用户真正想要的东西,是业务的意义所在,在设计安全方案时应该尽可能地不要改变商业需求的初衷。
作为安全工程师,要想的就是如何通过简单而有效的方案,解决遇到的安全问题。安全方案必须能够有效抵抗威胁,但同时不能过多干涉正常的业务流程,在性能上也不能拖后腿。
好的安全方案对用户应该是透明的,尽可能地不要改变用户的使用习惯。
微软在推出Windows Vista时,有一个新增的功能叫UAC,每当系统里的软件有什么敏感动作时,UAC就会弹出来询问用户是否允许该行为。这个功能在Vista众多失败的原因中是被人诟病最多的一个。如果用户能够分辨什么样的行为是安全的,那么还要安全软件做什么?同样的问题出现在很多主动防御的桌面安全保护软件中,它们动辄弹出个对话框询问用户是否允许目标的行为,这是非常荒谬的用户体验。
好的安全产品或模块除了要兼顾用户体验外,还要易于持续改进。一个好的安全模块,同时也应该是一个优秀的程序,从设计上也需要做到高聚合、低耦合、易于扩展。比如Nmap的用户就可以自己根据需要写插件,实现一些更为复杂的功能,满足个性化需求。
最终,一个优秀的安全方案应该具备以下特点:
❍ 能够有效解决问题;
❍ 用户体验好;
❍ 高性能;
❍ 低耦合;
❍ 易于扩展与升级。
关于产品安全性的问题,在本书的“互联网业务安全”一章中还会继续深入阐述。
1.7 白帽子兵法
在上节讲述了实施安全评估的基本过程,安全评估最后的产出物就是安全方案,但在具体设计安全方案时有什么样的技巧呢?本节将讲述在实战中可能用到的方法。
1.7.1 Secure By DefauIt原则
在设计安全方案时,最基本也最重要的原则就是“Secure by Default”。在做任何安全设计时,都要牢牢记住这个原则。一个方案设计得是否足够安全,与有没有应用这个原则有很大的关系。实际上,“Secure by Default”原则,也可以归纳为白名单、黑名单的思想。如果更多地使用白名单,那么系统就会变得更安全。
1.7.1.1 黑名单、白名单
比如,在制定防火墙的网络访问控制策略时,如果网站只提供Web服务,那么正确的做法是只允许网站服务器的80和443端口对外提供服务,屏蔽除此之外的其他端口。这是一种“白名单”的做法;如果使用“黑名单”,则可能会出现问题。假设黑名单的策略是:不允许SSH端口对Internet开放,那么就要审计SSH的默认端口:22端口是否开放了Internet。但在实际工作过程中,经常会发现有的工程师为了偷懒或图方便,私自改变了SSH的监听端口,比如把SSH的端口从22改到了2222,从而绕过了安全策略。
又比如,在网站的生产环境服务器上,应该限制随意安装软件,而需要制定统一的软件版本规范。这个规范的制定,也可以选择白名单的思想来实现。按照白名单的思想,应该根据业务需求,列出一个允许使用的软件以及软件版本的清单,在此清单外的软件则禁止使用。如果允许工程师在服务器上随意安装软件的话,则可能会因为安全部门不知道、不熟悉这些软件而导致一些漏洞,从而扩大攻击面。
在Web安全中,对白名单思想的运用也比比皆是。比如应用处理用户提交的富文本时,考虑到XSS的问题,需要做安全检查。常见的XSS Filter一般是先对用户输入的HTML原文作HTML Parse,解析成标签对象后,再针对标签匹配XSS的规则。这个规则列表就是一个黑白名单。如果选择黑名单的思想,则这套规则里可能是禁用诸如<script>、<iframe>等标签。但是黑名单可能会有遗漏,比如未来浏览器如果支持新的HTML标签,那么此标签可能就不在黑名单之中了。如果选择白名单的思想,就能避免这种问题——在规则中,只允许用户输入诸如<a>、<img>等需要用到的标签。对于如何设计一个好的XSS防御方案,在“跨站脚本攻击”一章中还会详细讲到,不在此赘述了。
然而,并不是用了白名单就一定安全了。有朋友可能会问,作者刚才讲到选择白名单的思想会更安全,现在又说不一定,这不是自相矛盾吗?我们可以仔细分析一下白名单思想的本质。在前文中提到:“安全问题的本质是信任问题,安全方案也是基于信任来做的”。选择白名单的思想,基于白名单来设计安全方案,其实就是信任白名单是好的,是安全的。但是一旦这个信任基础不存在了,那么安全就荡然无存。
在Flash跨域访问请求里,是通过检查目标资源服务器端的crossdomain.xml文件来验证是否允许客户端的Flash跨域发起请求的,它使用的是白名单的思想。比如下面这个策略文件:
<cross-domain-policy> <allow-access-from domain="*.taobao.com"/> <allow-access-from domain="*.taobao.net"/> <allow-access-from domain="*.taobaocdn.com"/> <allow-access-from domain="*.tbcdn.cn"/> <allow-access-from domain="*.allyes.com"/> </cross-domain-policy>
指定了只允许特定域的Flash对本域发起请求。可是如果这个信任列表中的域名变得不可信了,那么问题就会随之而来。比如:
<cross-domain-policy> <allow-access-from domain="*"/> </cross-domain-policy>
通配符“*”,代表来自任意域的Flash都能访问本域的数据,因此就造成了安全隐患。所以在选择使用白名单时,需要注意避免出现类似通配符“*”的问题。
1.7.1.2 最小权限原则
Secure By Default的另一层含义就是“最小权限原则”。最小权限原则也是安全设计的基本原则之一。最小权限原则要求系统只授予主体必要的权限,而不要过度授权,这样能有效地减少系统、网络、应用、数据库出错的机会。
比如在Linux系统中,一种良好的操作习惯是使用普通账户登录,在执行需要root权限的操作时,再通过sudo命令完成。这样能最大化地降低一些误操作导致的风险;同时普通账户被盗用后,与root帐户被盗用所导致的后果是完全不同的。
在使用最小权限原则时,需要认真梳理业务所需要的权限,在很多时候,开发者并不会意识到业务授予用户的权限过高。在通过访谈了解业务时,可以多设置一些反问句,比如:您确定您的程序一定需要访问Internet吗?通过此类问题,来确定业务所需的最小权限。
1.7.2 纵深防御原则
与Secure by Default一样,Defense in Depth(纵深防御)也是设计安全方案时的重要指导思想。
纵深防御包含两层含义:首先,要在各个不同层面、不同方面实施安全方案,避免出现疏漏,不同安全方案之间需要相互配合,构成一个整体;其次,要在正确的地方做正确的事情,即:在解决根本问题的地方实施针对性的安全方案。
某矿泉水品牌曾经在广告中展示了一滴水的生产过程:经过十多层的安全过滤,去除有害物质,最终得到一滴饮用水。这种多层过滤的体系,就是一种纵深防御,是有立体层次感的安全方案。
纵深防御并不是同一个安全方案要做两遍或多遍,而是要从不同的层面、不同的角度对系统做出整体的解决方案。我们常常听到“木桶理论”这个词,说的是一个桶能装多少水,不是取决于最长的那块板,而是取决于最短的那块板,也就是短板。设计安全方案时最怕出现短板,木桶的一块块板子,就是各种具有不同作用的安全方案,这些板子要紧密地结合在一起,才能组成一个不漏水的木桶。
在常见的入侵案例中,大多数是利用Web应用的漏洞,攻击者先获得一个低权限的webshell,然后通过低权限的webshell上传更多的文件,并尝试执行更高权限的系统命令,尝试在服务器上提升权限为root;接下来攻击者再进一步尝试渗透内网,比如数据库服务器所在的网段。
在这类入侵案例中,如果在攻击过程中的任何一个环节设置有效的防御措施,都有可能导致入侵过程功亏一篑。但是世上没有万能灵药,也没有哪种解决方案能解决所有问题,因此非常有必要将风险分散到系统的各个层面。就入侵的防御来说,我们需要考虑的可能有Web应用安全、OS系统安全、数据库安全、网络环境安全等。在这些不同层面设计的安全方案,将共同组成整个防御体系,这也就是纵深防御的思想。
纵深防御的第二层含义,是要在正确的地方做正确的事情。如何理解呢?它要求我们深入理解威胁的本质,从而做出正确的应对措施。
在XSS防御技术的发展过程中,曾经出现过几种不同的解决思路,直到最近几年XSS的防御思路才逐渐成熟和统一。
XSS防御技术的发展过程
在一开始的方案中,主要是过滤一些特殊字符,比如:
<<笑傲江湖>> 会变成 笑傲江湖
尖括号被过滤掉了。
但是这种粗暴的做法常常会改变用户原本想表达的意思,比如:
1<2 可能会变成 1 2
造成这种“乌龙”的结果就是因为没有“在正确的地方做正确的事情”。对于XSS防御,对系统取得的用户输入进行过滤其实是不太合适的,因为XSS真正产生危害的场景是在用户的浏览器上,或者说服务器端输出的HTML页面,被注入了恶意代码。只有在拼装HTML时输出,系统才能获得HTML上下文的语义,才能判断出是否存在误杀等情况。所以“在正确的地方做正确的事情”,也是纵深防御的一种含义——必须把防御方案放到最合适的地方去解决。(XSS防御的更多细节请参考“跨站脚本攻击”一章。)
近几年安全厂商为了迎合市场的需要,推出了一种产品叫UTM,全称是“统一威胁管理”(Unified Threat Management)。UTM几乎集成了所有主流安全产品的功能,比如防火墙、VPN、反垃圾邮件、IDS、反病毒等。UTM的定位是当中小企业没有精力自己做安全方案时,可以在一定程度上提高安全门槛。但是UTM并不是万能药,很多问题并不应该在网络层、网关处解决,所以实际使用时效果未必好,它更多的是给用户买个安心。
对于一个复杂的系统来说,纵深防御是构建安全体系的必要选择。
1.7.3 数据与代码分离原则
另一个重要的安全原则是数据与代码分离原则。这一原则广泛适用于各种由于“注入”而引发安全问题的场景。
实际上,缓冲区溢出,也可以认为是程序违背了这一原则的后果——程序在栈或者堆中,将用户数据当做代码执行,混淆了代码与数据的边界,从而导致安全问题的发生。
在Web安全中,由“注入”引起的问题比比皆是,如XSS、SQL Injection、CRLF Injection、X-Path Injection等。此类问题均可以根据“数据与代码分离原则”设计出真正安全的解决方案,因为这个原则抓住了漏洞形成的本质原因。
以XSS为例,它产生的原因是HTML Injection或JavaScript Injection,如果一个页面的代码如下:
<html> <head>test</head> <body> $var </body> </html>
其中 $var是用户能够控制的变量,那么对于这段代码来说:
<html> <head>test</head> <body> </body> </html>
就是程序的代码执行段。
而
$var
就是程序的用户数据片段。
如果把用户数据片段 $var当成代码片段来解释、执行,就会引发安全问题。
比如,当$var的值是:
<script src=http://evil></script>
时,用户数据就被注入到代码片段中。解析这段脚本并执行的过程,是由浏览器来完成的——浏览器将用户数据里的<script>标签当做代码来解释——这显然不是程序开发者的本意。
根据数据与代码分离原则,在这里应该对用户数据片段 $var进行安全处理,可以使用过滤、编码等手段,把可能造成代码混淆的用户数据清理掉,具体到这个案例中,就是针对 <、> 等符号做处理。
有的朋友可能会问了:我这里就是要执行一个<script>标签,要弹出一段文字,比如:“你好!”,那怎么办呢?
在这种情况下,数据与代码的情况就发生了变化,根据数据与代码分离原则,我们就应该重写代码片段:
<html> <head>test</head> <body> <script> alert("$var1"); </script> </body> </html>
在这种情况下,<script>标签也变成了代码片段的一部分,用户数据只有 $var1能够控制,从而杜绝了安全问题的发生。
1.7.4 不可预测性原则
前面介绍的几条原则:Secure By Default,是时刻要牢记的总则;纵深防御,是要更全面、更正确地看待问题;数据与代码分离,是从漏洞成因上看问题;接下来要讲的“不可预测性”原则,则是从克服攻击方法的角度看问题。
微软的Windows系统用户多年来深受缓冲区溢出之苦,因此微软在Windows的新版本中增加了许多对抗缓冲区溢出等内存攻击的功能。微软无法要求运行在系统中的软件没有漏洞,因此它采取的做法是让漏洞的攻击方法失效。比如,使用DEP来保证堆栈不可执行,使用ASLR让进程的栈基址随机变化,从而使攻击程序无法准确地猜测到内存地址,大大提高了攻击的门槛。经过实践检验,证明微软的这个思路确实是有效的——即使无法修复code,但是如果能够使得攻击的方法无效,那么也可以算是成功的防御。
微软使用的ASLR技术,在较新版本的Linux内核中也支持。在ASLR的控制下,一个程序每次启动时,其进程的栈基址都不相同,具有一定的随机性,对于攻击者来说,这就是“不可预测性”。
不可预测性(Unpredictable),能有效地对抗基于篡改、伪造的攻击。我们看看如下场景:
假设一个内容管理系统中的文章序号,是按照数字升序排列的,比如id=1000, id=1002, id=1003……
这样的顺序,使得攻击者能够很方便地遍历出系统中的所有文章编号:找到一个整数,依次递增即可。如果攻击者想要批量删除这些文章,写个简单的脚本:
for (i=0; i<100000; i++){ Delete(url+"? id="+i); }
就可以很方便地达到目的。但是如果该内容管理系统使用了“不可预测性”原则,将id的值变得不可预测,会产生什么结果呢?
id=asldfjaefsadlf, id=adsfalkennffxc, id=poerjfweknfd……
id的值变得完全不可预测了,攻击者再想批量删除文章,只能通过爬虫把所有的页面id全部抓取下来,再一一进行分析,从而提高了攻击的门槛。
不可预测性原则,可以巧妙地用在一些敏感数据上。比如在CSRF的防御技术中,通常会使用一个token来进行有效防御。这个token能成功防御CSRF,就是因为攻击者在实施CSRF攻击的过程中,是无法提前预知这个token值的,因此要求token足够复杂时,不能被攻击者猜测到。(具体细节请参考“跨站点请求伪造”一章。)
不可预测性的实现往往需要用到加密算法、随机数算法、哈希算法,好好使用这条原则,在设计安全方案时往往会事半功倍。
1.8 小结
本章归纳了笔者对于安全世界的认识和思考,从互联网安全的发展史说起,揭示了安全问题的本质,以及应该如何展开安全工作,最后总结了设计安全方案的几种思路和原则。在后续的章节中,将继续揭示Web安全的方方面面,并深入理解攻击原理和正确的解决之道——我们会面对各种各样的攻击,解决方案为什么要这样设计,为什么这最合适?这一切的出发点,都可以在本章中找到本质的原因。
安全是一门朴素的学问,也是一种平衡的艺术。无论是传统安全,还是互联网安全,其内在的原理都是一样的。我们只需抓住安全问题的本质,之后无论遇到任何安全问题(不仅仅局限于Web安全或互联网安全),都会无往而不利,因为我们已经真正地懂得了如何用安全的眼光来看待这个世界!
(附)谁来为漏洞买单?
昨天介绍了PHP中is_a()函数功能改变引发的问题,后来发现很多朋友不认同这是一个漏洞,原因是通过良好的代码习惯能够避免该问题,比如写一个安全的__autoload()函数。
我觉得我有必要讲讲一些安全方面的哲学问题,但这些想法只代表我个人的观点,是我的安全世界观。
互联网本来是安全的,自从有了研究安全的人,就变得不安全了。
所有的程序本来也没有漏洞,只有功能,但当一些功能被用于破坏,造成损失时,也就成了漏洞。
我们定义一个功能是否是漏洞,只看后果,而不应该看过程。
计算机用0和1定义了整个世界,但在整个世界中,并非所有事情都能简单地用“是” 或者“非”来判断,漏洞也是如此,因为破坏有程度轻重之分,当破坏程度超过某一临界值时,多数人(注意不是所有人)会接受这是一个漏洞的事实。但事物是变化的,这个临界值也不是一成不变的,“多数人”也不是一成不变的,所以我们要用变化的观点去看待变化的事物。
泄露用户个人信息,比如电话、住址,在以前几乎称不上漏洞,因为没有人利用;但在互联网越来越关心用户隐私的今天,这就变成了一个严重的问题,因为有无数的坏人时刻在想着利用这些信息搞破坏,非法攫取利益。所以,今天如果发现某网站能够批量、未经授权获取到用户个人信息,这就是一个漏洞。
再举个例子。用户登录的memberID是否属于机密信息?在以往做信息安全,我们都只知道“密码”、“安全问题”等传统意义上的机密信息需要保护。但是在今天,在网站的业务设计中,我们发现loginID也应该属于需要保护的信息。因为loginID一旦泄露后,可能会导致被暴力破解;甚至有的用户将loginID当成密码的一部分,会被黑客猜中用户的密码或者是黑客通过攻击一些第三方站点(比如SNS)后,找到同样的loginID来尝试登录。
正因为攻击技术在发展,所以我们对漏洞的定义也在不断变化。可能很多朋友都没有注意到,一个业务安全设计得好的网站,往往loginID和nickname(昵称)是分开的。登录ID是用户的私有信息,只有用户本人能够看到;而nickname不能用于登录,但可以公开给所有人看。这种设计的细节,是网站积极防御的一种表现。
可能很多朋友仍然不愿意承认这些问题是漏洞,那么什么是漏洞呢?在我看来,漏洞只是对破坏性功能的一个统称而已。
但是“漏洞”这顶帽子太大,大到我们难以承受,所以我们不妨换一个角度看,看看是否“应该修补”。语言真是很神奇的东西,很多时候换一个称呼,就能让人的认可度提高很多。
在PHP的5.3.4版本中,修补了很多年来万恶的0字节截断功能,这个功能被文件包含漏洞利用,酿造了无数“血案”。
我们知道PHP中include/require一个文件的功能,如果有良好的代码规范,则是安全的,不会成为漏洞。
这是一个正常的PHP语言的功能,只是“某一群不明真相的小白程序员”在一个错误的时间、错误的地点写出了错误的代码,使得“某一小撮狡猾的黑客”发现了这些错误的代码,从而导致漏洞。这是操作系统的问题,谁叫操作系统在遍历文件路径时会被0字节截断,谁叫C语言的string操作是以0字节为结束符,谁叫程序员写出这么小白的代码,官方文档里已经提醒过了,关PHP什么事情,太冤枉了!
我也觉得PHP挺冤枉的,但C语言和操作系统也挺冤的,我们就是这么规定的,如之奈何?
但总得有人来为错误买单,谁买单呢?写出不安全代码的小白程序员?
No!学习过市场营销方面知识的同学应该知道,永远也别指望让最终用户来买单,就像老百姓不应该为政府的错误买单一样(当然在某个神奇的国度除外)。所以必须得有人为这些不是漏洞,但造成了既成事实的错误负责,我们需要有社会责任感的owner。
很高兴的是,PHP官方在经历这么多年纠结、折磨、发疯之后,终于勇敢地承担起了这个责任(我相信这是一个很坎坷的心路历程),为这场酿成无数惨案的闹剧画上了一个句号。但是我们仍然悲观地看到,cgi.fix_pathinfo的问题仍然没有修改默认配置,使用fastcgi的PHP应用默认处于风险中。PHP官方仍然坚持认为这是一个正常的功能,谁叫小白程序员不认真学习官方文件精神!是啊,无数网站付出惨痛学费的正常功能!
PHP是当下用户最多的Web开发语言之一,但是因为种种历史遗留原因(我认为是历史原因),导致在安全的“增值”服务上做得远远不够(相对于一些新兴的流行语言来说)。在PHP流行起来的时候,当时的互联网远远没有现在复杂,也远远没有现在这么多的安全问题,在当时的历史背景下,很多问题都不是“漏洞”,只是功能。
我们可以预见到,在未来互联网发展的过程中,也必然会有更多、更古怪的攻击方式出现,也必然会让更多的原本是“功能”的东西,变成漏洞。
最后,也许你已经看出来了,我并不是要说服谁is_a()是一个漏洞,而是在思考,谁该为这些损失买单?我们未来遇到同样的问题怎么办?
对于白帽子来说,我们习惯于分解问题,同一个问题,我们可以在不同层面解决,可以通过良好的代码规范去保证(事实上,所有的安全问题都能这么修复,只是需要付出的成本过于巨大),但只有PHP在源头修补了这个问题,才真正是善莫大焉。
BTW:is_a()函数的问题已经申报了CVE,如果不出意外,security@php.net也会接受这个问题,所以它已经是一个既成事实的漏洞了。