<commit_before>##9.2 确保输入过滤 
用户数据的过滤是Web应用安全的基础。它是你验证数据合法性的过程。通过在输入时确认对所有的数据进行过滤，这样就可以避免攻击的数据在你的程序中被误信及误用。大多数Web应用的漏洞最终都是因为没有对用户输入进行恰当过滤造成的。

这里所指的过滤数据是指三个不同的步骤：

- 识别数据，搞清楚需要过滤的数据来自于哪里
- 过滤数据，弄明白我们需要怎么样的数据
- 区分已过滤及被污染数据，保证如果存在攻击数据那么过滤之后可以让我们使用更安全的数据

##识别数据
我把识别输入作为第一步是因为如果你不知道它是什么，来自于哪里，你也就不能正确地过滤它。输入是指所有源自外部的数据。例如，所有来自客户端的数据是输入，但客户端并不是唯一的外部数据源，其它如数据库和第三方提供的接口数据等也是外部数据源。

由用户输入的数据我们通过Go非常容易识别，Go通过`ParseForm`之后，把用户POST和GET的数据全部放在了`r.Form`里面。其它的输入要难识别得多，例如，`r.Header`中的很多元素是由客户端所操纵的。常常很难确认其中的哪些元素组成了输入，所以，最好的方法是把整个map看成用户输入。

##过滤数据
在知道数据来源之后，就可以过滤它了。过滤是一个有点正式的术语，它在平时表述中有很多同义词，如验证、清洁及净化。尽管这些大家平时所用的术语稍有不同，但它们都是指的同一个处理：防止非法数据进入你的应用。


##区分过滤数据

## links
   * [目录](<preface.md>)
   * 上一节: [预防CSRF攻击](<9.1.md>)
   * 下一节: [避免XSS攻击](<9.3.md>)

## LastModified 
   * $Id$<commit_msg>完成了第二小节过滤数据<commit_after>##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$
{'fraction_non_alphanumeric': 0.12106824925816023, 'fraction_numerical': 0.005044510385756677, 'mean_word_length': 10.274247491638796, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3027753', 'n_tokens_mistral': 2653, 'n_tokens_neox': 2977, 'n_words': 98}