网站安全
网站安全需要对网站设计和使用的各个方面保持警惕。这篇入门文章不会让你成为网站安全专家,但它将帮助你了解威胁来自何处,以及你可以做些什么来增强你的 Web 应用程序,以抵御最常见的攻击。
预备知识 | 基本的计算机知识。 |
---|---|
目标 | 了解 Web 应用程序安全最常见的威胁,以及你可以采取哪些措施来降低网站被黑客攻击的风险。 |
什么是网站安全?
互联网是一个危险的地方!我们经常听到网站因拒绝服务攻击而无法访问,或者其主页上显示修改过的(通常具有破坏性的)信息。在其他引人注目的案例中,数百万个密码、电子邮件地址和信用卡详细信息被泄露到公共领域,使网站用户面临个人尴尬和财务风险。
网站安全的目的就是防止这些(或任何)类型的攻击。网站安全更正式的定义是保护网站免受未经授权的访问、使用、修改、销毁或中断的行为/实践。
有效的网站安全需要在整个网站设计中付出努力:包括你的 Web 应用程序、Web 服务器的配置、创建和续订密码的策略以及客户端代码。虽然所有这些听起来都很可怕,但好消息是,如果你正在使用服务器端 Web 框架,它几乎肯定会“默认”启用针对许多常见攻击的健壮且经过深思熟虑的防御机制。其他攻击可以通过你的 Web 服务器配置来缓解,例如通过启用 HTTPS。最后,还有公开可用的漏洞扫描工具,可以帮助你找出是否犯了任何明显的错误。
本文的其余部分将为你提供有关一些常见威胁以及你可以采取的一些简单步骤来保护你的网站的更多详细信息。
注意:这是一个入门主题,旨在帮助你开始思考网站安全,但它并不详尽。
网站安全威胁
本节列举了一些最常见的网站威胁以及如何缓解它们。阅读时请注意,当 Web 应用程序信任或对来自浏览器的数据不够“偏执”时,威胁最容易成功。
跨站脚本 (XSS)
XSS 是一个术语,用于描述一类攻击,它允许攻击者通过网站将客户端脚本注入到其他用户的浏览器中。由于注入的代码是从网站发送到浏览器的,因此该代码被信任,可以执行诸如将用户的网站授权 cookie 发送给攻击者之类的操作。当攻击者获得 cookie 后,他们就可以像用户一样登录网站,并执行用户可以执行的任何操作,例如访问他们的信用卡详细信息、查看联系信息或更改密码。
注意:XSS 漏洞在历史上比任何其他类型的安全威胁都更常见。
XSS 漏洞根据网站将注入脚本返回到浏览器的方式分为反射型和持久型。
- 反射型 XSS 漏洞发生在用户内容传递到服务器后立即且未经修改地返回并在浏览器中显示时。原始用户内容中的任何脚本都会在新页面加载时运行。例如,考虑一个网站搜索功能,其中搜索词被编码为 URL 参数,并且这些词与结果一起显示。攻击者可以构造一个包含恶意脚本作为参数的搜索链接(例如,
https://mdn.org.cn?q=beer<script%20src="http://example.com/tricky.js"></script>
),并将其通过电子邮件发送给其他用户。如果目标用户点击这个“有趣的链接”,脚本将在显示搜索结果时执行。如前所述,这会为攻击者提供以目标用户身份进入网站所需的所有信息,可能以用户身份进行购买或共享其联系信息。 - 持久型 XSS 漏洞发生在恶意脚本存储在网站上,然后稍后未经修改地重新显示给其他用户无意中执行时。例如,一个接受包含未经修改的 HTML 评论的论坛可以存储来自攻击者的恶意脚本。当评论显示时,脚本会执行,并将访问用户帐户所需的信息发送给攻击者。这种攻击非常流行且强大,因为攻击者甚至可能不需要与受害者进行任何直接接触。
虽然来自 POST
或 GET
请求的数据是 XSS 漏洞最常见的来源,但来自浏览器的任何数据都可能存在漏洞,例如由浏览器呈现的 cookie 数据,或上传并显示的用户文件。
抵御 XSS 漏洞的最佳防御措施是删除或禁用任何可能包含运行代码指令的标记。对于 HTML,这包括诸如 <script>
、<object>
、<embed>
和 <link>
等元素。
修改用户数据,使其不能用于运行脚本或以其他方式影响服务器代码执行的过程称为输入消毒。许多 Web 框架默认会自动对 HTML 表单中的用户输入进行消毒。
SQL 注入
SQL 注入漏洞使恶意用户能够在数据库上执行任意 SQL 代码,从而允许访问、修改或删除数据,而无论用户权限如何。成功的注入攻击可能会伪造身份、创建具有管理权限的新身份、访问服务器上的所有数据,或者销毁/修改数据使其无法使用。
SQL 注入类型包括基于错误的 SQL 注入、基于布尔错误的 SQL 注入和基于时间的 SQL 注入。
如果传递给底层 SQL 语句的用户输入可以改变语句的含义,则存在此漏洞。例如,以下代码旨在列出所有具有特定名称(userName
)的用户,该名称是从 HTML 表单提供的。
statement = "SELECT * FROM users WHERE name = '" + userName + "';"
如果用户指定了一个真实名称,该语句将按预期工作。但是,恶意用户可以通过为 userName
指定 a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't
来完全改变此 SQL 语句的行为,使其变为以下示例中的新语句。
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
修改后的语句创建了一个有效的 SQL 语句,该语句删除了 users
表并从 userinfo
表中选择了所有数据(这会泄露每个用户的信息)。这之所以有效,是因为注入文本的第一部分(a';
)完成了原始语句。
为了避免此类攻击,最佳实践是使用参数化查询(预处理语句)。这种方法确保用户输入被视为数据字符串而不是可执行的 SQL,因此用户不能滥用特殊的 SQL 语法字符来生成意外的 SQL 语句。以下是一个示例:
SELECT * FROM users WHERE name = ? AND password = ?;
例如,在 Python 中执行上述查询时,我们将 name
和 password
作为参数传递,如下所示。
cursor.execute("SELECT * FROM users WHERE name = ? AND password = ?", (name, password))
库通常提供抽象良好的 API,为开发人员处理 SQL 注入保护,例如 Django 的模型。你可以通过使用封装的 API 而不是直接编写原始 SQL 来避免 SQL 注入。
跨站请求伪造 (CSRF)
CSRF 攻击允许恶意用户在其他用户不知情或未经其同意的情况下,使用该用户的凭据执行操作。
这类攻击最好通过示例来解释。Josh 是一个恶意用户,他知道某个特定网站允许登录用户使用包含账户名和金额的 HTTP POST
请求向指定账户汇款。Josh 构建了一个表单,其中包含他的银行详细信息和金额作为隐藏字段,并通过电子邮件将其发送给其他网站用户(“提交”按钮伪装成指向“快速致富”网站的链接)。
如果用户点击提交按钮,一个 HTTP POST
请求将被发送到服务器,其中包含交易详情和浏览器与网站关联的任何客户端 Cookie(将关联的网站 Cookie 添加到请求中是正常的浏览器行为)。服务器将检查 Cookie,并使用它们来确定用户是否已登录以及是否具有进行交易的权限。
结果是,任何在登录交易网站时点击“提交”按钮的用户都将进行交易。Josh 因此变得富有。
注意:这里的诀窍是 Josh 不需要访问用户的 Cookie(或访问凭据)。用户的浏览器存储此信息并自动将其包含在发送到关联服务器的所有请求中。
防止此类攻击的一种方法是,服务器要求 POST
请求包含一个用户特定的、由网站生成的秘密。该秘密将在发送用于进行转账的网页表单时由服务器提供。这种方法阻止了 Josh 创建自己的表单,因为他必须知道服务器为用户提供的秘密。即使他找到了秘密并为特定用户创建了表单,他也无法再使用相同的表单攻击所有用户。
Web 框架通常包含此类 CSRF 防护机制。
其他威胁
其他常见的攻击/漏洞包括:
- 点击劫持 (Clickjacking)。在这种攻击中,恶意用户劫持了旨在可见的顶级站点的点击,并将其路由到下方的隐藏页面。例如,此技术可能用于显示一个合法的银行网站,但将登录凭据捕获到由攻击者控制的不可见的
<iframe>
中。点击劫持还可能用于让用户点击可见网站上的一个按钮,但实际上却无意中点击了一个完全不同的按钮。作为防御措施,你的网站可以通过设置适当的 HTTP 头来阻止自身被嵌入到其他网站的 iframe 中。 - 拒绝服务 (DoS)。DoS 通常通过向目标网站发送大量虚假请求来实现,从而中断合法用户对网站的访问。请求可能数量众多,或者它们可能单独消耗大量资源(例如,慢速读取或上传大文件)。DoS 防御通常通过识别和阻止“恶意”流量,同时允许合法消息通过来实现。这些防御措施通常位于 Web 服务器之前或内部(它们不是 Web 应用程序本身的一部分)。
- 目录遍历 (Directory Traversal)(文件和泄露)。在这种攻击中,恶意用户试图访问他们不应访问的 Web 服务器文件系统部分。当用户能够传递包含文件系统导航字符(例如,
../../
)的文件名时,就会出现此漏洞。解决方案是在使用输入之前对其进行消毒。 - 文件包含 (File Inclusion)。在这种攻击中,用户能够指定一个“非预期”文件,以便在传递给服务器的数据中显示或执行。加载时,此文件可能会在 Web 服务器或客户端执行(导致 XSS 攻击)。解决方案是在使用输入之前对其进行消毒。
- 命令注入 (Command Injection)。命令注入攻击允许恶意用户在主机操作系统上执行任意系统命令。解决方案是在用户输入可能用于系统调用之前对其进行消毒。
有关网站安全威胁的完整列表,请参阅类别:Web 安全漏洞 (Wikipedia) 和类别:攻击 (开放式 Web 应用程序安全项目)。
几个关键信息
上一节中几乎所有的安全漏洞都是在 Web 应用程序信任来自浏览器的数据时成功的。无论你采取什么其他措施来提高网站的安全性,都应该在将所有用户生成的数据显示在浏览器中、用于 SQL 查询或传递给操作系统或文件系统调用之前对其进行消毒。
警告:关于网站安全,你可以学到的最重要的一课是永远不要信任来自浏览器的数据。这包括但不限于 GET
请求的 URL 参数、POST
请求、HTTP 标头和 Cookie 中的数据,以及用户上传的文件。始终检查并消毒所有传入数据。始终假设最坏的情况。
你可以采取的其他具体步骤包括:
- 使用更有效的密码管理。鼓励使用强密码。考虑为你的网站启用双重身份验证,除了密码之外,用户还必须输入另一个身份验证代码(通常是通过用户独有的物理硬件传递的代码,例如发送到其手机的短信中的代码)。
- 配置你的 Web 服务器以使用 HTTPS 和 HTTP 严格传输安全 (HSTS)。HTTPS 会加密客户端和服务器之间发送的数据。这确保登录凭据、Cookie、
POST
请求数据和标头信息不容易被攻击者获取。 - 跟踪最常见的威胁(当前的 OWASP 列表在这里)并首先解决最常见的漏洞。
- 使用漏洞扫描工具对你的网站执行自动化安全测试。之后,你非常成功的网站也可能通过提供错误赏金来发现错误,就像 Mozilla 在这里所做的那样。
- 只存储和显示你需要的数据。例如,如果你的用户必须存储信用卡详细信息等敏感信息,则只显示足够卡号以供用户识别,但又不足以被攻击者复制并在其他网站上使用。目前最常见的模式是只显示信用卡号的最后 4 位数字。
- 保持软件更新。大多数服务器都有定期的安全更新,可以修复或缓解已知的漏洞。如果可能,安排定期的自动化更新,最好在网站流量最少的时候安排更新。最好在更新前备份数据并测试新的软件版本,以确保服务器上没有兼容性问题。
Web 框架可以帮助缓解许多常见的漏洞。
总结
本文解释了网络安全的概念以及您的网站应尝试防御的一些常见威胁。最重要的是,您应该明白,Web 应用程序不能信任来自 Web 浏览器的任何数据。所有用户数据都应该在显示、用于 SQL 查询和文件系统调用之前进行清理。
通过本文,您已完成本模块,涵盖了服务器端网站编程的入门步骤。我们希望您喜欢学习这些基本概念,现在您已准备好选择一个 Web 框架并开始编程。