XSS, CSRF, CRLF, SQL注入学习笔记
XSS, CSRF, CRLF, SQL注入都是历史悠久的四种攻击方式,因为参数化查询已经成了普遍用法,我们已经离SQL注入很远了,但是,XSS,CSRF,CRLF却没有远离我们。
本文以初学者角度探讨四种Web攻击以及预防方式,是个人基于对多种Web漏洞的学习总结,不承诺内容规范。
XSS 跨站脚本
跨站脚本(XSS,Cross Site Scripting)攻击是一种注入攻击,其恶意脚本被注入到其他可信网站中。当攻击者使用Web应用将恶意代码(通常以浏览器脚本的形式)发送给不同的最终用户时,就会发生XSS攻击。
允许这些攻击成功的缺陷非常普遍,并且发生在Web应用程序在其生成的输出中使用来自用户的输入而无需验证或编码它的任何地方。
攻击者可以使用XSS将恶意脚本发送给毫无戒心的用户。最终用户的浏览器无法知道该脚本不应该被信任,并将执行该脚本。
因为它认为脚本来自可靠来源,所以恶意脚本可以访问任何cookies,session tokens或浏览器保留并与该站点一起使用的其他敏感信息(比如document.cookie
)。这些脚本甚至可以重写HTML页面的内容(比如document.body.innerHTML=""
)。
XSS攻击通常可以分为两类:存储和反射。还有第三种不太知名的XSS攻击类型,称为基于DOM的XSS。
存储XSS
又名AKA Persistent,Type I
存储XSS通常在用户输入存储在目标服务器上时发生,例如在数据库,文件,论坛消息,访问日志等中,导致永久性的每次存储数据反射回响应文代码就会在浏览器中执行的一种XSS漏洞。
反射XSS
又名AKA Non-Persistent,Type II
当Web应用程序在错误消息,搜索结果或包含用户作为请求的一部分提供的部分或全部输入的任何其他响应中立即返回用户输入,导致代码在浏览器执行时,会发生反射的XSS。
基于DOM的XSS
又名AKA Type-0
Amit Klein,发表了关于这个问题的第一篇文章,基于DOM的XSS是XSS的一种形式,其中从源到接收的整个受污染的数据流在浏览器中发生,即数据的来源是在DOM中,接收器也在DOM中,数据流永远不会离开浏览器。例如,源(读取恶意数据)可以是页面的URL(例如,document.location.href),或者它可以是HTML的元素,而接收器是一个敏感的方法调用,导致执行恶意数据(例如document.write
)。
设想,某博客留言板允许插入第三方图片,输入图片URL后,会生成img标签:
1 | <img src="图片地址" /> |
普通图片地址:https://img.com/img.png
XSS地址:https://img.com/img.png" onload="javascript:alert(document.cookie);
文档呈现:
1 | <img src="https://img.com/img.png" onload="javascript:alert(document.cookie);" /> |
扩展:
多年来,大多数人认为这些(存储,反射,DOM)是三种不同类型的XSS,但实际上它们是重叠的。你可以同时拥有存储和反射的基于DOM的XSS。你也可以使用存储和反映的非基于DOM的XSS。所以为了帮助澄清,从2012年中开始,研究团体提出并开始使用新术语来帮助组织可能发生的XSS类型:
- 服务器存储XSS
- 客户端存储XSS
- 服务器反射XSS
- 客户端反射XSS
此处不讲解,参考:https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting
实践
在实际项目中实施XSS攻击,获取用户cookie信息。
搭建Spring Boot Web项目,配置第三方服务以接收用户cookie信息。
1 | <img src='x' onerror='alert(document.cookie)'> |
备用XSS语法
XSS在属性中使用脚本
可以在不使用<script>
标记的情况下进行XSS攻击。其他标签将完全相同,例如:
1 | <body onload=alert(document.cookie)> |
XSS使用脚本通过编码的URI方案
如果我们需要隐藏Web应用程序过滤器,我们可能会尝试对字符串字符进行编码,例如:a=A (UTF-8)
并在IMG标记中使用它:
1 | <IMG SRC=jAvascript:alert(document.cookie)> |
有许多不同的UTF-8编码符号为我们提供了更多的可能性。
XSS使用代码编码
我们可以在base64中对脚本进行编码并将其放在META标记中。这样我们完全摆脱了alert()。
1 | <META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg"> |
这些和其他示例可以在XSS Filter Evasion Cheat Sheet中找到,它是备用XSS语法攻击的真正百科全书。
预防
- 对用户输入进行转义(
<
转义<
,>
转义>
,"
转义"
,空格转义
) - AngularJS 谨慎使用
ng-bind-html
,$sce.trustAsHtml()
- CK editor 谨慎使用
ck.config.allowedContent
(HTML内容过滤,建议在设置之前阅读安全性最佳实践) - 对必须允许的HTML输入设置白名单过滤(GitHub的做法,Jsoup类库也可以很方便地实现,参考Sanitize untrusted HTML)
- 添加必要的 HTTP 响应头进行 XSS 防护
HTTP响应头 | 描述 |
---|---|
X-XSS-Protection: 1; mode=block | 该响应头会开启浏览器的防 XSS 过滤器 |
X-Frame-Options: deny | 该响应头会禁止页面被加载到框架 |
X-Content-Type-Options: nosniff | 该响应头会禁用客户端的 MIME 类型嗅探行为,参考这里 |
Content-Security-Policy: default-src ‘self’ | 该响应头是防止 XSS 最有效的解决方案之一。它允许我们定义从 URLs 或内容中加载和执行对象的策略,参考这里 |
Set-Cookie: key=value; HttpOnly | Set-Cookie 响应头通过 HttpOnly 标签的设置将限制 JavaScript 访问你的 Cookie |
Content-Type: type/subtype;charset=utf-8 | 始终设置响应的内容类型和字符集 |
CSRF 跨站请求伪造
CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
GET场景
通常使用以下技术之一完成:
- 发送带有HTML内容的电子邮件
- 网页上植入网址或脚本
漏洞利用网址可以伪装成普通链接,鼓励受害者点击它:
1 | <a href="/rest/users/delete-all">click here</a> |
或者作为0x0假图像:
1 | <img width="0" height="0" src='/rest/users/delete-all'> |
POST场景
GET和POST攻击之间的唯一区别是受害者如何执行攻击。
此类请求无法使用标准a
或img
标记进行传递,但可以使用form
进行传递:
1 | <form action="/rest/users/delete-all" method="POST"> |
此表单将要求用户单击提交按钮,但这也可以使用JavaScript自动执行:
首先在另一个网站上传以下代码:
1 | <html lang="en-US"> |
1 | <html lang="en-US"> |
当被攻击用户去访问main.html时,他浑然不知浏览器悄悄地给另一个网站发送了修改设置的请求,显然浏览器在发送请求时自觉地还带上了被攻击网站的cookie。
如果把代码中的action换成微博的API,我们就能实现:如果你的浏览器已登录了微博,那你访问这篇文章的同时,就自动关注了我的微博。
究其原因:
- form可以跨域post数据。
- Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。
学到这里我突然想到一个问题,JS可以读取页面iframe元素的内容,那我是否可以实现CSRF的同时读取到服务端返回值了呢?这样岂不是可以造成数据泄露?
1 | setTimeout(function() { |
开发浏览器的人早就想到了这个问题,只要是跨域的iframe读取与操作,都是不允许的。Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.
组合XSS实现XSRF
直接把上面代码利用XSS漏洞,嵌入被攻击站点,这组合攻击方式称为XSRF。
预防
CSRF攻击的一般是由服务端解决。
- 遵循RESTful规范,避免在GET请求中作数据修改操作
- 添加特殊请求参数做验证
- 验证码
- Referer Check
- Anti CSRF Token
如果网站同时存在XSS漏洞,依然可以通过JS获取到Token。
CRLF 返回头拆分
参数注入返回头
CRLF是”回车换行”(\r\n
)的简称。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF区分HTTP头和HTTP体。
所以,一旦我们能够控制HTTP头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS。
在以下情况下发生HTTP响应拆分:
- 数据通过不受信任的来源进入Web应用程序,最常见的是HTTP请求。
- 数据包含在发送给Web用户的HTTP响应标头中,而不会对恶意字符进行验证。
对于HRS最简单的利用方式是注入两个\r\n,之后写入XSS代码,来构造一个XSS。
比如一个网站接受URL参数/redirect?url=
,URL放在Response Location后面跳转。
1 | GET /redirect?url=new-page |
1 | 302 |
如果我们输入的是
1 | GET /redirect?url=%0d%0a%0d%0a<img src=1 onerror=alert(/xss/)> |
1 | 302 |
糟了,由于Location为空,Chrome并没有跳转,而是把两个CRLF后的<ing>
渲染了出来,造成了XSS注入。
此攻击方式由于会注入返回头,危害大于XSS,其能够轻松修改返回头,导致浏览器XSS Auditor,Frame Deny等防护失效,还能恶意注入cookie的JSESSIONID
实现会话固定漏洞。
预防
- 过滤\r,\n,并且避免输入的数据污染到其他HTTP头。
SQL 注入
TODO
SQL注入攻击通过从客户端或Web到服务端的输入数据包括SQL查询的注入。成功的SQL注入攻击可以从数据库读取敏感数据,修改数据库数据(插入/更新/删除),对数据库执行管理操作(如关闭DBMS),恢复DBMS文件中存在的给定文件的内容系统并在某些情况下向操作系统发出命令。
拼接注入
1 | select * from users where username='test' and password=md5('test') |
预防
- 对用户的输入进行校验,可以通过正则表达式,或限制长度。
- 使用参数化SQL或存储过程。
- 为每个应用使用单独的权限有限的数据库连接。
- 应用的异常信息应该给出尽可能少的提示,使用自定义的错误信息对原始错误信息进行包装。
参考文献
- Cross-site Scripting (XSS)
- Types of Cross-Site Scripting
- HtmlEncode和JavaScriptEncode(预防XSS)
- Sanitize untrusted HTML (to prevent XSS)
- 安全隐患,你对X-XSS-Protection头部字段理解可能有误
- XSS初体验
- XSS Filter Evasion Cheat Sheet
- CKEditor Best Practices
- 深究AngularJS——$sce的使用
- Web安全之CSRF攻击
- 新浪某站CRLF Injection导致的安全问题
- 固定SessionID 漏洞 攻击
- 用 sql 注入攻破一个网站
封面来源: pixabay, free for commercial use.
XSS, CSRF, CRLF, SQL注入学习笔记