《白帽子讲Web安全》3-4章
3、跨站脚本攻击(XSS)
3.1、XSS简介
跨站脚本攻击,英文全称是Cross Site Script,本来缩写是CSS,但是为了和层叠样式表(Cascading Style Sheet, CSS)有所区别,所以在安全领域叫“XSS”。“XSS根据效果的不同可以分成如下几类。
第一种类型:反射型XSS
反射型XSS只是简单地把用户输入的数据“反射”给浏览器。也就是说,黑客往往需要诱使用户“点击”一个恶意链接,才能攻击成功。反射型XSS也叫做“非持久型XSS”(Non-persistent XSS)。
第二种类型:存储型XSS
存储型XSS会把用户输入的数据“存储”在服务器端。这种XSS具有很强的稳定性。
比较常见的一个场景就是,黑客写下一篇包含有恶意JavaScript代码的博客文章,文章发表后,所有访问该博客文章的用户,都会在他们的浏览器中执行这段恶意的JavaScript代码。黑客把恶意的脚本保存到服务器端,所以这种XSS攻击就叫做“存储型XSS”。
存储型XSS通常也叫做“持久型XSS”(Persistent XSS),因为从效果上来说,它存在的时间是比较长的。
第三种类型:DOM Based XSS
实际上,这种类型的XSS并非按照“数据是否保存在服务器端”来划分,DOM Based XSS从效果上来说也是反射型XSS。单独划分出来,是因为DOM Based XSS的形成原因比较特别,发现它的安全专家专门提出了这种类型的XSS。出于历史原因,也就把它单独作为一个分类了。
通过修改页面的DOM节点形成的XSS,称之为DOM Based XSS。
在 HTML 文档中,DOM(文档对象模型)将整个页面表示为一个树形结构,其中每个节点代表文档中的一个元素、属性、文本等。这些节点构成了DOM 树,也称为节点树,DOM 节点构成了页面的结构,让我们能够动态地访问和更新文档的内容、结构和样式,其关系为:
文档节点(Document Node):整个 HTML 文档是一个文档节点,它是 DOM 树的根节点。
元素节点(Element Node):每个 HTML 元素都是一个元素节点。例如,<div>、<p>、<h1> 等标签都是元素节点。
文本节点(Text Node):HTML 元素内的文本内容是文本节点。例如,<p>Hello world!</p> 中的 “Hello world!” 是一个文本节点。
属性节点(Attribute Node):每个 HTML 属性也是一个节点。例如,<img src="image.jpg" alt="My Image"> 中的 src 和 alt 属性都是属性节点。
注释节点(Comment Node):HTML 中的注释也是节点。例如,<!-- This is a comment --> 是一个注释节点。
节点之间的关系如下:
父节点拥有子节点。例如,<html> 是整个文档的父节点,它拥有 <head> 和 <body> 两个子节点。
同级的子节点被称为同胞(兄弟或姐妹)。例如,<h1> 和 <p> 是 <body> 的同胞节点。
每个节点都有父节点,除了根节点(它没有父节点)。
12345678910
3.2、XSS攻击进阶
3.2.1、初探XSS Payload
XSS攻击成功后,攻击者能够对用户当前浏览的页面植入恶意脚本,通过恶意脚本, 控制用户的浏览器。这些用以完成各种具体功能的恶意脚本,被称为“XSS Payload”。
3.2.2、强大的XSS Payload
3.2.2.1、构造GET与POST请求
一个网站的应用,只需要接受HTTP协议中的GET 或 POST请求,即可完成所有操作。对于攻击者来说,仅通过JavaScript,就可以让浏览器发起这两种请求。假设Sohu博客所在域的某页面存在XSS漏洞,那么通过JavaScript,这个过程如下。 正常删除该文章的链接是:
http://blog.sohu.com/manage/entry.do?m=delete&id=156713012
对于攻击者来说,只需要知道文章的id,就能够通过这个请求删除这篇文章了。
再看一个复杂点的例子。如果网站应用者接受POST请求,那么攻击者如何实施XSS攻击呢?
下例是Douban的一处表单。攻击者将通过JavaScript发出一个POST 请求,提交此表单,最终发出一条新的消息。
在正常情况下,发出一条消息,浏览器发的包是:
要模拟这一过程,有两种方法。
第一种方法是,构造一个form表单,然后自动提交这个表单。
如果表单的参数很多的话,通过构造DOM节点的方式,代码将会非常冗长。所以可以直接写HTML代码,这样会使得整个代码精简很多。第二种方法是,通过XMLHttpRequest发送一个POST请求。 所以XSS攻击后,攻击者除了可以实施“Cookie劫持”外,还能够通过模拟GET、POST请求操作用户的浏览器。这在某些隔离环境中会非常有用,比如“Cookie劫持”失效时,或者目标用户的网络不能访问互联网等情况。
3.2.2.2、XSS钓鱼
反射型XSS属于XSS钓鱼吗?
—XSS 反射是一种直接利用用户输入的攻击方式,而 XSS 钓鱼则是通过欺骗用户来达到攻击目的。
XSS 反射:
定义:XSS 反射是一种攻击,攻击者通过在 URL 参数、表单字段或其他用户可控的输入中插入恶意脚本,使用户在访问特定页面时执行这些脚本。原理:攻击者构造一个恶意 URL,将脚本作为参数传递给目标网站。当用户点击或访问该 URL 时,网站会将脚本从 URL 中提取并执行,导致攻击成功。示例:以下是一个简单的 XSS 反射示例,如果用户访问这个 URL,就会触发弹出一个警告框:
https://example.com/search?query=<script>alert('XSS!')</script>
1
XSS 钓鱼:
定义:XSS 钓鱼是一种利用 XSS 漏洞来欺骗用户的攻击方式。攻击者通过伪装成受信任的网站或服务,诱使用户点击恶意链接或执行恶意操作。原理:攻击者创建一个看似合法的页面,其中包含恶意脚本。然后,他们通过社交工程、电子邮件或其他方式将这个页面链接发送给用户。用户点击链接后,脚本会执行,可能导致信息泄露、账户被接管等问题。示例:攻击者可以伪装成银行网站,诱使用户输入敏感信息(如密码)。
3.2.2.3、识别用户浏览器
由于浏览器之间的实现存在差异——不同的浏览器会各自实现一些独特的功能,而同一个浏览器的不同版本之间也可能会有细微差别。所以通过分辨这些浏览器之间的差异,就能准确地判断出浏览器版本,而几乎不会误报。这种方法比读取UserAgent要准确得多。
//代码为文字识别后手动纠正可能和作者的原代码有差别
if (window .ActivexObject)1// MSTE 6.0 or below
//判断是否是IE7以上
if (document.documentElement && typeof document.documentElement.style.maxHeight!=undefined"){
//判断是否是工E8+
if (typeof document.adoptNode != "undefined"){// Safari3 & FF & Opera & Chrome & IE8
//MSIE 8.0因为同时满足前两个if判断,所以//在这里是IE 8
}
//MSIE7.0 否则就是IE7
}
return "msie";
else if (typeof window.opera != "undefined"){//Opera独占
//"Opera "+window.opera.version()
return "opera";
}
else if (typeof window.netscape != "undefined"){//Mozilla独占
//"Mozilla"
/可以准确识别大版本
if(typeof window.Iterator != "undefined"){
//Firefox 2以上支持这个对象
if (typeof document.styleSheetSets != "undefined"){// Firefox 3 & Opera 9
//Firefox 3同时满足这些条件的必然是Firefox 3了
return "mozilla”;
}
else if (typeof window.pageXOffset != "undefined"){ // Nozilla& Safari
//"Safari"
try{
if (typeof external.AddSearchProvider != "undefined") { // Firefox & Google Chrome
//Google Chrome
return "chrome" ;
}
}catch (e){
return "safari";
}
}
else{//unknown
//Unknown
return "unknown";
}
123456789101112131415161718192021222324252627282930313233343536373839
安全研究者Gareth Heyes曾经找到一种更巧妙的方法,通过很精简的代码,即可识别出不同的浏览器。
//Firefox detector 2/3 by DoctorDan
FF=/a/[-1]=='a'
//Firefox 3 by me:-
FF3=(function x(){})[-5]=='x'
//Firefox 2 by me:-
FF2=(function x(){})[-6]=='x'
//IE detector I posted previously
IE='v'=='v'
//Safari detector by me
Saf=/a/.__proto__=='//'
//Chrome by me
Chr=/source/.test((/a/.toString+''))
//Opera by me
Op=/^function (/.test([].sort)
//IE6 detector using conditionals
try {IE6=@cc_on @_jscript_version <= 5.7&&@_jscript_build<10000
12345678910111213141516
精简为一行代码,即:
B=(function x(){})[-5]=='x'?'FF3':(function x(){})[-6]=='x'?'FF2':/a/[-1]=='a'?'FF':'v'=='v'?'IE':/a/.__proto__=='//'?'Saf':/s/.test(/a/.toString)?'Chr':/^function (/.test([].sort)?'Op':'Unknown'
1
3.2.2.4、识别用户安装的软件
在XSS Payload中使用时,可以在Flash的ActionScript中读取system.capabilities对象后,将结果通过ExtenalInterface传给页面的JavaScript。这个过程在此不再赘述了。
该过程如下:
Flash 和 JavaScript 之间的通信:
Flash 可以通过 ExternalInterface
类与嵌入它的 HTML 页面中的 JavaScript 进行通信。ExternalInterface
允许 Flash 应用程序调用 JavaScript 函数,也允许 JavaScript 调用 Flash 应用程序中的函数。
system.capabilities
对象:
system.capabilities
是 Flash 中的一个对象,用于提供关于用户系统和浏览器的信息。通过读取 system.capabilities
,我们可以获取有关浏览器、操作系统、屏幕分辨率、Flash 版本等方面的信息。
XSS Payload 示例:
假设有一个 Flash 文件(SWF),其中包含以下 ActionScript 代码:
import flash.external.ExternalInterface;
// 获取系统和浏览器信息
var capabilities:Object = System.capabilities;
var browserInfo:String = capabilities.version + " " + capabilities.os;
// 将信息传递给 JavaScript
ExternalInterface.call("receiveBrowserInfo", browserInfo);
12345678
在 HTML 页面中,需要有一个 JavaScript 函数来接收 Flash 传递的信息:
function receiveBrowserInfo(info) {
console.log("Received browser info from Flash: " + info);
// 在这里执行其他操作,比如记录日志或更新页面内容
}
1234
当 Flash 文件加载并执行时,它会调用 receiveBrowserInfo
函数,并将浏览器信息作为参数传递给 JavaScript。
3.2.2.5、CSS History Hack
我们再看看另外一个有趣的XSS Payload—通过CSS,来发现一个用户曾经访问过的网站。
这个技巧最早被Jeremiah Grossman发现,其原理是利用style的visited属性—如果用户曾经访问过某个链接,那么这个链接的颜色会变得与众不同。
3.2.2.6、获取用户的真实IP
JavaScript本身并没有提供获取本地IP地址的能力,有没有其他办法?一般来说,XSS攻击需要借助第三方软件来完成。比如,客户端安装了Java环境(JRE),那么XSS就可以通过调用Java Applet的接口获取客户端的本地IP地址。
3.2.3、XSS攻击平台
Attack API
Attack API是安全研究者pdp所主导的一个项目,它总结了很多能够直接使用XSS Payload,归纳为API的方式。比如上节提到的“获取客户端本地信息的API”就出自这个项目。BeEF
BeEF曾经是最好的XSS演示平台。不同于Attack API,BeEF所演示的是一个完整的XSS攻击过程。BeEF有一个控制后台,攻击者可以在后台控制前端的一切。XSS-Proxy
XSS-Proxy是一个轻量级的XSS攻击平台,通过嵌套iframe的方式可以实时地远程控制被XSS攻击的浏览器。
3.2.4、终极武器:XSS Worm
3.2.4.1、Samy Worm
Samy蠕虫的技术细节分析一般来说,用户之间发生交互行为的页面,如果存在存储型XSS,则比较容易发起XSS Worm攻击。比如,发送站内信、用户留言等页面,都是XSS Worm的高发区,需要重点关注。而相对的,如果一个页面只能由用户个人查看,比如“用户个人资料设置”页面,因为缺乏用户之间互动的功能,所以即使存在XSS,也不能被用于XSS Worm的传播。
3.2.4.2、百度空间蠕虫
3.2.5、调试JavaScript
Firebug是JavaScript调试的第一利器。如果要说缺点,那就是除了Firefox外,对其他浏览器的支持并不好。在需要调试IE而又没有其他可用的JavaScript Debugger时,IE 8 Developer Tools是个不错的选择。Fiddler是一个本地代理服务器,需要将浏览器设置为使用本地代理服务器上网才可使用。Fiddler会监控所有的浏览器请求,并有能力在浏览器请求中插入数据。HttpWatch也能够监控所有的浏览器请求,在目标网站是HTTPS时会特别有用。但HttpWatch并不能调试JavaScript,它仅仅是一个专业的针对Web的“Sniffer”。
3.2.6、XSS构造技巧
3.2.6.1、利用字符编码
3.2.6.2、绕过长度限制
利用事件(Event)来缩短所需字节数,onclick、onfocus、onblur等。不足是能够缩短的字节数是有限的。将XSS Payload写到别处,再通过简短的代码加载这段XSS Payload。最常用的一个“藏代码”的地方,就是“location.hash”。而且根据HTTP协议,location.hash的内容不会在HTTP包中发送,所以服务器端的Web日志中并不会记录下location.hash里的内容,从而也更好地隐藏了黑客真实的意图。因为location.hash的第一个字符是#,所以必须去除第一个字符才行。location.hash本身没有长度限制,但是浏览器的地址栏是有长度限制的,不过这个长度已经足够写很长的XSS Payload了。要是地址栏的长度也不够用,还可以再使用加载远程JS的方法,来写更多的代码。在某些环境下,可以利用注释符绕过长度限制。比如我们能控制两个文本框,第二个文本框允许写入更多的字节。此时可以利用HTML的“注释符号”,把两个文本框之间的HTML代码全部注释掉,从而“打通”两个
<input>
标签。
3.2.6.3、使用<base>
标签
<base>
标签并不常用,它的作用是定义页面上的所有使用“相对路径”标签的hosting地址。
需要特别注意的是,在有的技术文档中,提到<base>
标签只能用于<head>
标签之内,其实这是不对的。<base>
标签可以出现在页面的任何地方,并作用于位于该标签之后的所有标签。
3.2.6.4、window.name的妙用
window.name对象是一个很神奇的东西。对当前窗口的window.name对象赋值,没有特殊字符的限制。因为window对象是浏览器的窗体,而并非document对象,因此很多时候window对象不受同源策略的限制。攻击者利用这个对象,可以实现跨域、跨页面传递数据。在某些环境下,这种特性将变得非常有用。
跟着书中的教程试了一下,在一个初始网站上给window.name赋值,然后在初始网站下用了window.location或location.href跳转到目标网站,此时的window.name还是初始网站上所赋的值。因此可以实现缩短XSS Payload长度的作用。
3.2.7、变废为宝:Mission Impossible
3.2.7.1、Apache Expect Header XSS
Apache的漏洞允许攻击者通过向HTTP头中注入恶意数据,触发未经处理的Expect头中的HTML代码执行。这个漏洞曾被认为是“鸡肋”,但后来安全研究者Amit Klein成功地利用了“使用Flash构造请求”的方法,将其变废为宝。
印证了安全是一个动态的过程
3.2.7.2、Anehta的回旋镖
回旋镖的思路就是:如果在B域上存在一个反射型“XSS_B”,在A域上存在一个存储型“XSS_A”,当用户访问A域上的“XSS_A”时,同时嵌入B域上的“XSS_B”,则可以达到在A域的XSS攻击B域用户的目的。
读的有点吃力了
3.2.8、 容易被忽视的角落:Flash XSS
Flash的安全问题也是其被淘汰的原因吧。
3.2.9、 真的高枕无忧吗:JavaScript开发框架
使用JavaScript框架并不能让开发者高枕无忧,同样可能存在安全问题。除了需要关注框架本身的安全外,开发者还要提高安全意识,理解并正确地使用开发框架。
3.3、XSS的防御
3.3.1、四两拨千斤:HttpOnly
HttpOnly 属性可以防止 JavaScript 访问 Cookie,降低 XSS 攻击的风险。如果某个 Cookie 带有 HttpOnly 属性,JavaScript 将无法读取该 Cookie。用户与服务端的正常交互不受影响,因为 HttpRequest 包中仍然会携带这个 Cookie 信息。
但是,HttpOnly不是万能的,添加了HttpOnly不等于解决了XSS问题。XSS攻击带来的不光是Cookie劫持问题,还有窃取用户信息、模拟用户身份执行操作等诸多严重的后果。如前文所述,攻击者利用AJAX构造HTTP请求,以用户身份完成的操作,就是在不知道用户Cookie的情况下进行的。使用HttpOnly有助于缓解XSS攻击,但仍然需要其他能够解决XSS漏洞的方案。
3.3.2、输入检查
输入检查的逻辑,必须放在服务器端代码中实现。如果只是在客户端使用JavaScript进行输入检查,是很容易被攻击者绕过的。目前Web开发的普遍做法,是同时在客户端JavaScript中和服务器端代码中实现相同的输入检查。客户端JavaScript的输入检查,可以阻挡大部分误操作的正常用户,从而节约服务器资源。XSS Filter在用户提交数据时获取变量,并进行XSS检查;但此时用户数据并没有结合渲染页面的HTML代码,因此XSSFilter对语境的理解并不完整。 XSS Filter还有一个问题——其对“<”、“>”等字符的处理,可能会改变用户数据的语义。
3.3.3、输出检查
一般来说,除了富文本的输出外,在变量输出到HTML页面时,可以使用编码或转义的方式来防御XSS攻击。
3.3.3.1、安全的编码函数
针对HTML代码的编码方式是HtmlEncode。在PHP中,有htmlentities()和htmlspecialchars()两个函数可以满足安全要求。相应地,JavaScript的编码方式可以使用JavascriptEncode(除了数字、字母外的所有字符,都使用十六进制“xHH”的方式进行编码)。还有许多用于各种情况的编码函数,比如XMLEncode(其实现与HtmlEncode类似)、JSONEncode(与JavascriptEncode类似)等。
3.3.3.2、只需一种编码吗
3.3.4、正确地防御XSS
想要根治XSS问题,可以列出所有XSS可能发生的场景,再一一解决:
在HTML标签中输出对应的防御方案——HtmlEncode
在HTML属性中输出对应的防御方案——HtmlEncode
在<script>
标签中输出对应的防御方案——JavascriptEncode
在CSS中输出对应的防御方案——OWASP ESAPI中的encodeForCSS()函数
在地址中输出——
一般来说,在URL的path(路径)或者search(参数)中输出,使用URLEncode即可。如果变量是整个URL,则应该先检查变量是否以“http”开头(如果不是则自动添加),以保证不会出现伪协议类的XSS攻击。在此之后,再对变量进行URLEncode。
伪协议如何导致XSS:一个URL的组成如:[Protocal] [Host] [Path][Search][Hash]其中Protocal处输入伪协议可能导致脚本执行,如“javascript”、“vbscript”以及“dataURI”等伪协议。
3.3.5、处理富文本
在过滤富文本时,“事件”应该被严格禁止,因为“富文本”的展示需求里不应该包括“事件”这种动态效果。
有一些比较成熟的开源项目,实现了对富文本的XSS检查。
Anti-Samy 是OWASP上的一个开源项目,也是目前最好的XSS Filter。最早它是基于Java的,现在已经扩展到.NET等语言。
在PHP中,可以使用另外一个广受好评的开源项目:HTMLPurify。
写文章的MarkDown编辑器同样也属于富文本范畴。
3.3.6、防御DOM Based XSS
DOM Based XSS是从JavaScript中输出数据到HTML页面里。而前文提到的方法都是针对“从服务器应用直接输出到HTML页面”的XSS漏洞,因此并不适用于DOM Based XSS。
书中的例子写到“当document.write输出数据到HTML页面时,浏览器重新渲染了页面”,浏览器会在以下几种情况下重新渲染页面:
DOM结构变化:添加或删除可见的DOM元素,或者元素的内容发生变化导致尺寸变化。样式属性变更:影响元素几何属性的CSS属性发生变化,例如宽度、高度、外边距、内边距、边框等。获取布局信息:当JavaScript请求读取依赖于布局信息的样式属性时,如offsetTop、offsetLeft、scrollTop、scrollWidth等。窗口或设备视口尺寸变化:用户调整浏览器窗口大小时,特别是在响应式设计场景下。
这里当使用document.write将数据输出到HTML页面时,它会直接修改DOM,添加新的内容,从而导致页面的重新渲染。浏览器渲染页面的机制和原理:
当用户在浏览器地址栏输入网址并访问页面时,浏览器经历了以下阶段:
1.HTTP请求阶段:浏览器向服务器发起请求,包括DNS解析、TCP协议的握手和挥手,以及HTTP和HTTPS之间的区别。
2. HTTP响应阶段:服务器返回相应的页面源代码,包括HTTP状态码、304缓存和HTTP报文。
3. 浏览器渲染阶段:浏览器将源代码渲染成可视化页面。这一阶段包括以下步骤:
DOM树渲染:浏览器解析HTML标签,构建DOM树。但仅有DOM树还不能显示页面,需要进一步处理。CSSDOM树渲染:浏览器解析CSS样式,生成CSSOM(CSS Object Model)。生成Render Tree(渲染树):将DOM树和CSSOM结合,生成渲染树。回流(Layout):根据渲染树计算元素的确切位置和大小,触发重新布局。重绘(Painting):根据渲染树的几何信息,绘制页面内容。GPU展现:通知GPU按照渲染树逐步绘制页面。
回流一定会触发重绘,但重绘不一定触发回流。优化时要减少DOM的回流和重绘,例如分离读写操作、减少HTTP请求次数和大小等。
从JavaScript输出到HTML页面,也相当于一次XSS输出的过程,需要分语境使用不同的编码函数。安全研究者Stefano Di Paola设立了一个DOM Based XSS的cheatsheet,有兴趣深入研究的读者可以参考。
3.3.7、换个角度看XSS的风险
一般来说,存储型XSS的风险会高于反射型XSS。因为存储型XSS会保存在服务器上,有可能会跨页面存在。从攻击过程来说,反射型XSS,一般要求攻击者诱使用户点击一个包含XSS代码的URL链接;而存储型XSS,则只需要让用户查看一个正常的URL链接。从风险的角度看,用户之间有互动的页面,是可能发起XSS Worm攻击的地方。
3.4、小结
这里想到了之前刷题用到的xss platform。xss platform,提供了注入语句,将语句插入到出现xss的地方,该url就被污染了,然后别人访问该url就会被盗取cookie。
4、跨站点请求伪造(CSRF)
CSRF的全名是Cross Site Request Forgery,翻译成中文就是跨站点请求伪造。
4.1、CSRF简介
4.2、CSRF进阶
4.2.1、浏览器的Cookie策略
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。两种常见 Cookie 类型:
Session Cookie(临时 Cookie):
这些 Cookie 没有指定过期时间;保存在浏览器进程的内存空间中,当用户关闭浏览器时,Session Cookie 就会失效;通常用于保持用户登录状态,只在用户会话期间有效;例如,登录某个网站后,该网站会使用 Session Cookie 来跟踪你的登录状态。
Third-party Cookie(本地 Cookie):
这些 Cookie 由服务器在设置时指定了过期时间(Expires/Max-age);只有到了过期时间后,Third-party Cookie 才会失效;保存在本地,不受浏览器关闭的影响;例如,广告商或其他网站可能在你访问某个网站时设置 Third-party Cookie,以便跟踪你的浏览行为。
此外,还有一些其他属性与 Cookie 相关:
Domain(域):表示当前 Cookie 所属于哪个域或子域下。例如,.baidu.com
表示在 .baidu.com
下可以访问。Path(路径):表示 Cookie 的所属路径,一般设为“/”,表示同一个站点的所有页面都可以访问这个 Cookie。Secure:表示该 Cookie 只能用 HTTPS 传输,通常用于包含认证信息的 Cookie。HttpOnly:表示此 Cookie 必须用于 HTTP 或 HTTPS 传输,防止客户端脚本访问操作此 Cookie,有助于避免 XSS 攻击。SameSite:用于防止跨站请求伪造攻击(CSRF),可以有三种值:Strict、Lax 和 None。
4.2.2、P3P头的副作用
P3P Header是W3C制定的一项关于隐私的标准,全称是The Platform for Privacy Preferences。如果网站返回给浏览器的HTTP头中包含有P3P头,则在某种程度上来说,将允许浏览器发送第三方Cookie。在IE下即使是
<iframe>
、<script>
等标签也将不再拦截第三方Cookie的发送。
4.2.3、GET? POST?
如果服务器端已经区分了GET与POST,那么攻击者有什么方法呢?对于攻击者来说,有若干种方法可以构造出一个POST请求。
最简单的方法,就是在一个页面中构造好一个form表单,然后使用JavaScript自动提交这个表单。攻击者甚至可以将这个页面隐藏在一个不可见的iframe窗口中,那么整个自动提交表单的过程,对于用户来说也是不可见的。
4.2.4、Flash CSRF
Flash也有多种方式能够发起网络请求,包括POST。除了URLRequest外,在Flash中还可以使用getURL,loadVars等方式发起请求。
4.2.5、CSRF Worm
4.3、CSRF的防御
4.3.1、验证码
4.3.2、Referer Check
常见的互联网应用,页面与页面之间都具有一定的逻辑关系,这就使得每个正常请求的Referer具有一定的规律。 缺陷:
服务器并非什么时候都能取到Referer 。很多用户出于隐私保护的考虑,限制了Referer的发送。在某些情况下,浏览器也不会发送Referer,比如从HTTPS跳转到HTTP,出于安全的考虑,浏览器也不会发送Referer。在Flash的一些版本中,曾经可以发送自定义的Referer头。
4.3.3、Anti CSRF Token
现在业界针对CSRF的防御,一致的做法是使用一个Token。
4.3.3.1、CSRF的本质
CSRF为什么能够攻击成功?其本质原因是重要操作的所有参数都是可以被攻击者猜测到的。
一个解决方案:把参数加密,或者使用一些随机数,从而让攻击者无法猜测到参数值。
在实际应用时,Token可以放在用户的Session中,或者浏览器的Cookie中。由于Token的存在,攻击者无法再构造出一个完整的URL实施CSRF攻击。
Token需要同时放在表单和Session中。在提交请求时,服务器只需验证表单中的Token,与用户Session(或Cookie)中的Token是否一致,如果一致,则认为是合法请求;如果不一致,或者有一个为空,则认为请求不合法,可能发生了CSRF攻击。
4.3.3.2、Token的使用原则
Token 生成原则:
防御 CSRF(跨站请求伪造)的 Token 是根据“不可预测性原则”设计的方案。Token 的生成必须足够随机,通常使用安全的随机数生成器来生成。
Token 使用方便性:
在一个用户的有效生命周期内,可以允许使用同一个 Token,直到它被消耗掉。如果用户已经提交了表单,应该重新生成一个新的 Token,以确保安全性。当用户同时打开多个相同页面并操作时,某个页面消耗掉 Token 后,其他页面的表单仍保存着已消耗的 Token,导致其他页面再次提交时出现 Token 错误。解决方法是生成多个有效的 Token,以适应多页面共存的场景。
Token 存储位置问题:
如果 Token 存储在 Cookie 中而不是服务器端的 Session 中,会引发新的问题。
Token 保密性:
使用 Token 时应注意其保密性。尽量将 Token 放在表单中,而不是 URL 中。将敏感操作由 GET 请求改为 POST 请求,以 form 表单(或 AJAX)的形式提交,可以避免 Token 泄露。