站点安全

站点安全需要在网站设计和使用的各个方面保持警惕。这篇入门文章不会让你成为一个网站安全专家,但是可以帮助你理解威胁的来源以及如何保护你的 Web 应用来远离这些常见的攻击。

准备内容: 计算机基础知识。
目标:

了解针对 Web 应用常见的攻击方式和用来减少网站被黑客攻击的风险的方法。

什么是站点安全?

互联网很危险!我们经常听到网站因为拒绝服务攻击或主页显示被修改的(通常是有害的)内容而无法使用。在一些出名的案例中,上百万的密码、邮件地址和信用卡信息被泄露给了公众,导致网站用户面临个人尴尬和财务威胁。

站点安全的目的就是为了防范这些(或者说所有)形式的攻击。更正式点说,站点安全就是为保护站点不受未授权的访问、使用、修改和破坏而采取的行为或实践。

有效的站点安全需要在对整个站点进行设计:包括 Web 应用编写、Web 服务器的配置、密码创建和更新的策略以及客户端代码编写等过程。尽管这听起来很凶险,好消息是如果你使用的是服务器端的 Web 服务框架,那么多数情况下它默认已经启用了健壮而深思熟虑的措施来防范一些较常见的攻击。其他的攻击手段可以通过站点的 Web 服务器配置来减轻威胁,例如启用 HTTPS. 最后,可以用一些公开可用的漏洞扫描工具来协助发现你是否犯了一些明显的错误。

文章的剩余部分列举了一些常见威胁的细节以及用来保护站点的一些简单措施。

备注: 这只是一篇介绍性的主题,旨在帮你开始思考站点安全。它并不详尽。

站点安全威胁

这个部分列举了常见网站攻击手段以及如何减轻它们带来的危害。当你读的时候请注意,这些攻击是如何得手的,当 web 应用相信这些来自浏览器的信息或者不够坚持自己的时候。

跨站脚本 (XSS)

XSS 是一个术语,用来描述一类允许攻击者通过网站将客户端脚本代码注入到其他用户的浏览器中的攻击手段。由于注入到浏览器的代码来自站点,其是可信赖的,因此可以做类似将该用户用于站点认证的 cookie 发送给攻击者的事情。一旦攻击者拿到了这个 cookie,他们就可以登陆到站点,就好像他们就是那个用户,可以做任何那个用户能做的事情。根据站点的不同,这些可能包括访问他们的信用卡信息、查看联系人、更改密码等。

备注: XSS 攻击在历史上较其他类型更为常见。

