俗话说病从口入,可以说绝大多数的应用安全问题的源头都是由输入的入口引发,但是输入入口安全检测不能解决所有的潜在安全问题,原因很简单,那就是接收输入的数据时,接收者并不知道此数据用来做什么,涉及的业务逻辑是什么,所以无法在输入入口处进行完备而妥善的安全检测与处理。但是,还是要强调一下这个“但是”,我们不能因为此处不能解决所有的问题而放弃它忽略它,本文将告诉你:做好完善合理的“输入验证”,可以解决50%(只是经验值,后文不再解释)以上的企业应用安全攻击。
概念解释:
1.输入
广义的“输入”是相对于发送端与接收端的一个相对的概念,对于基于HTTP的web应用程序,那就是来自于浏览器这一端发往HTTP服务端请求数据包皆为输入,以此类推,请求端的所有数据对于服务端来说皆为数据输入。
2.输入验证
就是在服务端(强调一下:是服务端,而不是客户端)依据业务逻辑的需要对来自于客户端数据进行合法性校验的过程。
我们先从两个大家最熟悉的安全攻击开始来看看它们与输入的关系是什么。
例1,XSS(跨站脚本攻击),大家可以在firefox上试一下这个测试链接:
http://www.testfire.net/search.aspx?txtSearch=<script>alert%2812345%29<%2Fscript>
这就是一个典型的反射式跨站脚本攻击,它的过程是这样的:字符串<script>alert%2812345%29<%2Fscript>通过HTTP的请求传送到服务端,服务端经过自己的业务逻辑处理后返回到浏览器这一端,由于返回后未做正确的编码,以至于在客户端被浏览器执行。
例2,SQLInjection(SQL注入攻击),大家也可以在firefox上测试一下这个链接:
这是一个典型的SQL注入问题,它的过程是这样的,字符串admin%27+or+%271%27%3D%271的原型是:admin’or’1′=’1,这显然是通过猜测应用程序后台的实现方式,假设后台程序实现人员通过把用户的输入拼装到SQL查询语句上进行SQL查询的,以至于轻易的被黑客注入自编的SQL语句,骗过登录验证逻辑而成功以管理员身份登录。
这两个例子当中,输入项有许多,我们只关注txtSearch/uid/passw这三个输入点,它们是这两个问题的输入源,对它进行输入验证是否有意义呢?当然有。假如我们的业务逻辑上只要求txtSearch/uid/passw这个输入点长度只需要15,数据类型只需要数字与英文字母即可满足产品的设计需求,那么我们则可以这么做:
1)服务端获取txtSearch/uid/passw的值
2)确认它的长度是否超过15
3)它的值是否只包含字母与数字
后台实现上,只有以上条件完全满足,再进行进一步业务逻辑处理,否则以友好的方式告知客户端输入非法。当然,为了更好的用户体验,可以在客户端(假如是HTTPweb应用程序)用JS实时的动态提示用户本处可以接受的合法输入是什么。这么做的好处是什么?
1)对于第一个例子,如果我们因为疏忽或者某些未预料原因而导致XSS问题,由于你的限制足够苛刻,以至于即便有XSS问题,黑客也难以对其进行很好的利用,因为有长度限制,也有字符集的限制。
2)对于第二个例子,如果我们真的因为实现上的疏忽而留下了SQL注入的安全问题,也会因为字符集、字符串长度的限制而让黑客无法进一步利用它做更多的坏事儿。
以上只是抛砖引玉,象目录遍历、文件上传、缓冲区溢出等等诸多应用安全问题,均可通过合理的正确的输入验证方式减少、降低甚至避免安全攻击的发生。
关于输入验证,个人将其分为两大类,一类是普通的字符串输入,绝大多数是属于这一类;另一类是支持富文本的输入,比如文档编辑、允许用户输入简单标签的输入点。对于第二类,可以使用OWASP提供的AntiSamy(https://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project)框架来辅助解决。
最后强调一下就是:我并没有想表达做好输入验证就可以解决所有的安全问题,它的重要程度、利害关系及意义通过以上的描述,我想已经表达清楚了。做好输入验证的座右铭是:永远不要相信来自于客户端的输入,这里的输入不仅仅是显式的用户直接输入,同时也包括隐式的协议数据包部分。