《第三方JavaScript编程》——7.2 跨站脚本

简介: 对代码进行潜在的XSS安全漏洞检查似乎很令人沮丧,但最好不要忘记和低估XSS攻击的影响。不过,相比失去发布者和用户的信任而言,花费几个小时的时间进行安全检查是很值得的。在下一节中,我们将谈论另外一种占比很大的攻击方式,一个名为跨站请求伪造的漏洞,利用该漏洞,攻击者可以使用用户的身份悄悄地发送恶意请求。

本节书摘来自异步社区《第三方JavaScript编程》一书中的第7章,第7.2节,作者:[美] Ben Vinegar Anton Kovalyov著,更多章节内容可以访问云栖社区“异步社区”公众号查看

7.2 跨站脚本

或许Web应用开发者都会遇到的一个尴尬问题,就是跨站脚本(俗称XSS)。许多不同的成功攻击案例都是利用了这个安全漏洞,小到将Facebook装扮成MySpace风格的恶作剧[2],大到像金融诈骗这样的严重后果。攻击者利用跨站脚本漏洞可以将自己的代码注入到用户浏览的Web页面中。迄今为止,这是包括第三方JavaScript应用在内的现代Web应用最常见的攻击形式。根据赛门铁克(Symantec)2007年的互联网安全威胁报告[3],在其中所描述所有安全漏洞中,XSS占到了80%的比例。

在本节中,我们会介绍什么是XSS攻击,并解释其工作原理,同时会告诉你如何在自己的应用中防御这种攻击。

