尽管我们在安全工具上投入了大量的成本,代码库仍可能是任何组织的网络安全中最薄弱的环节。净化和验证输入通常是第一层防御。
多年来攻击者一直在钻典型漏洞的空子,成功率相当高。虽然高级威胁分子采用更复杂的方法,比如对抗性机器学习、高级混淆和零日漏洞,但典型的攻击技术依然是最常见的攻击,比如SQL注入、跨站脚本(XSS)、远程文件包含(RFI)和目录遍历。
这些技术通常是提升权限和横向移动的第一步。这就是为什么开发人员在处理事务或将任何数据保存到数据库中之前必须正确地净化和验证数据。
本文着重介绍净化和验证输入,但为了正确地保护表单,还必须考虑其他要素,比如服务器配置。
验证是指检查输入(比如Web表单上的输入)是否符合特定的策略和约束(比如单引号)。比如说,考虑以下输入:
如果未经验证,无法阻止攻击者通过录入意外的输入而不是预期的输入来利用表单。如果提交的表单存储在数据库中,攻击者还可以尝试直接执行代码,这种情况很常见。
为了防止这种糟糕的情况,开发人员必须添加验证步骤,即在进行下一步之前检查数据。比如说,使用PHP之类的流行语言,就可以检查数据类型、长度及其他许多标准。
净化包括从用户输入中删除任何不安全的字符,而验证将检查数据是否符合预期的格式和类型。净化修改输入内容,以确保显示的格式有效,或者在插入到数据库之前有效。
针对薄弱输入的最常见技术可能是跨站脚本(XSS)攻击,即攻击者将恶意脚本注入到原本可信任的网站中。
一些XSS攻击比其他XSS攻击来得更明显,这意味着即使你花时间来净化和验证输入,熟练的攻击者仍然可能找到在特定条件下注入恶意代码的方法。
典型的攻击演示包括在薄弱输入中注入以下脚本,其中占位符“XSS”是任意JavaScript:
如果输入的内容显示在页面上(或其他地方),攻击者可以在目标网站上执行任意JavaScript。典型的情况是易受攻击的搜索输入在页面上显示搜索词:https://mysite.com/?s=
如果恶意条目存储在数据库中,情况会变得更糟。演示代码看起来很有意思,值得一试,但在实际情况下,攻击者可以用JavaScript达成众多目的,有时甚至窃取cookie。
净化的最大问题是它可能会给人一种虚假的安全感。去除不需要的字符和HTML标记只是检查的一个层面。它常常执行得很差,删除太多的信息,比如合法的引号和特殊字符,却没有兼顾所有的攻击面。不能盲目地运用宽泛的规则。
上下文是关键,这包括所使用的编程语言。稍后将详细介绍,但重要的是遵循“晚转义”(比如在输出前一刻)的原则,因为你知道使用数据的确切上下文。
根据本人的经验,最棘手的情况是当你需要允许原始输入及其他宽松的配置时。在这种情况下,正确地净化数据变得非常困难,你必须维护允许字符的自定义白名单,或者将一些恶意模式手动列入黑名单。
建议使用稳健可靠的库和框架。
更普遍的情况是,开发人员必须毫不犹豫地返回糟糕输入的错误,而不是靠猜测或修复,这很容易产生错误和漏洞。
开发团队可以遵循几个原则和最佳实践,以获得最佳结果。我们将介绍广泛的类别以及需要注意的细节。
1.不要相信用户输入
一些网站不检查用户输入,从而将应用程序暴露在最大的危险中。幸好由于安全意识和代码分析,这种情况在减少。然而,不全面的净化也不是很好的解决方案。
以下是你需要考虑的几条可能存在的攻击路径。
1、GET请求
如果开发人员没有正确地净化字符串,攻击者可以利用XSS缺陷,比如:
https://mysite.com/?s=
典型的网络安全意识通常以简单的console.log或甚至警报来高亮显示上面的示例。然而,它表明任何人都可以在你的页面上执行任意JavaScript,只需向毫无防备的受害者发送畸形URL的缩短版本。
一些XSS漏洞甚至可能是持久性的(比如存储在数据库中),由于自动向网站用户投递恶意载荷,攻击者无需为让受害者点击某个内容而操心。
2、cookie
网站经常使用HTTP cookie进行会话管理、定制和跟踪。比如说,开发人员可以记录用户的浏览活动、记住用户的偏好以及分析他们的行为。
服务器生成cookie或一段近似的数据,将其发送到浏览器保存起来供以后使用。因此,窃取cookie使攻击者能够冒充受害者,无需登录即可立即访问目标帐户。
此外,黑客不必侵入受害者的电脑。由于HTTP cookie随每个请求一起发送,因此攻击者就可以在中间人(MITM)攻击期间拦截这些请求以窃取数据。
一种更复杂的方法可以使用跨站攻击,将恶意代码插入到目标网站中,最终复制用户的cookie,并冒充用户执行危害性操作。
虽然谷歌计划明年逐步淘汰其Chrome浏览器中的cookie,但开发网络安全最佳实践仍然很重要。比如说,从2022年开始,SSL(安全套接字层)不再是可选层。然而,如果代码发送非SSL请求,cookie将以纯文本形式发送,因此确保一律使用SSL。
另一个好的实践是总是使用httpOnly属性来防止JavaScript劫持。还建议开发人员使用SameSite属性。
虽然cookie对用户和开发人员都很方便,但现代身份验证和API提供了更好的方法。由于将数据存储在客户端数据库中存在许多安全和隐私漏洞,因此最好改而采用其他更安全的实践。
3、POST请求
POST请求是服务器端请求,因此它们不会在URL中暴露数据,比如说当你将图片上传到在线帐户或当你提交联系表单时,比如:
一个常见的误解是POST请求比GET请求来得安全。然而,POST请求最多是通过隐匿性来实现安全。虽然将POST请求用于用户修改更好,但这么做不利于安全,更不会神奇地加强安全性。
可以通过以下命令获得一种非常简单的方法来使用PHP净化来自输入的POST数据:
filter_var($_POST['message'], FILTER_SANITIZE_STRING);
filter_var('[email protected]', FILTER_VALIDATE_EMAIL)
PHP中的另一个好实践是使用htmlentities()来转义字符串中任何不需要的HTML字符。
与cookie一样,始终使用SSL来加密数据,因此只有TCP/IP信息未加密。
4、目录遍历
如果代码库含有图像标记,黑客可能会尝试使用https://yourwebsite.com/getImages?filename=../../../etc/passwd以访问用户的信息。
然而,如果你的服务器配置正确,这种企图泄露机密信息的活动会被阻止。你还应该考虑过滤用户输入,确保只传输预期格式和类型的数据。
2. 不要相信客户端验证
一个常见的误解(特别对于初学者而言)是完全依赖HTML和JavaScript来验证表单数据。虽然HTML允许定义模式和必需的字段,比如设置字符限制或要求填充特定的字段,但没有在客户端无法修改的HTML属性或JavaScript代码。
黑客也可能使用cURL或任何HTTP客户端提交表单,因此客户端绝对不是验证表单的安全层。
3. 启用严格模式
尽量启用严格模式,无论是PHP、JavaScript或SQL,还是任何其他语言。然而,由于严格模式阻止了许多方便的语法,如果你有大量的技术债务和遗留问题,可能很难启用它。
另一方面,如果你没有在严格模式下编写代码,引擎会开始猜测,甚至可能自动修改值以使代码正常工作。这为漏洞埋下了隐患,黑客可以用来注入恶意命令。
比如在2015年,WordPress的主要贡献者Andrew Nacin解释了如何仅需通过在SQL中启用严格模式就可以避免一个严重的安全漏洞。他演示了黑客如何利用一个严重的漏洞:使用四字节字符强制MySQL截断,然后在数据库中注入恶意代码。
虽然防止这种攻击的一个简单的解决办法是执行命令SET SESSION sql_mode = "STRICT_ALL_TABLES",但不可能在不破坏所有WordPress支持的网站的情况下启用该模式。
4. 参照OWASP Web测试指南
OWASP即开放Web应用程序安全项目维护一份全面的文档,名为Web安全测试指南(WTSG),其中包含输入验证这部分内容。
该指南提供了关于如何测试各种注入及针对输入的其他隐蔽攻击的信息。内容经常更新,各种场景都有详细的说明。
比如说,你可以查看测试存储跨站脚本的页面(https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/02-Testing_for_Stored_Cross_Site_Scripting),以了解持久性XSS的工作原理以及如何重现该漏洞。
图1
净化和验证输入是一个强制性的开发实践,但不能对所有条目运用通用的解决方案。你必须考虑特定的上下文才能够阻止注入。此外,不要在没有验证的情况下将任何内容存储在数据库中,但也要在显示值之前转义值,因为一些注入可能污染数据库记录。
另一个基本实践是尽可能晚地转义数据,最好是在显示数据前一刻。这样一来,你完全了解最终的上下文,没有办法让数据不转义。
最后,花时间对静态代码分析进行微调。这个过程往往会生成很多误报,比如无法被利用的XSS漏洞;然而,每一个动态获取其值的HTML属性和标记都应该被转义。
虽然黑客无法利用所有标签来获取敏感数据或欺骗登录的用户,但你仍然应该结合静态分析以便尽量防止漏洞。
参考及来源:https://www.esecurityplanet.com/endpoint/prevent-web-attacks-using-input-sanitization/