在应用程序安全性中,如果不处理用户输入及其相关数据,可能会带来安全风险。
而注入是使用最广泛,以及最古老和非常危险的漏洞之一,在 OWASP Top 10 从 2010 开始一直到现在都是第一,还是挺有趣的。
注入是将不受信任的数据作为命令或查询的一部分发送到解析器时,会产生诸如SQL注入、NoSQL注入、OS注入和LDAP注入的注入缺陷。
攻击者的恶意数据可以诱使解析器在没有适当授权的情况下执行非预期命令或访问数据。
例如,我们需要查询用户输入的用户名和密码是否对应。
SELECT * FROM Users WHERE username= $username AND password= $password;
如果未经处理用户的输入,当用户的输入中存在’--
或者 ‘ or ‘1’=’1
,那可能不仅仅只是代码没有进行预期的工作,还可能泄露信息。
一般情况下,我们使用验证和清理来解决这些问题。
这些验证应根据服务器的功能在应用程序的每一层中执行。
要说明是,所有数据验证过程都必须在受信系统(服务器)上完成。
当应用程序处理用户数据时,默认情况下必须将提交的数据视为不安全,并且只有在进行了适当的安全检查后才可以接受。
还必须将数据源标识为可信或不可信,如果是不可信源,则必须进行验证检查。
在验证检查中,将根据一组条件检查用户输入,以确保用户确实输入了期望的数据。
如果验证失败,则拒绝输入。
这不仅从安全角度来看很重要,而且从数据一致性和完整性的角度来看也很重要,因为数据通常在各种系统和应用程序中使用。
Golang 标准库中其实有了很多可以用于检查输入的包,strconv 类型转换、strings 字符串处理、regexp 正则等。
如何确保数据的有效性?
- 白名单机制,尽可能根据白名单来验证输入
- 边界检查,数据长度均应进行验证
- 字符转义,用于特殊字符,例如独立的引号
- 数值验证
- 空字节
- 换行符
- 路径变更字符
../ \\..
- UTF-8 字符替代表示
完成了这些检查,我们就能保证数据在一定程度上是有效的。
为什么说是一定程度上的,因为还没有做完呢。
如果用户输入的数据是用于读取文件的,那么是不是应该还需要检查一下文件,当然,还会有访问控制和权限管理,这些放到之后在讨论。
数据完整性
主要还需要说一下数据源,当数据的来源可信度较低的时候,都应该进行一些检查,确保数据没有被篡改。
- 一致性检查
- 唯一性检查(如果有的话)
- 哈希校验
- 引用完整性
当然,对于数据完整性的内容还是推荐自己去看看,这里的主要内容还是 OWASP Top 10。
我们完成了输入验证,一般都还需要执行验证后的操作。
比如,告知用户提交的数据不符合要求,应该修改数据以符合要求的条件。
在这一步骤中,我们需要注意的一个题外话,OWASP Top 10 并不是威胁最大的漏洞,威胁最大的可能是社工。
比如,攻击者并不想进行注入攻击,他只想知道这个手机号是否是这个程序、网站的注册用户。
如果我们程序对于手机号、密码错误返回的提示不同(用户不存在、密码错误),那么攻击者实际上就已经拿到了他想要的信息。
一般情况下,推荐后端只返回失败,不告知具体原因。
言归正传,我们检查了数据,但是没有做安全处理,我们还需要给数据进行消毒。
消毒是指删除、替换数据。
- 比如在 html 中将 < 转意为 <
- 剥离 html 标签
- 某些场景下(文件名)替换非 ASCII 字符
- 删除换行符、制表符、多余的空格
- URL 路径清理
注入攻击一直不怎么好防御,因为多数情况下是需要维护一个老系统,很多地方不能更改,动起来都碍手碍脚的。
在这种情况下,除了上述方法还可以通过其他手段来达到目的。
- 权限控制,比如对于某张表只能查、对于另一张表没有查权限
- 白名单不好整,黑名单还是能使的
- 弄个蜜罐,想怎么玩就怎么玩