很多师傅们都知道,大多数攻击的起源,尤其是应用层渗透的起源,都源于恶意数据的输入,以及多数安全设备的基础原理,基于恶意数据进行拦截,随着安全左移思想的深入,安全编码/安全设计的第一条即为输入过滤,输出消毒,这里我和大家简单聊聊输入过滤问题。
在这里不聊具体的漏洞,以系统的梳理数据输入源来分析如何去做好输入验证工作。
输入验证应该在语法和语义两个层面上进行:
语法验证应该强制执行结构化字段(例如社会安全号码、日期、货币符号)的正确语法。保障可用性。
语义验证应该强制执行它们在特定业务环境中的值的正确性(例如开始日期在结束日期之前,价格在预期范围内)。保障安全性。
输入验证的作用:
一是可以防止恶意攻击,是防止注入类漏洞、上传类攻击的首道防线;
二是确保数据的完整性和合规性,尤其在输入敏感数据时;
三是可判断输入的数据是否满足既定规则要求,避免程序错误和系统崩溃(比如输入身份证号,必须要是XX位的数字等);
四是能够提升用户体验,在输入错误情况下能给予友好提示并及时处理。
数据的输入源:
数据的输入源有很多,不仅是用户输入,还有程序输入,其核心区别点在于是否能够被控制,在实际开发过程中,可以通过数据流梳理的方式全面梳理数据的输入源
随着应用的复杂度不同,数据的输入源也不同,一般来说主要分为以下几种:
用户输入:
- 用户直接输入,如网页提交的数据、用户名、密码、查询图书等,通过GET、POST等传输
- 用户通过API提交的数据,包括APP端和后端交互过程用到的API
- 用户上传的文件
- 用户的URL参数和查询字符串
第三方数据——非本服务的服务,均为第三方服务
- 从其他系统或API获取的数据,常见于程序之间互相调用,包括微服务内部,服务与服务直接调用,即使这些数据在源头进行了验证,也可能在传输过程中被篡改。
- 用户通过OAuth、OpenID等第三方身份验证服务登录时传递的数据。
- 合作伙伴或供应商提供的数据(供应链的范畴)
- 环境变量和配置参数:
- 应用程序可能从环境变量或配置文件中读取敏感信息(如数据库连接字符串、API密钥等),这些也需要验证其来源和格式。
- 应用程序配置中的动态部分,如根据部署环境变化的设置。
- 未经验证的HTTP请求头:如User-Agent、Referer等,这些头信息可能被用于跟踪用户或误导系统行为。
- 自定义的HTTP请求头,它们可能包含未预期的数据或格式。
系统生成的输入:
- 虽然系统生成的输入通常被认为是可信的,但在某些情况下(如系统被黑客控制时),这些输入也可能包含恶意内容。
数据输入验证措施:
- 数据输入白名单:白名单机制永远都要比黑名单机制更好用,即只允许符合特定格式或范围的输入通过。比如很简单身份证输入功能,需要分析身份证的规则(18位数字或17位数字+一个大写字母)这个时候输入验证就不允许客户输入字符串,注意,这里的验证需要放在后端,任何前端的验证都是不可信的
- 转义和编码:对输入数据进行适当的转义或编码,以防止其被解释为代码执行。
- 使用安全的库和框架:选择经过安全审计的库和框架来处理输入,它们通常提供了内置的安全机制。如Apache Commons Validator、Spring Framework Validation等,这个可以根据自己所用到的语言来选取具体的框架
- 限制输入长度:避免超长输入导致的缓冲区溢出等问题。这里也需要去和对应的业务场景结合,
- 监控和日志记录:对所有输入进行监控和日志记录,以便在发生安全事件时进行追溯和分析。这里需要运维+安全共同配合落地。
通过这些措施,可以大大降低因恶意输入而导致的安全风险。注:在不影响性能的前提下,建议所有的验证措施都放在后端进行,尤其是用户身份标识和验证,这个必须放在后端验证。服务器端验证能够防止攻击者通过修改客户端代码或网络请求来绕过验证机制。攻击者可能试图在客户端拦截和修改用户输入的数据,以模拟合法用户的身份。
服务器端验证的方式包括用户名和密码、token、数字证书等,这里我在前面的文章写过,我就不再详细说明。
还有一个点比较重要,即关键参数需要存在服务器,而不是放在客户端拼接,原因也是一样,同时输入的拼接内容也需要进行规范化处理以后再做验证,比如文件路径、URL地址等
数据输入内容的规范化处理
我梳理了几点在数据输入过程中的一些原则性要求,可以做成安全基线供开发的