Files
build-web-application-with-…/9.2.md
2012-10-15 18:10:13 +08:00

73 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
##9.2 确保输入过滤
用户数据的过滤是Web应用安全的基础。它是你验证数据合法性的过程。通过在输入时确认对所有的数据进行过滤这样就可以避免攻击的数据在你的程序中被误信及误用。大多数Web应用的漏洞最终都是因为没有对用户输入进行恰当过滤造成的。
这里所指的过滤数据是指三个不同的步骤:
- 识别数据,搞清楚需要过滤的数据来自于哪里
- 过滤数据,弄明白我们需要怎么样的数据
- 区分已过滤及被污染数据,保证如果存在攻击数据那么过滤之后可以让我们使用更安全的数据
##识别数据
我把识别输入作为第一步是因为如果你不知道它是什么,来自于哪里,你也就不能正确地过滤它。输入是指所有源自外部的数据。例如,所有来自客户端的数据是输入,但客户端并不是唯一的外部数据源,其它如数据库和第三方提供的接口数据等也是外部数据源。
由用户输入的数据我们通过Go非常容易识别Go通过`ParseForm`之后把用户POST和GET的数据全部放在了`r.Form`里面。其它的输入要难识别得多,例如,`r.Header`中的很多元素是由客户端所操纵的。常常很难确认其中的哪些元素组成了输入所以最好的方法是把整个map看成用户输入。
##过滤数据
在知道数据来源之后,就可以过滤它了。过滤是一个有点正式的术语,它在平时表述中有很多同义词,如验证、清洁及净化。尽管这些大家平时所用的术语稍有不同,但它们都是指的同一个处理:防止非法数据进入你的应用。
过滤数据有很多种方法其中有一些安全性较但最好的方法是把过滤看成是一个检查的过程在你使用数据之前都检查一下是否符合你的设计。请不要试图好心地去纠正非法数据要让你的用户按你的规则去做历史证明了试图纠正非法数据往往会导致安全漏洞。这里举个例子最近建设银行系统升级之后密码后面两位是0的只要输入前面四位就能登录系统一样这是一个非常严重的漏洞这个漏洞应该就是纠正非法数据引起的这种安全漏洞是非常严重的。
一般过滤数据主要采用如下一些库来操作:
- strconv包下面的字符串转换因为我们`r.Form`返回的是字符串我们有些时候需要固定的整数就需要使用改包来转换例如Atoi、ParseBool、ParseFloat、ParseInt等函数。
- string包下面的一些过滤函数Trim、ToLower、ToTitle等函数能够获取我们想要的格式信息。
- regexp包来判断一些复杂的需求例如是否是Email、生日之类的
过滤数据除了检查验证之外,还可以在可能时用白名单方法。白名单是指你需要假定你正在检查的数据是非法的,除非你能证明它是合法的。使用这个方法,如果出现错误只会导致把合法的数据当成是非法的,尽管不想犯任何错误,但这样总比把非法数据当成合法数据要安全得多。
##区分过滤数据
如果完成了上面的两步数据过滤的工作就基本完成了但是在编写Web应用的时候我们还需要区分已过滤和被污染数据因为这样可以保证过滤数据的完整性而不影响输入的数据。我们约定把所有经过滤的数据放入一个叫全局的Map变量中(CleanMap)。你需要用两个重要的步骤来防止被污染数据的注入:
- 每个请求都要初始化CleanMap为一个空Map。
- 加入检查及阻止来自外部数据源的变量命名为CleanMap。
接下来为了巩固这些概念,我们通过一个例子来说明这些概念,请看下面这个表单
<form action="/whoami" method="POST">
我是谁:
<select name="name">
<option value="astaxie">astaxie</option>
<option value="herry">herry</option>
<option value="marry">marry</option>
</select>
<input type="submit" />
</form>
在处理这个表单的编程逻辑中非常容易犯的错误是认为只能提交三个选择中的一个。其实攻击可以模拟POST操作递交`name=attack`这样的数据,所以我们在处理的时候需要做类似白名单的处理
r.ParseForm()
name := r.Form.Get("name")
CleanMap := make(map[string]interface{}, 0)
if name == "astaxie" || name == "herry" || name == "marry" {
CleanMap["name"] = name
}
上面代码中我们初始化了一个CleanMap的变量当判断获取的name在astaxie、herry、marry三个之后我们把数据存储到了CleanMap之中这样就可以确保CleanMap["name"]中的数据是合法的从而在代码的其它部分使用它。当然我们还可以在else部分增加非法数据的处理一种可能是再次显示表单并提示错误。但是不要试图为了友好而输出被污染的数据。
上面的方法对于过滤一组已知的合法值的数据很有效,但是对于过滤有一组已知合法字符组成的数据时就没有什么帮助。例如,你可能需要一个用户名只能由字母及数字组成:
r.ParseForm()
username := r.Form.Get("username")
CleanMap := make(map[string]interface{}, 0)
if ok, _ := regexp.MatchString("^[a-zA-Z0-9].$", username); ok {
CleanMap["username"] = username
}
## links
* [目录](<preface.md>)
* 上一节: [预防CSRF攻击](<9.1.md>)
* 下一节: [避免XSS攻击](<9.3.md>)
## LastModified
* $Id$