SQL注入和XSS:白帽黑客如何信任用户输入
SQL注入攻击
SQL注入缓解
跨站点脚本 (XSS) 攻击
XSS 攻击缓解
注意你的输入
软件开发人员总是心事重重。在创建网站或应用程序时,有无数的问题需要考虑:我们将使用哪些技术?架构将如何构建?我们需要哪些功能?用户界面应该是什么样子?尤其是在软件市场中,发布新应用更像是一场声誉竞赛,而非一个深思熟虑的流程,因此,最重要的问题之一往往被搁置在“紧急”事项的最后:我们的产品如何得到保障?
如果您使用一个强大的开源框架来构建产品(并且该框架适用且可用,为什么不呢?),那么一些基本的安全问题,例如 CSRF 令牌和密码加密,可能已经为您处理好了。不过,对于快速发展的开发人员来说,复习一下常见威胁和陷阱的知识还是很有益处的,哪怕只是为了避免一些尴尬的新手错误。通常,软件安全中最薄弱的环节就是您自己。
我最近对信息安全,尤其是道德黑客的实践越来越感兴趣。道德黑客,有时也被称为“白帽”黑客,有时也简称为“黑客”,是指那些搜索潜在安全漏洞并负责任地(私下)向项目所有者报告的人。相比之下,恶意黑客或“黑帽”黑客,也被称为“骇客”,是指那些利用这些漏洞进行娱乐或谋取私利的人。白帽黑客和黑帽黑客可能使用相同的工具和资源,并且通常会尝试进入他们不该进入的地方;然而,白帽黑客这样做是经过许可的,目的是加强防御,而不是摧毁它们。黑帽黑客则是坏人。
说到学习如何查找安全漏洞,我毫不奇怪地会竭尽全力地学习一切能找到的资料;这篇文章总结了一些关键领域,这些领域对开发人员处理用户输入特别有帮助。这些经验教训都来自以下优秀的资源:
- 开放Web 应用程序安全项目指南
- HackerOne YouTube 频道上的 Hacker101 播放列表
- Peter Yaworski 撰写的《Web 黑客 101》
- Brute Logic 的博客
- Computerphile YouTube频道
- 以 Jason Haddix ( @jhaddix ) 和 Tom Hudson ( @tomnomnom )为主角的视频(两位经验丰富的道德黑客,他们使用的方法虽然不同,但都很有效)
您可能对“净化您的输入!”这句口号很熟悉。然而,我希望这篇文章能够说明,开发一个安全可靠的应用程序并非易事。我建议换个说法:关注您的输入。让我们通过分析利用此领域漏洞的最常见攻击来详细说明:SQL 注入和跨站点脚本攻击。
SQL注入攻击
如果您还不熟悉 SQL(结构化查询语言)注入攻击(SQLi),这里有一个很棒的SQLi 视频,讲解得像我五岁一样详细。您可能已经从xkcd 的 Little Bobby Tables中了解过这种攻击。本质上,恶意攻击者可能能够通过您网站上的某些输入(例如从数据库中提取结果的搜索框)发送影响您应用程序的 SQL 命令。使用 PHP 编写的网站尤其容易受到此类攻击,一次成功的 SQL 攻击可能会对依赖数据库的软件造成毁灭性打击(例如,您的“用户”表现在变成了一盆矮牵牛花)。
您可以测试自己的网站,看看是否容易受到此类攻击。(请仅测试您拥有的网站,因为在您所在地区,未经授权进行 SQL 注入可能是违法的;而且,在世界范围内,这绝对不是什么好事。)以下有效载荷可用于测试输入:
' OR 1='1
计算结果为常数 true,成功时返回表中的所有行。' AND 0='1
计算结果为常量 false,成功时不返回任何行。
该视频演示了上述测试,并很好地展示了 SQL 注入攻击的影响力。
值得庆幸的是,有办法减轻 SQL 注入攻击,它们都归结为一个基本概念:不要相信用户输入。
SQL注入缓解
为了有效地缓解 SQL 注入,开发人员必须阻止用户成功向网站的任何部分提交原始 SQL 命令。
有些框架会帮你完成大部分繁重的工作。例如,Django通过使用QuerySet实现了对象关系映射(ORM)的概念。我们可以将这些函数视为包装函数,帮助你的应用程序使用预定义的方法查询数据库,从而避免使用原始 SQL。
然而,能够使用框架从来都不是万能的。当直接处理数据库时,我们可以使用其他方法来安全地从用户输入中抽象出 SQL 查询,尽管这些方法的效果各不相同。以下是按优先顺序排列的,并附有相关示例的链接:
如果您想实现上述技术,链接中的速查表是一个很好的起点,可以帮助您深入研究。简而言之,使用这些技术来获取数据,而不是使用原始的 SQL 查询,有助于最大限度地减少应用程序中任何需要用户输入的部分处理 SQL 的可能性,从而减轻 SQL 注入攻击。
然而,这场战斗才胜利了一半……
跨站点脚本 (XSS) 攻击
如果您是一名恶意程序员,JavaScript 几乎是您最好的朋友。正确的命令可以在网页上执行合法用户能够执行的任何操作(甚至一些他们本不应该执行的操作),有时甚至无需实际用户进行任何交互。跨站脚本攻击 (XSS) 是指 JavaScript 代码被注入网页并改变其行为的情况。其影响范围从恶作剧到更严重的身份验证绕过或凭证窃取。Apache在 2010 年的一份事件报告很好地展示了 XSS 如何与更大规模的攻击串联起来,从而控制帐户和机器。
一年一度的 DOM 舞蹈比赛迎来了一位意外的嘉宾);
XSS 攻击可以发生在服务器端或客户端,通常分为三种类型:基于 DOM(文档对象模型)的 XSS、存储型 XSS 和反射型 XSS。它们的区别在于攻击载荷注入应用程序的位置。
基于DOM的XSS
基于 DOM 的 XSS攻击是指 JavaScript 负载影响用户已在浏览器中加载的网页的结构、行为或内容。这类攻击通常通过修改 URL 来执行,例如在钓鱼邮件中。
为了了解注入 JavaScript 操作页面有多么容易,我们可以用 HTML 网页创建一个工作示例。尝试在本地系统上创建一个名为xss-test.html
(或任何你喜欢的名称)的文件,其中包含以下 HTML 和 JavaScript 代码:
<html>
<head>
<title>My XSS Example</title>
</head>
<body>
<h1 id="greeting">Hello there!</h1>
<script>
var name = new URLSearchParams(document.location.search).get('name');
if (name !== 'null') {
document.getElementById('greeting').innerHTML = 'Hello ' + name + '!';
}
</script>
</h1>
</html>
除非该网页从查询字符串中接收到值为 的URL 参数,否则它将显示标题“Hello there!” name
。要查看脚本的运行情况,请在浏览器中打开该页面并附加 URL 参数,如下所示:
file:///path/to/file/xss-test.html?name=Victoria
很有趣,对吧?我们的不安全(指安全感,而非情感感)页面接收 URL 参数值 ,name
并将其显示在 DOM 中。页面期望该值是一个友好易懂的字符串,但如果我们将其更改为其他值会怎样?由于该页面归我们所有,并且仅存在于本地系统上,因此我们可以随意测试它。但如果我们将name
参数更改为 ,会发生什么<img+src+onerror=alert("pwned")>
?
这只是一个例子,主要基于Brute 的帖子中的一个,演示了如何执行 XSS 攻击。有趣的弹出警报可能很有趣,但 JavaScript 可以造成很多危害,包括帮助恶意攻击者窃取密码和个人信息。
存储型和反射型XSS
当攻击载荷存储在服务器(例如数据库中)时,就会发生存储型 XSS。每当浏览器检索并呈现存储的数据时,攻击都会影响受害者。例如,攻击者可能会更新其在社交网站上的个人资料页面,而不是使用 URL 查询字符串,例如在“关于我”部分添加一个隐藏脚本。该脚本以不恰当的方式存储在网站服务器上,稍后当其他用户查看攻击者的个人资料时,就会成功执行。
最著名的例子之一是2005年几乎席卷MySpace的Samy蠕虫。它通过发送HTTP请求进行传播,每当受感染的个人资料被浏览时,就会将病毒复制到受害者的个人资料页面上。短短20小时内,它就传播到了超过一百万用户。
反射型XSS类似地发生在注入的有效载荷到达服务器时,但恶意代码最终不会存储在数据库中。相反,它会立即由Web应用程序返回到浏览器。此类攻击可以通过诱骗受害者点击恶意链接来执行,该链接会向存在漏洞的网站服务器发送请求。然后,服务器会向攻击者和受害者发送响应,这可能导致攻击者能够获取密码,或执行看似源自受害者的操作。
XSS 攻击缓解
在所有这些情况下,都可以通过两种关键策略来缓解 XSS 攻击:验证表单字段,并避免在网页上直接注入用户输入。
验证表单字段
在确保用户提交的表单合法性方面,框架可以再次提供帮助。Django的内置类就是一个例子,它提供了一些Field
字段,可以验证某些常用类型,并指定合理的默认值。EmailField
例如,Django 的 使用一组规则来判断输入的电子邮件地址是否有效。如果提交的字符串中包含电子邮件地址中通常不存在的字符,或者不符合电子邮件地址的常见格式,那么 Django 就不会认为该字段有效,表单也不会被提交。
如果依赖框架不可行,我们可以实现自己的输入验证。这可以通过几种不同的技术来实现,包括类型转换,例如,确保数字属于 类型int()
;检查数字的最小值和最大值范围以及字符串的长度;使用预定义的选项数组来避免任意输入,例如月份;以及根据严格的正则表达式检查数据。
值得庆幸的是,我们无需从头开始。有一些开源资源可以提供帮助,例如OWASP 验证正则表达式存储库,它提供了一些常见数据格式的匹配模式。许多编程语言都提供特定于其语法的验证库,我们可以在 GitHub 上找到很多这样的库。此外,XSS 过滤规避备忘单提供了一些测试有效载荷的建议,我们可以使用它们来测试现有的应用程序。
虽然这看起来很乏味,但正确实施的输入验证可以保护我们的应用程序免受 XSS 的攻击。
避免直接喷射
应用程序中直接将用户输入返回浏览器的元素,乍一看可能并不明显。我们可以通过探索以下几个问题来确定应用程序中可能存在风险的区域:
- 数据如何在我们的应用程序中流动?
- 当用户与此输入交互时,他们期望发生什么?
- 数据出现在页面上的什么位置?是嵌入在字符串中还是属性中?
以下是一些示例有效载荷,我们可以用它来测试我们网站上的输入(再次强调,仅限我们自己的网站!),由Hacker101提供。任何这些示例的成功执行都可能表明存在由于直接注入而导致的 XSS 漏洞。
"><h1>test</h1>
'+alert(1)+'
"onmouserover="alert(1)
http://"onmouseover="alert(1)
一般来说,如果您能够避免直接注入输入,请这样做。或者,请务必完全理解所选方法的效果;例如,在 JavaScript 中使用innerText
而不是innerHTML
将确保内容设置为纯文本,而不是(可能存在漏洞的)HTML。
注意你的输入
在与黑帽黑客(或恶意黑客)竞争时,软件开发人员处于明显的劣势。我们竭尽全力保护每一个可能危及应用程序的输入,但攻击者只需找到我们遗漏的那个即可。这就像给所有门都装了门闩,却留下一扇窗户开着!
然而,通过学习以攻击者的思维方式思考,我们可以更好地准备我们的软件来抵御恶意行为者。尽快发布功能固然令人兴奋,但如果我们事先花时间思考应用程序的流程、跟踪数据并关注输入,我们就能避免积累大量的安全债务。
文章来源:https://dev.to/victoria/sql-injection-and-xss-what-white-hat-hackers-know-about-trusting-user-input-481b