生命以负熵为食 —— 《生命是什么》薛定谔
背景
我想作为一个信息安全从业者,无论是在渗透测试、代码审计亦或是其他安全服务中都会接触到各种各样的漏洞。把这些漏洞进行简单分类可能能够得到几十类漏洞,当然几乎所有的漏洞类型在common Weakniss Enumeration都有相应的描述。
面对类型如此丰富漏洞,我们要如何进行处理呢?
要知道人类的本质是懒惰,逐条进行分析验证几乎是不可能的。而且渗透测试、红蓝对抗等服务在本质上都是为了达到以点带面的目的,那么这个面在哪里是值得我们去思考的问题。
废话扯完了,进入正题。
安全策略清单
本文所说的安全策略,即系统采用的方式用于处理可能存在的安全风险。
我这边简单的梳理了一下,在考虑软件安全时需要考虑的几个方面的问题,图如下:
身份认证策略、访问控制策略、会话管理策略这三个方面基本上属于整个软件安全的基石,如果这三个方面缺少了相应控制或者实现的大方向上存在问题,那么对于整个软件的影响极大,可能是颠覆性的需要推到重建。
对抗中间人: 对于中间人攻击大部分人的看法可能是属于软件后期的部署问题,采用https/HSTS就没什么问题(问题可能并没有这么简单),不过我还是把它纳入到框架。
输入输出: 这可能有点老生常谈,不过我觉得清楚的了解对于软件而言什么是输入,什么是输出,可能会更好进行分析。
敏感数据:在网络逐渐形成虚拟社会的背景下,其开放性的特征必然会引起有关部门的注意,作为一项重要的合规项应当在初期就纳入考虑到。同时如果出现相应的问题,软件修复起来极其头疼,完全可能出现修不完的情况,在投产的过程触犯了相应的法规造成的损失可能也极其巨大。
软件技术栈: 白话一点的说法就是软件都用了什么技术。
配置管理: 一些意料之外漏洞可能都出自于错误的配置管理,诸如交易日志泄露
异常处理: 安全对抗的本质是获取信息,尽可能的获取一些常规获取不到的信息,异常是一项比较重要的来源。
身份认证
概念
在虚拟世界中的身份认证同现实世界中一样,首先需要识别人,建立在识别的基础能够去开展各项业务,产生、处理、使用、存储数据。 只是目前而言,由于你是真实存在的,不会出现类似于证明你是你问题,而虚拟身份的更像是一个物化的概念,那么身份认证就是能够证明虚拟身份是属于现实的你。
所以在虚拟世界这是一个必须要解决的问题——如何让真实的你与虚拟世界中虚拟的你进行绑定。
那么最直接的方式就是提供秘密——只有账号的拥有者持有的信息。例如在账号注册时输入的密码信息、密保信息
换句话说,在虚拟的世界中只要能够提供账号的相关的秘密信息,就能声明账号的所有权。因为对于全知全能的程序而言,它都是仅仅收到了这个秘密,你和他并没有区别。
举个例子:
alkaid登录账号alkaid,输入了密码password,程序验证通过,允许以alkaid账号执行相关业务
person2 不知道通过什么途径知道了,alkaid/password的认证信息,程序验证通过,允许以alkaid账号执行相关业务。
风险与处理
现在身份认证应该就是很清晰了,其实就是验证虚拟账号的秘密信息,那么要知道只要是验证信息就会返回成功和失败的结果,从一定程度这也是一类信息泄露,只是信息泄露的量较少,通过不断累积信息,就能最终破获秘密信息,也是身份认证主要的风险点。
这类风险的直接体现就是暴力枚举破解账号。
当然,这风险属于无法解决的,我们只能采用降低风险,使风险可控的方式:
解决累积信息的特点。
账号锁定机制
由于完美解决1是相对困难的(主要是引入的不可用风险不一定能被接受),也可以采用提高目标的信息量的方式,在有限的时间维度内,无法破解账号
提供秘密信息的复杂度,例如密码的复杂度
采用验证码技术,防止通过机器突破现实人的极限。
减少单次信息累积的量
模糊失败的错误提示
其实还有其他的无法解决的风险,例如秘密信息被窃取。所以一般还会要求提供更换秘密信息的功能。
采用生物信息的技术让人难以接受的是无法进行更新秘密信息,如果需要更换可能需要重新设计相关的算法和信息采样的算法,可能会要求所有虚拟用户同时更换设备和秘密信息。
会话管理
概念
由于HTTP协议属于无状态(每个数据包都是独立的,仅根据数据包无法判断之前发过哪些数据包)的协议,同时在身份认证一节中已经说明大部分的业务操作是需要基于虚拟身份进行的,那么在完成身份认证后,后续数据包无法回溯之前的数据包,从而导致无法证明自己确实能够持有声明的虚拟身份。
当然如果每次都带着身份的秘密信息请求进行确实是可以进行身份认证,但是频繁的使用这类秘密信息可能会增加秘密信息泄露的风险。
现实的生活中由于时间和空间的限制,基本上不存在这类风险,我们也很难进行参考。
不过这类问题反过来——如何让服务器知晓是你这个真实的人在操作你的拥有的虚拟身份(问题又回到了身份认证)
同身份认证一章节所述,那就是掌握秘密信息。
即服务器与我商量一个只有我们两个人知道的临时秘密,来替代原先虚拟身份的秘密。
临时秘密作为虚拟身份的秘密的替代品,在每次访问时都进行提供。—— 临时秘密即我们一般而言的sesssionID(会话ID)。
会话管理,即围绕sessionID是怎么进行处理的。
再绕一点,是不是觉得会话像是系统给我开设的临时虚拟身份,但是同时具有原先虚拟身份的信息?
确实没错,会话从某种程度上来讲与账号其实没有区别,也能够提供相应的信息存储,只不过会话是临时的。
风险与处理
会话与账号相似,其面对的风险也与身份认证相同。
但是由于是相似(如果完全相同,又会回到最开始的问题——无法证明自己确实能够持有声明的虚拟身份),会话最大的一个特征是临时性。 由于预置的时间属性,基本我们采用2中的复杂度方式。
如何来尽可能的保证的复杂度呢?
随机生成的符号组合。(避免组合单词,账号信息等,从信息熵的角度来说,尽可能避免与已知信息相关联,关联的越多,这段数据包含的信息越少,越容易被猜测)
一定长度的保证(每一位的长度增加,破解难度都是成倍提高)
访问控制
概念
鉴于可能会与我们学习过的MAC、DAC、RBAC混淆,这节讨论的东西不是这些具体的策略,谈论这些具体的策略,可能搜索一下google、wiki来得更加方便和准确,是讨论访问控制解决什么问题,面对什么样的风险。
涉及到访问控制,自然有两个概念,主体和客体。
主体
一般指提出访问请求的对象。在实现身份认证和会话管理的基础上,主体相对明确,有两类构成
虚拟身份代表的主体
没有虚拟身份,(代表了所有未授权的情况)
客体
一般指被访问的资源。 具体哪些资源其实在相关的系统里是很难明确的,这里我仅提及两类,功能和数据。 它们应该是在各类系统中最最常见的两类资源。
分析
既然是虚拟时间,如果排除了空间和时间的影响,其本质应该是一样的,现实中与安全性质类似场景包括消防、防盗、安保等
这里以我居住的小区为例,alkaid 住在A小区1幢11层1111房间。
我需要回到我的房间需要经过,小区的门禁、1幢的门禁、1幢的电梯梯控、1111房间的钥匙、密码。
我们讲现实的场景与虚拟的场景进行映射,帮助进行分析也方便大家思考,发现一些我可能没有提到的东西。
小区的门禁,相当于系统的身份认证,通过门禁确认我属于小区的住户
进入小区后,处于会话管理的范畴内,当我只持有1幢的门禁和梯控,相当于只允许我访问某些功能。
而房间钥匙则对应着我的数据访问权限。
而小区的跑道和绿化则属于授权用户的公共资源, 小区外的其他则属于任何人都能访问的资源
小区中遍布的监控能够支持对行为的审计
总结
在访问控制方面,我们至少需要几点:
功能级别访问控制
针对用户数据或者其他资源的数据级的访问控制
梳理公共资源以及个人的资源
监控与审计
风险与处理
风险也围绕着我们在上文所说的几点
相关资源的访问控制(即门禁的设计)。
根据系统不同的需要,对不同的资源设置相应的访问权限。毕竟在一般情况下,我房间应该只有我以及我授权的相关人员能够进入
需要评估和确认访问权限设计的有效性,满足最小化的原则
对于资源默认的访问权限应当是拒绝
授权绕过/未授权访问
在系统变更过程中是否持续对资源进行梳理和监控
侧信道/信息推测
例子的描述: 房间里会有窗户,可能透过窗户能够看到一些 或者 分析你的生活习惯推测一些信息。 从描述来看,风险相较于其他几项较少
之前提到过,我们忽略了时间和空间的影响。在虚拟世界中可能会存在直接访问到我个人房间的情况,所以一般情况下我们首先需要去验证访问者是否持有认证通过后持有的虚拟身份。(一般这个操作在软件系统中会作为全局拦截器来实现,相当于将所有的资源纳入到门禁的范围内,避免在实现新增功能时,忘了考虑这类情况,从而产生风险【该类情况即使通过审计发现,也无法进行追溯】。)
对抗中间人
背景
在前文中提到的三大基石策略——身份认证、访问控制和会话管理,在通信过程都涉及到与远程服务器交换秘密信息,同时在实际的业务过程中,也包含了大量的个人隐私信息。
如果这些信息被窃取,可能会对社会、个人造成巨大的影响。
概念
在具体讨论怎么对抗中间人之前,我们首先来看看中间人到底是什么?
从名字来看,中间——那肯定是在什么之间。
信息系统常见几个重要的主体,应用、操作系统、网络链路、客户端、服务端,通信过程如下:
一般而言,我们所考虑的中间人攻击的情况是图中虚线的框框——网络设备,攻击者可能控制了相关的路由器或者交换机,进而对应用的相关数据包进行监听、篡改。
一般不考虑应用与操作系统、操作系统与网卡之间拦截,一方面由于这些操作都需要对客户端/服务端的操作系统进行控制,如果能进行控制,那么有其他更加丰富的方式获 取相关的数据,包括但不仅限于hook相关的技术、屏幕录像。 另一方面,由于需要获取操作系统的控制权,一般而言是个例,不具有普遍性,在资源有限的情况,不会进行考虑。同时在这类场景下,更关键是需要解决恶意攻击者获得操作系统控制权的问题。
当然,如果有特殊的要求确实需要纳入到考虑范围的情况,那肯定需要在应用层面去完成,自然是最方便的途径,从数据的源头进行防护,设计的要求同针对网络设备的中间人一致。
对抗中间人攻击,不可能去解决掉中间人,而不可能去保证每个人一个人的链路的安全。
需要解决如何在不可性的链路上去构建一个可信或者相对可信的链路。
风险与处理
风险其实在背景里已经提到了就是信息被窃取、篡改,而处理的解决方式就是需要去构建可信信道。
具体的风险可以细化成以下四种:
虚拟身份或者临时身份被窃取
重放
监听(隐私信息采集等)
业务数据包被篡改
从风险可知,构建的可信信道需要满足
数据被加密,防止被窃取身份,被采集信息
加密应该保证每个对象与对象之间不同(如果是现代密码学算法应该保证每组通信采用不同的密钥)。
需要支持完整性的校验
需要支持对抗重放数据——即每个数据包有自己的标识
已有技术
提到中间人,不得不提到的一定是SSL、TLS,以及结合http协议形成的https,一般情况其代码实现已经集成在操作系统中。
理想情况下,TLS或者SSL协议能够达成我们的目标,但是它们在构建可信信道的过程中,依赖于数字证书技术。如果不当使用数字证书,例如自签证书、不可信CA滥发证书,那么可信信道就无法构建。
无共享信息的可信信道,基本上无法建立,除了量子。
那么为了部分解决SSL/TLS的数字证书问题,只能采取增加部分预置信息的方式,例如HSTS——浏览器缓存证书,SSL Pinning——内置证书进行比较
注意事项
由于SSL/TLS是对抗中间人的完整性校验和对抗重放,重放的另一个威胁源来自客户端自身在应用层发起的请求,这类情况无法适用SSL/TLS。
一般建议在应用层面的核心业务,再次实现完整性校验和对抗重放的技术。例如交易。
异常处理
渗透测试的小伙伴应该会这样的体会——只有当输入信息与我们预期的正常情况存在出入的时候,才会引起注意,例如页面500报错、异常的业务流程、预计之外数据输出。
所以异常可以算是一切攻击的源头,如果所有的情况都能符合预期,那么我想攻击者的途径应该会少很多吧。
可惜的是人非圣贤,异常难以避免。
对研发而言,我们希望异常越清晰越好,查看异常越简单越好,能够协助我们尽快的定位bug,分析业务。
在攻击者在挖掘漏洞的过程,同样希望异常越清晰越好,与开发者们的预期一致,在频繁的上线和更新代码的过程中,经常会遗忘掉这些暗门,从而使攻击者能够从研发留下的痕迹中收获不少敏感信息。
所幸的是目前部分框架已经支持对全局异常的统一处理。
剩下的需要解决的是配置问题,在下文中也会单独的讲这个问题,不过在这个篇章里索性就先提一点。
研发人员和攻击者都关注异常信息,自然不可能把异常信息全都屏蔽,那么对于研发人员可能是一场灾难。
那么如何把两者划开来呢,测试环境自不必说,对于生产环境而言,攻击者能接触到的应用页面、通信的数据包,而研发人员在授权的情况下,理论上可以接触到所有数据的,所以我们可控制异常信息的输出,输出到攻击者无法接触的地方,例如操作系统的某个固定目录下、统一的日志收集平台。
当然有个后话,如何保证相关目录的安全、日志平台安全以及哪些数据需要隐藏又是我们需要继续考虑的问题。
注意事项
异常处理的手段其实在本质上并不能解决信息泄露的问题,只是通过对异常处理的控制,尽可能地降低从异常中获取的信息量,提高攻击者的攻击成本和利用难度,从而降低风险。
(没有任何回显,本身就是一种异常。只是造成这类异常的情况丰富且复杂,从而降低从异常中获取得信息量)
PS:信息量/信息熵等概念,可自行搜索了解,本质上是为了量化信息。
配置管理
对于应用系统而言,经常需要部署在不同的运行环境,我们引入了配置从而避免了因为环境的变动就需要对应用进行重新编码,重新测试的情况,同时各种各样的配置项可以支持各式各样的组件和程序不同的运行方式,极大的提高了效率。
场景一 不同的运行环境
跨平台的编程语言解决了应用需要在不同操作系统部署的问题,优化了大量的时间投入。但是它们没办法解决不同抽象的运行环境,例如开发环境、测试环境、准生产、生产环境,不同的抽象运行环境对应着不同的组件、网络以及信息安全的要求。
开发环境/测试环境:对于信息安全的要求应该是最低,同时也是输出信息最为丰富、系统最不稳定的。
准生产/生产环境:对于信息安全有明确的要求,仅保留必要的输出,系统最为稳定。
风险与处理
在从开发环境切换到准生产/生产环境,难免需要更新具体的配置,一般而言会考虑引入编译的配置选项来解决,实现一键切换,例如maven的profile属性,不同的属性值对应了不同的策略,包括不同的打包策略、不同的配置文件。
这类方式是提高系统可靠性的重要方式,但是也存在一些副作用,需要使用者进行控制。
如果管理不当,开发者有可能接触到生产环境的具体配置信息,对生产经营产生影响
如果打包策略/配置文件未进行检查和校验,导致多个环境信息被一起打包。
如果缺少对具体配置项的检查,导致生产环境采用了测试环境的配置,进而可能产生信息泄露事件。
各个组件(中间件、容器等)的配置。虽然目前有docker一类的容器技术,用于实现运行环境的标准化配置,但是标准化配置不是一个不再需要关注的点而是一个更加需要关注的点,试想如果某个标准化上配置存在弱口令账号或者所有的标准化环境共享一个账号,会带来什么样的风险。
场景二 日志管理
日志功能相关的组件越来越成熟,大多通过配置进行实现,索性就纳入到了配置管理模块。
日志是目前所有系统审计的重要依据,同时也是发现攻击者和内鬼的重要手段,但是随着SSL/TLS等相关加密方式普及,位于通信链路上的相关设备越来越难以捕获到明文信息,应用系统自身的日志显得越来越重要。 我想随着云相关技术的进一步推广,应用系统的日志会更加重要。
如何管理这些日志,采集哪些日志就是需要进行考虑的。
风险与处理
日志最好也能有全局的日志控制。
当然,如果需要最大发挥日志的价值,一般是需要汇集到统一的日志平台,用于支持搜索。而日志往往包含了最详尽的业务信息,从某种意义上而言,这些日志可能是在诱导犯罪。(不知道是不是对前文中关于临时身份有印象,拿到这些临时身份的令牌,就可以完成身份窃取)
一类风险是正式这些日志信息造成,我们需要有明确的规定有指导数据记录,例如敏感信息、个人隐私信息、令牌、身份等信息,不要存储全文,需要进行部分的模糊化。
另一类风险正是引入日志平台其本身带入的风险。
日志的具体配置不当,例如本地的日志文件存放到了web的相关目录,从而能直接访问,造成大量信息泄露。(相关实例可自行搜索)
场景三 权限配置
目前越来越来多的应用支持复杂的权限配置和资源配置,有效维护这类信息,能够大幅度降低安全风险。
具体涉及的权限内容,可查看访问控制策略章节了解
软件技术栈
概念
软件技术栈——我姑且这么写如果有更好的名称可以私信我,这里主要想提一提应用系统中那些无法进行掌控的第三方组件,例如java的第三方jar、框架、中间件。
对于现在的应用程序而言,从零开始的搭建已经有点不可能了,不是技术不可能,而是业务上不可能,我们需要速度,在已有轮子的前提下,肯定不需要再造轮子,即使造轮子,你有能力保证造的轮子就比别人的好使吗?
但是呢,这类第三方的代码与我们编写的代码在运行时共享的是同样的权限、系统资源,如果这些代码出现了问题,又当如何?
风险与处理
至少需要能管理、了解到底用了哪些第三方的代码。唯有这样,在相关的第三方出现安全问题时,能够快速响应,做到止损。(相关工具:SCA——软件成分分析软件)
尽可能不使用存在已知问题的组件,不使用停止维护/维护不当的第三方内容(如果是开源的,己方有能力维护可以不纳入考虑)。
敏感数据
敏感数据现在是属于法律法规领域最关注的一个点了,虽然互联网上的应用五花八门,但是所有这些虚拟身份的背后都是一个唯一的实体人。生命以负熵为食,人天生喜欢规律也是有规律的,大量数据可能能够重新去定义一个实体人,通过定向的投放去影响和控制人,甚至盗取这个人的现实身份,这也是为什么敏感数据会成为各国法律法规的关注点。
所以在应用系统设计的初期,我们必须需要明确哪些是所谓的敏感数据,以及围绕着敏感数据的生命周期要如何处置
哪些是敏感数据
个人隐私
CISSP ALL in One 在隐私章节里给出部分属于隐私的数据类型,如下:
敏感的业务数据
财务相关交易记录
…… 需要根据具体内容去决定
敏感数据的生命周期
一般而言,生命周期至少包括数据的产生、存储、使用、销毁,该过程映射到实际的应用系统中可能还需要进行更加细节的处理。
传输
应用系统涉及到信息交互,那么首先要新增的一个过程就是传输,在传输过程中的敏感信息要如何进行处理是需要进行明确。明文传输肯定是不行的。
产生
我觉得更明确的词,应该是采集。
采集就涉及到到底是采用完整的数据,还是部分关键即可,如果是部分关键,那么将采集出的数据直接转成Hash摘要可作为一种参考方式。
存储
避免明文存储
如果不需要明文的敏感数据,建议采用hash算法等方式转化数据,仅用于对比
如果需要明文的敏感数据进行操作,建议采用加密算法保障
其他情况
使用
使用可能涉及到多个场景,例如客户端页面的展示、业务操作的需要,如不必要进行展示,尽量不进行展示。
销毁
一般这类敏感数据是需要提供销毁数据的功能的,是真正从数据库清空相关信息,而不是通过标志位实现的假删除方式
注意: 这里的说明,仅仅是提供敏感数据相关策略的参考,具体内容以相关的法律法规以及业务自行设计。
输入输出
概念
输入与输出,绝大多数的漏洞都在体现在这方面,假如一个系统完全没有输入和输出,开个玩笑,如果真有这个系统,有和没有又有什么区别?
输入与输出,这两个词虽然简单但是里面缺少了一个东西——主体,什么东西的输入输出。
假如实体服务器,那么输入输出基本上就是我们在通信链路上来回输送的数据,但是这些数据有点驳杂,没有办法再进一步的处理,唯一能做是采用一些全局的过滤器或者拦截器,但是误杀又太高。
所有站在这个维度,虽然实现起来相对容易,但是把握度的难度会比较困难
假如把实体的粒度划的再细一点,定义成应用软件(软件,而非系统)。从这个维度来看,系统的输入和输出类型就丰富来起来
输入:来自文件系统上的文件、数据库中的数据、中间件/容器传递的业务数据、其他组件的数据输入、
输出:文件系统、数据库、其他组件、中间件/容器
此时的输入输出可以看得出与部分漏洞有了联系,例如与文件系统相关的文件上传漏洞、任意文件读取漏洞
记住,原则上一切的输入和输出都不可信,只是经过校验和过滤的数据才能提高可信度。
风险与处理
通过缩小实体的粒度,我们更加明确了输入与输出,后续的风险便是围绕着具体的输入信息和输出的信息进行分析,构建针对性的过滤和处理。
当然,这种方法因为缩小了粒度所以在实现上就要复杂的多,主要是分析工作量增大,需要有针对性。
引用
*本文原创作者:alka1d,本文属于FreeBuf原创奖励计划,未经许可禁止转载
来源:freebuf.com 2020-02-01 09:00:02 by: alka1d
请登录后发表评论
注册