7.2.1 XSS攻击
攻击者是如何将他们的代码注入到你的Web应用中的呢?通常情况下,是因为你的应用没有对输入的内容做足够的过滤。例如,假设Camera Stork网站(http://camerastork.com)没有对用户提交的评论内容做过滤便展示在主页上,因为内容是输出到一个HTML页面上,所以攻击者只需要将它们自己的HTML代码嵌入在提交的内容中,便可以将其输出到页面上。例如,如果攻击者的评论内容中包含下述代码,所有浏览Camera Stork主页的用户都将会看到一个无聊的弹出提示“Hello, World”。
screenshot
这种“Hello, World”的提示消息并不可怕,但这种漏洞可以被多种不同的方式利用并攻击你的用户或者访问者。例如,利用该漏洞,攻击者可以注入JavaScript代码,从而获取用户cookies中的会话令牌,如图7.1所示。正如你已经了解的,攻击者通过劫持用户的会话可以在受影响的服务中访问用户存储的数据,并可以执行恶意的操作。
screenshot
通过代码注入访问用户的cookies很简单,攻击者只需要通过查询第6章中介绍的document.cookies属性即可。但只是访问用户的cookies还不够,攻击者还需要获得这些cookies。为了实现这一点,攻击者需要在浏览器中通过一个HTTP请求将用户的cookies发送到自己的服务器。有一种方式可以做到:在DOM中追加一个新的脚本元素,并发起一个兼容所有浏览器的CORS XmlHttpRequest。但最简单最便捷的方式是使用浏览器中自带的JavaScript Image对象。
下面是攻击者注入到一个商品评论内容中的代码片段示例。
screenshot
这段代码是什么作用?每次加载评论时,它都会创建一个新的Image DOM元素,但不会将其添加到文档中,因此用户并不可见。即便不可见,浏览器依旧会发起HTTP请求下载对应的图片文件[4],这正是攻击者所需要的。这里src中的URL(attacker.example.com)并不是一个图片地址,而是攻击者用来收集cookies信息的服务端地址。而cookie数据通过目标URL的查询字符串进行传递。当这段代码被插入到Camera Stork网站后,访问者就会发起该请求,攻击者只需要监控日志并存储接收到的会话信息即可。

通常情况下,会话标识符并不隶属于某台特定计算机,因此攻击者唯一需要做的就是将获得的标识符作为自己的cookie数据。之后,攻击者便可以成为已验证用户并使用用户拥有的任何权限。

你可能也已经注意到了,这种攻击实际上用户是感知不到的,不像之前“Hello, World”的例子,这种情况下用户的体验同普通的页面没有什么不同。也就是说,用户在意识到他们的会话被窃取之前不太可能会主动报告这个问题,一旦用户自己意识到就太晚了,攻击者就能够通过他们的身份登录,访问他们的私人数据,并且可以直接执行未经授权的操作。黑客的真正目标并不是执行任意脚本,他们的真正目的是获取用户的会话。
7.2.2 CSS中的XSS漏洞
另一个XSS攻击的成功案例是攻击微件的CSS自定义样式。有时候,你希望微件能够足够定制化,因此会允许发布者指定微件的自定义样式。即便是通过iframe的形式展现的微件,也可以使用我们在第3章中介绍的自适应的HTML和CSS技术来实现。

请看程序清单7.1。在这个例子中,发布者除了将Camera Stork微件嵌入在他们的Web页面中之外,还使用全局变量指定了两个自定义颜色:stork_bg_color包含背景颜色,stork_bg_color包含前景(文字)颜色。

程序清单7.1 发布者使用全局配置变量来自定义颜色
screenshot
假设这个版本的Camera Stork微件是通过外部iframe的形式引入。程序清单7.2中的代码是生成的iframe中的HTML代码。它从iframe的URL查询字符串中解析获取传递的自定义颜色参数[5],并将最终生成后的样式插入到页面中。

程序清单7.2 注入发布者指定颜色设置的漏洞代码
screenshot
现在假设攻击者在他们的网站中安装了你的微件,并利用背景色设置,将如下代码赋值给stork_bg_color变量。
screenshot

这段代码不仅会设置CSS的背景色,还会恶意声明一个额外的CSS表达式。该表达式执行的是一段似曾相识的JavaScript代码,使用隐藏的Image对象将用户当前的会话标识符发送到攻击者的网站。现在,攻击者只需要将你的用户吸引到一个包含微件的页面就完成了攻击[6],攻击者便会接收到用户的cookie内容,从而窃取他们的会话。因为攻击代码是在你的域名下的iframe页面中执行的,因此恶意代码可以访问到你的域名的所有cookie。

需要谨防不信任的发布者,但不是每个安装了应用的人都会这么做,因为他们确实有实际使用的需求。而有些人安装你的应用是为了作为攻击的切入点,在本章后面你会了解到,这种攻击不仅只是针对你的用户,也可能针对其他的发布者。与普通用户的情况相同,应当确保来自发布者的输入值没有潜在的危险。

该CSS表达式的例子只适用于Internet Explorer浏览器,但对于一个有经验的攻击者而言,它是众多可用的XSS切入点之一。在程序清单7.2中,攻击者也可以直接闭合< style >标签并注入代码到iframe的< head >部分。其他漏洞利用方式包括:将javascript:< expr >作为图片的URL,在Firefox的refresh meta标签中嵌入JavaScript异常等。
现在你已经了解了应用被攻击的不同方式,接着我们会针对XSS攻击介绍一些有用的保护方法。

7.2.3 防止XSS对应用的攻击
所有的Web应用程序,尤其是第三方微件,都有许多不可信的数据来源,这些入口都可能成为XSS攻击的切入点。不幸的是,避免这类攻击的唯一方法就是对所有的外部数据进行仔细的过滤,无论这些数据是来自最终用户还是发布者。

一般情况下,在将任何外部数据插入到DOM(包括发布者和你自己)中之前,应该对其中包含的任意字符进行转义如下。

  • 将<转换为&lt。
  • 将>转换为&gt。
  • 将&转换为&amp。
  • 将"转换为&quot。
  • 将 ' 转换为。

将这些字符转换为相应的HTML实体,可以确保攻击者无法在消息中插入< script >标签,攻击者也无法在微件中执行任意代码。这种方式称之为“数据消毒”,在接收一些不能确保安全的数据时经常用到。这种情况下,对数据进行处理并且对所有有潜在威胁的部分(如HTML标签)进行删除或者转义。有效地进行“数据消毒”是一个很难解决的问题,特别是当你希望用户能够使用一些HTML元素的时候,而这些元素并不会导致漏洞。有许多种“数据消毒”的方式,但最行之有效的是对数据进行过滤以确保输入源的安全性。

1.拒绝已知的错误
让我们假设有一个简单的用于评论微件的所见即所得(WYSIWYG)编辑器。你希望用户能够使用像这样简单的标签,但是不希望有人通过插入< script >标签来劫持其他用户的会话。我们想到的第一件事就是删除所有的< script >标签,这是一种简单粗暴的解决方案,或者套用美国著名记者Henry Louis Mencken的一句话:“这是一种简洁、合理并且错误的解决方案”。攻击者只需要绕过过滤器的过滤规则即可,比如将< script >标签替换为< scr< script >ipt >。由于过滤器会将< script >替换为空字符串,结果不小心便产生了另外一个< script >元素。如果你使用多个过滤器对用户的输入进行多次验证,攻击者也可能利用这些步骤的顺序来绕过它们。

我们刚刚描述的方式通常称之为“拒绝已知的威胁(reject known bad)”。你需要有某种黑名单,其中包含一系列对微件有潜在威胁的攻击模式,并且对每个输入都进行黑名单的匹配检查。这种方式有时是很有必要的,例如,当输入是一个格式不受限制的文本域,像产品评论或者邮件正文,它可能是防御应用遭受XSS攻击最安全的方式。但通过Web传输的数据可以通过许多种方式编码,因此有些情况极有可能从黑名单中漏掉。

2.接受已知正确的内容
一种更好的方式是尽可能接受已知安全的数据。同之前的做法正好相反,这种做法需要维护一个安全值的白名单,而不是像之前维护危险值的黑名单。例如,如果你从发布者页面将产品标识传递给iframe微件,取代之前对输入进行“数据消毒”的方式是验证输入数值是否是数字。
screenshot
还有另外一个不错的方法,就是尝试将输入数据限制为已知的安全值,从而缩小输入的范围。以程序清单7.2中的CSS跨站脚本攻击为例,代码中的值预期为有效的十六进制颜色值,但是攻击者提供的是一个CSS表达式,从而获取用户(碰巧使用了早期版本Internet Explorer的用户)的会话。程序清单7.3展示了通过缩小发布者页面提供的数据范围来防止这种攻击。

程序清单7.3 通过缩小输入数据的范围防止XSS攻击
screenshot
正如程序清单7.3所示,你甚至不需要再对代码进行“消毒”,因为在数据无效的情况下,攻击者最多无法看到正确的背景色。但是毕竟是攻击者,所以这种情况完全可以接受。

对代码进行潜在的XSS安全漏洞检查似乎很令人沮丧,但最好不要忘记和低估XSS攻击的影响。不过,相比失去发布者和用户的信任而言,花费几个小时的时间进行安全检查是很值得的。在下一节中,我们将谈论另外一种占比很大的攻击方式,一个名为跨站请求伪造的漏洞,利用该漏洞,攻击者可以使用用户的身份悄悄地发送恶意请求。

相关文章
|
2月前
|
存储 JavaScript 前端开发
JavaScript编程实现tab选项卡切换的效果+1
JavaScript编程实现tab选项卡切换的效果+1
|
3月前
|
JavaScript 前端开发 编译器
解锁JavaScript模块化编程新纪元:从CommonJS的基石到ES Modules的飞跃,探索代码组织的艺术与科学
【8月更文挑战第27天】随着Web应用复杂度的提升,JavaScript模块化编程变得至关重要,它能有效降低代码耦合度并提高项目可维护性及扩展性。从CommonJS到ES Modules,模块化标准经历了显著的发展。CommonJS最初专为服务器端设计,通过`require()`同步加载模块。而ES Modules作为官方标准,支持异步加载,更适合浏览器环境,并且能够进行静态分析以优化性能。这两种标准各有特色,但ES Modules凭借其更广泛的跨平台兼容性和现代语法逐渐成为主流。这一演进不仅标志着JavaScript模块化的成熟,也反映了整个JavaScript生态系统的不断完善。
50 3
|
7天前
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
在浏览器执行js脚本的两种方式
|
11天前
|
自然语言处理 JavaScript 前端开发
JavaScript闭包:解锁编程潜能,释放你的创造力
【10月更文挑战第25天】本文深入探讨了JavaScript中的闭包,包括其基本概念、创建方法和实践应用。闭包允许函数访问其定义时的作用域链,常用于数据封装、函数柯里化和模块化编程。文章还提供了闭包的最佳实践,帮助读者更好地理解和使用这一强大特性。
11 2
|
2月前
|
JavaScript 前端开发
JavaScript编程实现tab选项卡切换的效果
JavaScript编程实现tab选项卡切换的效果
|
2月前
|
JavaScript 前端开发
用JavaScript编程控制网页上checkbox选择状态:全选、全部取消、反选
用JavaScript编程控制网页上checkbox选择状态:全选、全部取消、反选
|
2月前
|
JavaScript 前端开发
用JavaScript脚本将当地时间转换成其它时区
用JavaScript脚本将当地时间转换成其它时区
|
2月前
|
JavaScript 前端开发 安全
JavaScript编程实现字符和字符串翻转
JavaScript编程实现字符和字符串翻转
|
2月前
|
JavaScript 前端开发
用JavaScript编程定义二维数组并初始化,然后输出元素值
用JavaScript编程定义二维数组并初始化,然后输出元素值
|
3月前
|
JavaScript 前端开发 安全
揭秘TypeScript的魔力:它是如何华丽变身为JavaScript的超能英雄,让您的代码飞入全新的编程维度!
【8月更文挑战第22天】在Web开发领域,JavaScript是最主流的编程语言之一。但随着应用规模的增长,其类型安全和模块化的不足逐渐显现。为解决这些问题,微软推出了TypeScript,这是JavaScript的一个超集,通过添加静态类型检查来提升开发效率。TypeScript兼容所有JavaScript代码,并引入类型注解功能。
38 2