扫码订阅《 》或入驻星球,即可阅读文章!

GOLANG ROADMAP

阅读模式

  • 沉浸
  • 自动
  • 日常
首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
author-avatar

GOLANG ROADMAP


首页
Go友会
  • 城市
  • 校园
Go学院
  • Go小课
  • Go小考
  • Go实战
  • 精品课
Go求职
  • 求职辅导🔥
  • Offer收割社群
  • 企业题库
  • 面试宝典
Go宝典
  • 在线宝典
  • B站精选
  • 推荐图书
  • 每日博文
Go仓库
实验区
  • Go周边
  • Go下载
  • Go月刊
消息
更多
  • 用户中心

    • 我的信息
    • 推广返利
  • 玩转星球

    • 星球介绍
    • 角色体系
    • 星主权益
  • 支持与服务

    • 联系星主
    • 成长记录
    • 常见问题
    • 吐槽专区
  • 合作交流

    • 渠道合作
    • 课程入驻
    • 友情链接
  • Go语言Web编程

    • 课程介绍
  • Web基础

  • 表单

  • 访问数据库

  • session和数据存储

  • 文本处理

  • Web服务

  • 安全与加密

    • 第1节:安全与加密
    • 第2节:预防CSRF攻击
    • 第3节:确保输入过滤
    • 第4节:避免 XSS 攻击
    • 第5节:避免 SQL 注入
    • 第6节:存储密码
    • 第7节:加密和解密数据
    • 第8节:小结
  • 国际化和本地化

  • 错误处理,调试和测试

  • 部署与维护

  • 如何设计一个Web框架

  • 扩展Web框架

扫码订阅《 》或入驻星球,即可阅读文章!

第3节:确保输入过滤


ASTA谢

过滤用户数据是 Web 应用安全的基础。它是验证数据合法性的过程。通过对所有的输入数据进行过滤,可以避免恶意数据在程序中被误信或误用。大多数 Web 应用的漏洞都是因为没有对用户输入的数据进行恰当过滤所引起的。

我们介绍的过滤数据分成三个步骤:

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

# 识别数据

“识别数据”作为第一步是因为在你不知道“数据是什么,它来自于哪里”的前提下,你也就不能正确地过滤它。这里的数据是指所有源自非代码内部提供的数据。例如:所有来自客户端的数据,但客户端并不是唯一的外部数据源,数据库和第三方提供的接口数据等也可以是外部数据源。

由用户输入的数据我们通过Go非常容易识别,Go 通过 r.ParseForm 之后,把用户POST和GET的数据全部放在了 r.Form 里面。其它的输入要难识别得多,例如,r.Header 中的很多元素是由客户端所操纵的。常常很难确认其中的哪些元素组成了输入,所以,最好的方法是把里面所有的数据都看成是用户输入。(例如 r.Header.Get("Accept-Charset") 这样的也看做是用户输入,虽然这些大多数是浏览器操纵的)

# 过滤数据

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

过滤数据有很多种方法,其中有一些安全性较差。最好的方法是把过滤看成是一个检查的过程,在你使用数据之前都检查一下看它们是否是符合合法数据的要求。而且不要试图好心地去纠正非法数据,而要让用户按你制定的规则去输入数据。历史证明了试图纠正非法数据往往会导致安全漏洞。这里举个例子:“最近建设银行系统升级之后,如果密码后面两位是 0,只要输入前面四位就能登录系统”,这是一个非常严重的漏洞。

过滤数据主要采用如下一些库来操作:

  • strconv 包下面的字符串转化相关函数,因为从 Request 中的 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>
1
2
3
4
5
6
7
8
9

在处理这个表单的编程逻辑中,非常容易犯的错误是认为只能提交三个选择中的一个。其实攻击者可以模拟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
}
1
2
3
4
5
6

上面代码中我们初始化了一个 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
}
1
2
3
4
5
6

# 总结

数据过滤在 Web 安全中起到一个基石的作用,大多数的安全问题都是由于没有过滤数据和验证数据引起的,例如前面小节的 CSRF 攻击,以及接下来将要介绍的 XSS 攻击、SQL 注入等都是没有认真地过滤数据引起的,因此我们需要特别重视这部分的内容。

  • 识别数据
  • 过滤数据
  • 区分过滤数据
  • 总结