“安全功能”

作者:我不知道该唱什么 发布时间:July 5, 2014 01:34:47 分类:tech

我们见过太多的业务逻辑被安全功能破坏的例子了,也见过太多安全人员写出来的号称安全的程序连基本的业务需求都胜任不了,到处都是关于过滤、转义的BUG。

很早之前和开发人员交流时候提出来的一个问题,当时一些开发同事困惑的是,对于恶意输入,程序当中需要做什么过滤以及应该在什么时候做过滤?
这几天在群里又恰好提到了这个问题。

这里涉及到“安全功能”在程序中应该如何部署。
我的看法是系统中可以有辅助的安全模块:日志、审计等不对业务流产生能动效果的功能,但是理想状况下不应该有专门为安全而设计的功能模块(比如一些框架提供的sql注入的过滤/转义模块等)。

这个看法是基于这个观点产生的:

1. 专门写出来安全功能,意味着和业务逻辑相脱离,这样容易产生两种现象:
1.1 安全逻辑打扰业务逻辑;
1.2 通过业务逻辑绕过安全逻辑。
2. 业务逻辑的鲁棒性可以提供安全性。

1.1 安全逻辑打扰业务逻辑


专门写出来安全功能,意味着和业务逻辑相脱离,这时候很容易出现的问题就是安全逻辑打扰到了业务逻辑。就很容易出现下面这些情况:


Blog系统是否应该在插入数据库之时就执行anti_samy()来一劳永逸得防止后续xss的产生?而sql过滤函数应该在其内过滤怎样的语句?过滤逻辑需要检测单引号亦或是select xx from yyy呢?
这里一个主要的困惑是安全功能到底需要保证什么

1.2 通过业务逻辑绕过安全逻辑


这里一时没找到相应的例子,想到一个不太恰当的例子是:
snort可以看做是一个安全逻辑,他从数据链路层开始接管整个流程,其对协议栈的实现在一定程度上是安全逻辑把一部分业务逻辑按照协议重新实现了一下。导致实现差异而产生绕过。
另外可能发生的情况是:
安全逻辑在不同的语义层上实现(比如在php层检查sql语句),导致利用应用中一些字符串转换逻辑(例如base64)等可以用来绕过相应安全逻辑。(这实际上也是在尝试把sql的语义分析逻辑重新在php层粗略实现了一次)。
两个例子实质上差不多,应该有其他更好的例子。这里实际上我们看到很多安全逻辑的绕过都是针对于其与业务逻辑差异性导致的:
1. 反向代理上waf的绕过: 反向代理和web服务器对http协议的实现可能不尽相同(比如win和linux上对请求路径“\”的处理);
2. 多字节字符集的问题:安全过滤中使用的不支持多字节字符集的字符串处理函数和目标业务功能(数据库、浏览器)对同一段二进制解析成字符串的行为不同。
跑题了。

2. 业务逻辑的鲁棒性可以提供安全性


比如申请表单:
表单中填写电话以及身份证号的输入框可能被利用来实施xss。
这时填写的电话以及身份证号必然不是具有实际意义的字符串,这在业务层面上是无意义的坏数据,需要提示用户重新输入。

比如Blog系统:
Blog系统当初的定义是类似于“可以写日志的系统”,而不是“可以写不带单引号的日志的系统”。所以没有任何人规定写的日志里不能带单引号,不能出现select xxx from yyy的字符。遇到单引号就崩溃的系统就没有保证其业务逻辑的完整实现。而用户也不希望自己想在blog里发select字样就会自动替换为空,或者报hacking attempt的警告,用户仅仅是想要发一篇带有select字样的日志。

比如上传附件:
程序员的意愿是可以上传任意文件,这时候上传php就可能会导致远程代码执行,但这里也违反了程序员的意愿:和上传照片等文件一样,程序员想让其上传后下载的是相同的文件,如果下载者访问被上传资源的url后,php在服务端执行了,这个附件功能对于php脚本来讲就会工作不正常:下载的内容是经过解释php执行后的html内容,而不是上传的php脚本本身。

业务逻辑的鲁棒性是指在任何情况下,有关程序的功能性都按照程序员预期的方式去运行。
而安全漏洞往往以为着,程序按照程序员预期之外的逻辑运行,如崩溃、执行设计之外的逻辑等。

那么对于开始的问题:程序当中需要做什么过滤以及应该在什么时候做过滤?安全功能到底需要保证什么?

  • 安全功能需要保证的是程序不能执行恶意逻辑,比如“rm -rf /”、无限制的消耗本地资源;

  • 而业务的鲁棒性在某种程度上需要做到的一点是程序在任何情况下程序都需要按照程序员的逻辑在执行。

  • 我们假定程序员是善意的,刚刚也说过这里讨论的安全功能不包括日志等审计功能。

    比如对于Blog系统,我们想要确定程序在插入数据库、取出数据库的时候需要对用户的输入作出什么行为,我们需要定义的是在数据库里存的东西在业务层面上是什么意义:是用户输入的原始文本,还是需要输出的html内容?

    如果定义了在数据库内存储的数据是原始文本,那么我们需要在sql语义层面上保证用户原始文本的正确插入(比如预处理、或者元字符的转义等),保证数据库存储内容意义的正确性,然后在后期使用这些数据的时候,比如输出的html,我们需要其在浏览器上正确显示出用户所输入的原意,就需要转义html元字符等操作;同理如果是输出到console,就需要注意转义一些终端控制字符等;
    如果定义了在数据库内存储的数据是需要输出的html内容,那么我们在插入数据库前,就需要对html元字符进行转义,因为用户输入的<并不是html元字符(这取决于这个系统的业务功能,即与用户之间形成的约定,blog系统一般提供给用户的功能就是:你输入一些内容,我给你展示这些内容,包括"<",系统需要正确地将其展示出来),所以要和真正的html区分开来,这种情况下就要在插入数据库前对html元字符进行转义,而此时数据库内存储的就是真正的html内容,后期使用仅仅需要向浏览器输出数据库内容即可。

    这些都是业务的逻辑,如果保证了其鲁棒性,那么程序的运行就不会出现程序员意愿之外的行为(但是需要保证业务逻辑本身不能被滥用,这里需要一些安全思维)。

    我们见过太多的业务逻辑被安全功能破坏的例子了,也见过太多安全人员写出来的号称安全的程序连基本的业务需求都胜任不了,到处都是关于过滤、转义的BUG。
    引用Linus的一段偏极端的话:

    安全漏洞臭虫只是众多臭虫的一种,过于拔高或赞美安全臭虫的修正者是走在错误的方向上。Linus大神称那帮整天炫耀 OpenBSD安全性的家伙其实是一群自慰的猴子,好像除了安全性,没有其它东西能让他们兴奋起来。安全是重要的,但不等于一切。

    程序员就是控制程序行为的,我们要做到程序所利用的技术栈从上到下都在自己的控制之中,而不依赖于动态数据(各种物理因素导致物理介质变化的因素就考虑不了了)。
    也许我们需要做的仅仅是让程序更鲁棒一些。

    现在有点烦躁 想的和写的都比较乱,现在也不想回去再整理和修改了,先这样放着,不妥当的地方以后再回来改。

    标签: none

    添加新评论 »