9.2 确保输入过滤
我们介绍的过滤数据分成三个步骤:
- 1、识别数据,搞清楚需要过滤的数据来自于哪里
- 2、过滤数据,弄明白我们需要什么样的数据
- 3、区分已过滤及被污染数据,如果存在攻击数据那么保证过滤之后可以让我们使用更安全的数据
“识别数据”作为第一步是因为在你不知道“数据是什么,它来自于哪里”的前提下,你也就不能正确地过滤它。这里的数据是指所有源自非代码内部提供的数据。例如:所有来自客户端的数据,但客户端并不是唯一的外部数据源,数据库和第三方提供的接口数据等也可以是外部数据源。
由用户输入的数据我们通过Go非常容易识别,Go通过之后,把用户POST和GET的数据全部放在了r.Form
里面。其它的输入要难识别得多,例如,r.Header
中的很多元素是由客户端所操纵的。常常很难确认其中的哪些元素组成了输入,所以,最好的方法是把里面所有的数据都看成是用户输入。(例如r.Header.Get("Accept-Charset")
这样的也看做是用户输入,虽然这些大多数是浏览器操纵的)
过滤数据有很多种方法,其中有一些安全性较差。最好的方法是把过滤看成是一个检查的过程,在你使用数据之前都检查一下看它们是否是符合合法数据的要求。而且不要试图好心地去纠正非法数据,而要让用户按你制定的规则去输入数据。历史证明了试图纠正非法数据往往会导致安全漏洞。这里举个例子:“最近建设银行系统升级之后,如果密码后面两位是0,只要输入前面四位就能登录系统”,这是一个非常严重的漏洞。
过滤数据主要采用如下一些库来操作:
- strconv包下面的字符串转化相关函数,因为从Request中的
r.Form
返回的是字符串,而有些时候我们需要将之转化成整/浮点数,、ParseBool
、ParseFloat
、ParseInt
等函数就可以派上用场了。 - regexp包用来处理一些复杂的需求,例如判定输入是否是Email、生日之类。
过滤数据除了检查验证之外,在特殊时候,还可以采用白名单。即假定你正在检查的数据都是非法的,除非能证明它是合法的。使用这个方法,如果出现错误,只会导致把合法的数据当成是非法的,而不会是相反,尽管我们不想犯任何错误,但这样总比把非法数据当成合法数据要安全得多。
- 每个请求都要初始化CleanMap为一个空Map。
- 加入检查及阻止来自外部数据源的变量命名为CleanMap。
接下来,让我们通过一个例子来巩固这些概念,请看下面这个表单
在处理这个表单的编程逻辑中,非常容易犯的错误是认为只能提交三个选择中的一个。其实攻击者可以模拟POST操作,递交name=attack
这样的数据,所以在此时我们需要做类似白名单的处理
上面代码中我们初始化了一个CleanMap的变量,当判断获取的name是astaxie
、herry
、三个中的一个之后
,我们把数据存储到了CleanMap之中,这样就可以确保CleanMap[“name”]中的数据是合法的,从而在代码的其它部分使用它。当然我们还可以在else部分增加非法数据的处理,一种可能是再次显示表单并提示错误。但是不要试图为了友好而输出被污染的数据。
数据过滤在Web安全中起到一个基石的作用,大多数的安全问题都是由于没有过滤数据和验证数据引起的,例如前面小节的CSRF攻击,以及接下来将要介绍的XSS攻击、SQL注入等都是没有认真地过滤数据引起的,因此我们需要特别重视这部分的内容。
- 上一节: 预防CSRF攻击