有两种主要的方法可以让站点将注入的脚本返回到浏览器——通常被称做 反射型 和 持久型 XSS 攻击。

  • 反射型 XSS 攻击发生在当传递给服务器的用户数据被立即返回并在浏览器中原样显示的时候——当新页面载入的时候原始用户数据中的任何脚本都会被执行!

    举个例子,假如有个站点搜索函数,搜索项被当作 URL 参数进行编码,这些搜索项将随搜索结果一同显示。攻击者可以通过构造一个包含恶意脚本的搜索链接作为参数(例如 https://developer.mozilla.org?q=beer<script%20src="http://example.com/tricky.js"></script>),然后把链接发送给另一个用户。如果目标用户点击了这个链接,当显示搜索结果时这个脚本就会被执行。正如上述讨论的,这促使攻击者获取了所有需要以目标用户进入站点的信息——可能会购买物品或分享联系人信息。

  • 持久型 XSS 攻击:恶意脚本存储在站点中,然后再原样地返回给其他用户,在用户不知情的情况下执行。

    举个例子,接收包含未经修改的 HTML 格式评论的论坛可能会存储来自攻击者的恶意脚本。这个脚本会在评论显示的时候执行,然后向攻击者发送访问该用户账户所需的信息。这种攻击类型及其常见而且有效,因为攻击者不需要与受害者有任何直接的接触。

    尽管 POSTGET 方式获取到的数据是 XSS 攻击最常见的攻击来源,任何来自浏览器的数据都可能包含漏洞(包括浏览器渲染过的 Cookie 数据以及用户上传和显示的文件等).

防范 XSS 攻击的最好方式就是删除或禁用任何可能包含可运行代码指令的标记。对 HTML 来说,这些包括类似 <script>, <object>, <embed>,和 <link> 的标签。

修改用户数据使其无法用于运行脚本或其他影响服务器代码执行的过程被称作输入过滤。许多 Web 框架默认情况下都会对来自 HTML 表单的用户数据进行过滤。

SQL 注入

SQL 注入漏洞使得恶意用户能够通过在数据库上执行任意 SQL 代码,从而允许访问、修改或删除数据,而不管该用户的权限如何。成功的注入攻击可能会伪造身份信息、创建拥有管理员权限的身份、访问服务器上的任意数据甚至破坏/修改数据使其变得无法使用。

如果传递给底层 SQL 语句的用户输入可以修改该语句的语义,这种漏洞便是存在的。例如下面一段代码,本来是用来根据 HTML 表单提供的特定名字(userName) 来列出所有的用户:

sql
statement = "SELECT * FROM users WHERE name = '" + userName + "';"

如果用户输入了真实的名字,这段代码会如预想的运行。然而一个恶意用户可以完全将这个 SQL 语句的行为改变为下面的新语句的行为,只要通过将 userName指定为下列“粗体”的文本。修改后的代码创建了一个合法的 SQL 语句,该语句删除了整个 users 表,然后从 userinfo 表中获取了所有数据(所有用户的信息都被暴露了)。这是有效的,因为注入的文本的第一部分 (a';) 结束了原来的语句 ( ' 在 SQL 语句中是用来描述字符串常量的) 。

sql
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';

避免此种攻击的方法就是确保任何传递给 SQL 查询语句的用户数据都无法更改查询的本来用意。有种方式便是将用户输入中任何在 SQL 语句中有特殊含义的字符进行转义。

备注: SQL 语句把 ' 号作为一个字符串常量的开头的结尾。通过在前面放置一个斜杠,我们把单引号进行了转义 ( \' ),然后 SQL 就会将其视为一个字符(作为字符串的一部分)。

在下面的语句中我们对 ' 字符进行了转义。SQL 会将粗体显示的整段字符串解释为 name(这个 name 很古怪,但至少是没有危害的!)

sql
SELECT * FROM users WHERE name = 'a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t';

Web 框架通常会为你进行这种转义操作。例如 Django,可以确保任何传递给查询集合 (model 查询) 的用户数据都是已经转义过的。

备注: 本章节引用了大量来自 Wikipedia 的内容。

跨站请求伪造 (CSRF)

CSRF 攻击允许恶意用户在另一个用户不知情的情况下利用其身份信息执行操作。

这种形式的攻击用实例来解释最好。John 是一个恶意用户,他知道某个网站允许已登陆用户使用包含了账户名和数额的 HTTP POST 请求来转帐给指定的账户。John 构造了包含他的银行卡信息和某个数额做为隐藏表单项的表单,然后通过 Email 发送给了其他的站点用户(还有一个伪装成到“快速致富”网站的链接的提交按钮)。

如果某个用户点击了提交按钮,一个 HTTP POST 请求就会发送给服务器,该请求中包含了交易信息以及浏览器中与该站点关联的所有客户端 cookie(将相关联的站点 cookie 信息附加发送是正常的浏览器行为) 。服务器会检查这些 cookie,以判断对应的用户是否已登陆且有权限进行上述交易。

最终的结果就是任何已登陆到站点的用户在点击了提交按钮后都会进行这个交易。John 发财啦!

备注: 这里的诀窍是,John 不需要访问那些用户的 cookie(或者说身份信息)——用户的浏览器存储了这些信息,而且会自动将其包含在发送给对应服务器的请求中。

杜绝此类攻击的一种方式是在服务器端要求每个 POST 请求都包含一个用户特定的由站点生成的密钥 ( 这个密钥值可以由服务器在发送用来传输数据的网页表单时提供)。这种方式可以使 John 无法创建自己的表单,因为他必须知道服务器提供给那个用户的密钥值。即使他找出了那个密钥值,并为那个用户创建了表单,他也无法用同样的表单来攻击其他的所有用户。

Web 框架通常都会包含一些类似的 CSRF 防范技巧。

其他威胁

其他常见的攻击/漏洞利用方式包括:

  • 劫持. 通过这种方式,恶意用户劫持了对可见上层站点的点击,然后将其转发给下层隐藏的页面。这种技术例如可以用来显示一个合法的银行网站,但是将登陆认证信息截获到由攻击者控制的隐藏的<iframe>中。另外也可以用于促使用户点击可见网页的按钮,实际上却在不知情的情况点击了一个完全不同的按钮。作为防范手段,你的站点可以通过设置适当的 HTTP 头来防止其被嵌入到另一个站点的 iframe 中。
  • 拒绝服务 (DoS). Dos 通常通过使用伪造的请求淹没站点,这样合法用户的访问就会被中断。这些请求可能仅仅是数量巨大或者是单独消耗了大量资源 (如 延缓读,上传大文件等) 。DoS 防护通常通过识别并堵塞“恶意”的网络数据来工作,同时允许合法信息通过。这些防护一般都是在 Web 服务器之前或服务器中进行 (它们并非 web 应用本身所为).
  • 目录遍历(File and disclosure). 在这种攻击中,攻击者会尝试访问 Web 服务器文件系统中他们本不该访问的部分。这种漏洞会在用户可以传递包含文件系统导航字符的文件名时出现(比如 ../../ )。解决方法就是在使用前对用户输入进行过滤。
  • 文件包含. 在此攻击方式中,用户在传递给服务器的数据中指定一个“非故意”的文件来显示或执行。一旦载入成功,这个文件就可以在服务器或客户端(造成 XSS 攻击)执行。解决方式就是在使用前对输入进行过滤。
  • 命令行注入. 命令行注入攻击允许恶意用户在主机操作系统中执行任意系统命令。解决方法就是在系统调用中使用前对用户输入进行过滤。

还有很多的方式。要查看更全面的列表,请访问 Category:Web security exploits (Wikipedia) 和 Category:Attack (Open Web Application Security Project).

一些关键信息

当 Web 应用信任来自浏览器的数据时,上述章节里提到的大多数攻击利用手段才能成功。无论你做什么其他的事情来提升你的网站的安全性能,在将信息展示在浏览器之前、在使用 SQL 语句进行查询之前、在传递给一个操作系统或者文件系统之前,你应该过滤掉所有的用户源信息。

警告: 在你可以了解到的有关网站安全大多数 课程之中,最重要的就是不要相信来自浏览器的数据。包括在 URL 参数中的 GET 请求、POST 请求、HTTP 头、cookies、用户上传的文件等等。一定要每次都检查用户输入的信息。每次都预想最坏的结果。

你可以采取一些简单的步骤:

  • 采取更加强大的密码管理措施。当密码频繁更换时鼓励更加健壮的密码。采取双因素认证,也就是说除了密码,用户还应该输入另一种认证码(通常是只有唯一一个用户拥有的通过一些物理硬件传输的,比如发送给用户手机的验证短信)。
  • 将你的服务器配制成 HTTPSHTTP Strict Transport Security (HSTS)。HTTPS 会加密你的用户和服务器之间传输的信息。这使得登录认证、cookise、POST 数据及头信息不易被攻击者获得。
  • 持续追踪那些常见的网络攻击 (the current OWASP list is here),先解决最脆弱的部分。
  • 使用 vulnerability scanning tools 来对你的网站进行一些安全测试 (然后,你的非常受欢迎的网站还可以靠提供赏金来寻找 bug,就像 Mozilla 这样(like Mozilla does here)。
  • 只存储和展示你不得不需要的东西。比如,如果你的用户不得不存储一些敏感信息(如信用卡详明),只展示足以让用户识别卡号的几位数字即可,却不足以让黑客复制之后在另一个站点使用。现今最常见的是只展示信用卡卡号后 4 位数字。

web 框架可以帮助抵御很多常见的攻击。

总结

这篇文章介绍了有关网络安全的概念和你应该避免的一些常见的攻击。最重要的是,你应该明白一个 web 应用不可以相信任何来自网络服务器的数据!所有的用户数据在展示、使用 SQL 查询或者回应系统之前应该被过滤。

这也是这个模块的结尾,涵盖了你之前在服务器端编程学到的知识。我们希望你非常享受这个学习基础概念的过程,并且你现在已经准备好选择一个 web 框架开始编程了。