WEB2.0的普及,丰富了各类WEB产品。WEB交互能力的增强,也滋生出种类繁多的安全威胁,用户输入便成了万恶之源,不仅威胁用户信息安全,也给服务器、操作系统,甚至整个局域网带来灾难,因此,验证用户输入是WEB应用必不可少的安全防范举措。
1 输入为何需要验证
输入验证一般基于两个方面的原因:一是为了保证业务功能的合理性,二是为了保证用户数据、应用程序及内部系统和网络的安全。
1.1 业务层面
从业务的有效性和合理性来说,用户提交的参数都需要进行验证。在业务层面可能要求用户名只能包含大小写字母、数字,长度必须小于12位等;密码必须同时包含字母、数字、特殊字符,且长度必须大于8位等;金额必须在(0~1000]之间……诸如此类,我们的应用程序都需进行检测。可以想象,在金融类系统中,如果不对金额进行有效性检查,在转账类业务中,如果提交转账金额-10000¥,那是不是等同于对方给我转了10000¥呢。
1.2 安全层面
从另一个安全层面来说,输入验证则显得尤为重要,而这一点恰恰容易被开发者所忽略,众多的WEB漏洞中,无不因输入引起。如果你的应用中涉及如下功能,那么就得考虑是否有相应的验证和防护措施。
类型 | 功能 | 安全威胁 | 说明 |
输入 ↓ 服务器端 响应 | 输入作为响应body的一部分 | XSS | |
XXE | XML作为参数传递 | ||
输入作为响应header的一部分 | CRLF | | |
输入作为邮件的一部分 | SMTP注入 | | |
响应中包含输入指定的文件 | 目录遍历 | | |
重定向至输入指定的网页 | 任意重定向 | | |
输入 ↓ 服务器端 执行代码 | 输入嵌入SQL语句中 | SQL注入 | |
输入嵌入XPath表达式 | XPath注入 | | |
输入嵌入LDAP语句 | LDAP注入 | | |
输入作为应用代码执行 | OS命令注入 | | |
文件上传 | 后门 | | |
输入 ↓ 服务端 API参数 | 服务器端包含输入指定的文件 | 文件包含 | |
服务器端请求输入的网址 | 服务器端HTTP重定向 | | |
不同应用之间通过输入参数互相调用 | SOAP注入 | 使用SOAP | |
JSON注入 | 使用JSON | ||
XML注入 | 使用XML | ||
HTTP参数注入 | | ||
…… | |
表1-1功能和安全威胁对照表
2 哪些输入要素需要验证
上面已经提到如果不验证输入带来的威胁,那到底哪些输入需要验证呢?这就得先了解信任边界。
2.1 信任边界
信任边界是以组件或者功能单元进行区分,在信任边界外的所有输入均需验证。下图展示了一个典型的信任边界验证的例子(各个组件在执行操作前,均需输入验证):
图2-1 信任边界确认
(1) 应用程序收到用户提交的输入。表单处理程序确认每个输入仅包含的合法字符、特殊长度限制,应转义或者过滤任何已知的攻击特征。
(2) 应用程序执行SQL查询。为防止SQL注入, DAO执行查询前,应用程序应对用户输入中包含的可用于攻击数据库的所有字符转义。
(3) 将用户提交的某些输入数据提交给SOAP服务,进一步获取相关数据。为防止SOAP注入,SAOP查询组件需要对用户提交数据中的任何XML元字符进行适当编码。
(4) 应用程序在用户的浏览器中显示响应数据。为防止跨站脚本攻击,输出组件需对植入返回页面中的任何用户提交数据执行HTML编码。
2.2 输入来源
在WEB应用中,输入来源基本可分为两类:一类由用户提交,另一类则来自于共享数据。用户提交的数据主要包括:
(1) 查询参数
(2) POST参数
(3) Cookie
(4) Referer
(5) REST参数
共享数据主要包括:
(1) 共享数据库
(2) 共享文件数据
(3) 网络数据
3 如何验证输入
输入验证的方式按照输入的类型有所不同,输入的类型可归纳为两类:常规参数和文件。当然,这里不涉及业务层面的合理性验证。
3.1 常规参数的输入验证
常规参数的输入验证方法需要根据特定的功能而定,主要有如下方法:
方法 | 说明 | 备注 |
字符转义 | 对影响程序结构和逻辑,以及改变输出效果的特殊字符均需转义。 | 效果比较好,且不影响业务功能。 |
字符过滤 | 对影响程序结构和逻辑,以及改变输出效果的特殊字符均需过滤。 | 效果比较好,但特殊字符可能与业务要求冲突。 |
白名单 | 只接受特定的输入,其他均忽略。 | 效果非常好,但适用性低,适合需要严格检查的业务。 |
黑名单 | 不接受特定的输入,其他均接受。 | 效果差,特别针对XSS等此类变种很多的安全威胁。 |
映射 | 建立映射关系,外部使用映射对象,而非直接使用源数据 | 效果非常好,但适用性低,适合需严格检查且不希望泄露源数据的业务。 |
表3-1 常规参数输入验证方法
如下针对各类安全威胁,列举了行之有效的参数输入验证方法:
类型 | 安全威胁 | 输入验证方法 |
输入 ↓ 服务器端 响应 | XSS | 纯文本的参数:输出时转义<、>、&、{、}、#、"、'、;、/、[、]为HTML实体,对于DOM型XSS,则需确保JS在使用参数前,转义这些特殊字符。 富文本的参数: 1、 格式化输入,保证所有数据均可被识别和标准化; 2、 采用白名单的机制,明确允许出现哪些标签、哪些属性,对不允许出现的标签和属性值进行干扰或移除; 3、 对特定的属性值进行检查,比如:url必须以http://或https://开头,或者以/开头等。 4、 对标签内的特殊字符(<、>、"、'等)采用HTML实体转义。 |
XXE | 优选方案:在解析XML时,不解析外部实体。 | |
CRLF | 移除%0D%0A或者其他形式的回车换行符。 | |
SMTP注入 | 移除%0D%0A或者其他形式的回车换行符。 正文中不允许仅包含.的消息行。 | |
目录遍历 | 对输入解码和规范化,拒绝包含、/以及空字符的请求。 检查是否请求相应目录、相应后缀的文件。 或者使用白名单指定允许访问的文件列表,拒绝其他文件的访问。 | |
任意重定向 | 对输入解码和规范化,检查重定向地址是否在合法域内。 | |
输入 ↓ 服务器端 执行代码 | SQL注入 | 参数化查询。 |
XPath注入 | 仅允许字母数字。 拒绝包含(、)、=、'、[、]、:、,、*、/,以及所有空白符的请求。 | |
LDAP注入 | 仅允许字母数字。 拒绝包含(、)、;、,、*、|、&、=,以及空字符的请求。 | |
OS命令注入 | 避免直接调用操作系统命令。 白名单限制允许调用的系统命令,拒绝白名单之外的命令执行。 | |
输入 ↓ 服务端 API参数 | 文件包含 | 白名单限制允许包含的文件,拒绝白名单之外的文件包含。 |
服务器端HTTP重定向 | 建立重定向地址的强随机映射表,外部提交随机字符,应用程序则根据随机字符匹配重定向地址,拒绝无法映射的重定向。 | |
SOAP注入 | 解码后转义<、>、/ 为<、>、/。 | |
XML注入 | 解码后转义<、>、/ 为<、>、/。 | |
JSON注入 | 解码后转义'、"、、{、}、[、]为'、"、\、{、}、[、]。 | |
HTTP参数注入 | 解码后转义&为%26 |
表3-2 常见安全威胁输入验证方法
3.2 文件上传的输入验证
文件上传时,如果验证不当,则很可能被用于上传恶意文件,也就是说,服务器以及内部网络可能完全沦陷。一般地,需对上传文件执行如下检查:
(1) 文件类型:最好使用endWith检查上传文件名中的文件类型。
(2) 文件头:检查二进制文件的文件头是否为白名单文件类型的文件头。
(3) 文件格式:检查文件的格式是否为白名单文件类型的格式。
以上只是简单总结WEB应用常见安全威胁的一般输入验证方法,在具体的项目开发过程中,还需要有针对性的调整验证策略。希望此文能给各位开发、测试人员以及白帽子们有所帮助。