【全棧修煉】CORS和CSRF修煉寶典

《全棧修煉》系列

  1. 《【全棧修煉】OAuth2修煉寶典》

CORS 和 CSRF 太容易混淆了,看完本文,你就清楚了。

一、CORS 和 CSRF 區別

先看下圖:

CORS 和 CSRF 區別

兩者概念完全不同,另外常常我們也會看到 XSS ,這裏一起介紹:

  1. CORS : Cross Origin Resourse-Sharing 跨站資源共享

  2. CSRF : Cross-Site Request Forgery 跨站請求僞造

  3. XSS : Cross Site Scrit 跨站腳本攻擊(爲與 CSS 區別,所以在安全領域叫 XSS)

二、CORS

1. 概念

跨來源資源共享(CORS),亦譯爲跨域資源共享,是一份瀏覽器技術的規範,提供了 Web 服務從不同網域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現代版。與 JSONP 不同,CORS 除了 GET 請求方法以外也支持其他的 HTTP 請求。用 CORS 可以讓網頁設計師用一般的 XMLHttpRequest,這種方式的錯誤處理比 JSONP 要來的好。另一方面,JSONP 可以在不支持 CORS 的老舊瀏覽器上運作。現代的瀏覽器都支持 CORS。
—— 維基百科

核心知識: CORS是一個W3C標準,它允許瀏覽器向跨源服務器,發出XMLHttpRequest 請求,從而克服 AJAX 只能同源使用的限制。

因此,實現 CORS 通信的關鍵是服務器。只要服務器實現了 CORS 接口,就可以跨源通信,即爲了解決跨域問題

2. CORS 請求類型

瀏覽器將 CORS 請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

簡單請求一般包括下面兩種情況:

情況 描述
請求方法 請求方法爲:HEADGETPOST
HTTP 頭信息 HTTP 頭信息不超出以下幾種字段:Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值 application/x-www-form-urlencodedmultipart/form-datatext/plain

凡是不同時滿足上面兩個條件,就屬於非簡單請求

3. 簡單請求的 CORS 流程

當瀏覽器發現我們的 AJAX 請求是個簡單請求,便會自動在頭信息中,增加一個 Origin 字段。

Origin 字段用來說明本次請求的來源(包括協議 + 域名 + 端口號),服務端根據這個值來決定是否同意此次請求。

簡單請求的 CORS 流程

Origin 指定的源不在許可範圍,服務器會返回一個正常的 HTTP 迴應,但瀏覽器會在響應頭中發現 Access-Control-Allow-Origin 字段,便拋出異常。

Origin 指定的源在許可範圍,服務器返回的響應頭中會多出幾個頭信息字段:

簡單請求的 CORS 流程

除了上面圖中的頭信息,一般會有以下三個相關頭信息:

  1. Access-Control-Allow-Origin

該字段是必須的。表示許可範圍的域名,通常有兩種值:請求時 Origin 字段的值或者 *(星號)表示任意域名。

  1. Access-Control-Allow-Credentials

該字段可選。布爾值,表示是否允許在 CORS 請求之中發送 Cookie 。若不攜帶 Cookie 則不需要設置該字段。

當設置爲 trueCookie 包含在請求中,一起發送給服務器。還需要在 AJAX 請求中開啓 withCredentials 屬性,否則瀏覽器也不會發送 Cookie

 

let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
  1. Access-Control-Expose-Headers

該字段可選。可以設置需要獲取的字段。因爲默認 CORS 請求時,XMLHttpRequest 對象的getResponseHeader()方法只能拿到以下 6 個基本字段:

Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

4. 非簡單請求的 CORS 流程

非簡單請求情況如:請求方法是 PUT / DELETE 或者 Content-Type:application/json 類型的請求。

在非簡單請求發出 CORS 請求時,會在正式通信之前增加一次 “預檢”請求(OPTIONS方法),來詢問服務器,本次請求的域名是否在許可名單中,以及使用哪些頭信息。

“預檢”請求 通過以後,纔會正式發起 AJAX 請求,否則報錯。

4.1 預檢請求

 

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
User-Agent: Mozilla/5.0...
...

“預檢”請求 信息中包含兩個特殊字段:

  1. Access-Control-Request-Method

該字段是必須的,用來列出瀏覽器的 CORS 請求會用到哪些 HTTP 方法,上例是 PUT

  1. Access-Control-Request-Headers

指定瀏覽器 CORS 請求額外發送的頭信息字段,上例是 X-Custom-Header

4.2 預檢響應

 

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Connection: Keep-Alive
...

當預檢請求通過以後,在預檢響應頭中,會返回 Access-Control-Allow- 開頭的信息,其中 Access-Control-Allow-Origin 表示許可範圍,值也可以是 *

當預檢請求拒絕以後,在預檢響應頭中,不會返回 Access-Control-Allow- 開頭的信息,並在控制檯輸出錯誤信息。

三、CSRF

1. 概念

跨站請求僞造(英語:Cross-site request forgery),也被稱爲 one-click attack 或者 session riding,通常縮寫爲 CSRF 或者 XSRF, 是一種挾制用戶在當前已登錄的Web應用程序上執行非本意的操作的攻擊方法。跟跨網站腳本(XSS)相比,XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任。
—— 維基百科

核心知識: 跨站點請求僞造請求。

簡單理解: 攻擊者盜用你的身份,以你的名義發送惡意請求。

常見場景:以你名義發送郵件,發消息,盜取你的賬號,甚至於購買商品,虛擬貨幣轉賬等等。

造成影響:個人隱私泄露以及財產安全。

2. CSRF 攻擊流程

CSRF 攻擊流程

上面描述了 CSRF 攻擊的流程,其中受害者完成兩個步驟:

  1. 登錄受信任網站 A ,並在本地生成保存Cookie;
  2. 在不登出 A 情況下,訪問病毒網站 B;

可以理解爲:若以上兩個步驟沒有都完成,則不會受到 CSRF 攻擊。

3. 服務端防禦 CSRF 攻擊

服務端防禦的方式有很多,思想類似,都是在客戶端頁面增加僞隨機數

3.1 Cookie Hashing(所有表單都包含同一個僞隨機數)

最簡單有效方式,因爲攻擊者理論上無法獲取第三方的Cookie,所以表單數據僞造失敗。以 php 代碼爲例:

 

<?php
    //構造加密的Cookie信息
    $value = "LeoDefenseSCRF";
    setcookie("cookie", $value, time()+3600);
?>

在表單裏增加Hash值,以認證這確實是用戶發送的請求。

 

<?php
    $hash = md5($_COOKIE['cookie']);
?>
<form method="POST" action="transfer.php">
  <input type="text" name="toBankId">
  <input type="text" name="money">
  <input type="hidden" name="hash" value="<?=$hash;?>">
  <input type="submit" name="submit" value="Submit">
</form>

然後在服務器端進行Hash值驗證。

 

<?php
    if(isset($_POST['check'])) {
       $hash = md5($_COOKIE['cookie']);
       if($_POST['check'] == $hash) {
            doJob();
        } else {
     //...
      }
    } else {
      //...
    }
?>

這個方法個人覺得已經可以杜絕99%的CSRF攻擊了,那還有1%呢....由於用戶的 Cookie 很容易由於網站的 XSS 漏洞而被盜取,這就另外的1%。

一般的攻擊者看到有需要算Hash值,基本都會放棄了,某些除外,所以如果需要100%的杜絕,這個不是最好的方法。

3.2 驗證碼

思路是:每次用戶提交都需要用戶在表單中填寫一個圖片上的隨機字符串,這個方案可以完全解決CSRF,但易用性差,並且驗證碼圖片的使用涉及 MHTML 的Bug,可能在某些版本的微軟IE中受影響。

3.3 One-Time Tokens(不同的表單包含一個不同的僞隨機值)

需要注意“並行會話的兼容”。如果用戶在一個站點上同時打開了兩個不同的表單,CSRF保護措施不應該影響到他對任何表單的提交。考慮一下如果每次表單被裝入時站點生成一個僞隨機值來覆蓋以前的僞隨機值將會發生什麼情況:用戶只能成功地提交他最後打開的表單,因爲所有其他的表單都含有非法的僞隨機值。必須小心操作以確保CSRF保護措施不會影響選項卡式的瀏覽或者利用多個瀏覽器窗口瀏覽一個站點。

php 實現如下:

  1. 先是 Token 令牌生成函數(gen_token())和 Session 令牌生成函數(gen_stoken()):

 

<?php
    function gen_token() {
        $token = md5(uniqid(rand(), true));
        return $token;
    }
  function gen_stoken() {
    $pToken = "";
    if($_SESSION[STOKEN_NAME]  == $pToken){
      $_SESSION[STOKEN_NAME] = gen_token();
    }    
    else{ }
  }
?>
  1. WEB表單生成隱藏輸入域的函數:

 

<?php
    function gen_input() {
        gen_stoken();
        echo "<input type=\"hidden\" name=\"" . FTOKEN_NAME . "\"
            value=\"" . $_SESSION[STOKEN_NAME] . "\"> ";
  }
?>
  1. WEB表單結構:

 

<?php
    session_start();
    include("functions.php");
?>
<form method="POST" action="transfer.php">
    <input type="text" name="toBankId">
    <input type="text" name="money">
    <? gen_input(); ?>
    <input type="submit" name="submit" value="Submit">
</FORM>
  1. 服務端覈對令牌

這一步很簡單,不做詳細介紹。

四、XSS

注意: 本文簡單介紹 XSS 知識,具體詳細可以閱讀 FEWY 寫的 《跨站腳本攻擊—XSS》https://segmentfault.com/a/1190000020402185

1. 概念

跨站腳本(英語:Cross-site scripting,通常簡稱爲:XSS)是一種網站應用程序的安全漏洞攻擊,是代碼注入的一種。它允許惡意用戶將代碼注入到網頁上,其他用戶在觀看網頁時就會受到影響。這類攻擊通常包含了HTML以及用戶端腳本語言。
—— 維基百科

XSS 攻擊,一般是指攻擊者通過在網頁中注入惡意腳本,當用戶瀏覽網頁時,惡意腳本執行,控制用戶瀏覽器行爲的一種攻擊方式。

常見 XSS 危害有:

  • 竊取用戶Cookie,獲取用戶隱私,盜取用戶賬號。
  • 劫持用戶(瀏覽器)會話,從而執行任意操作,例如進行非法轉賬、強制發表日誌、發送電子郵件等。
  • 強制彈出廣告頁面,刷流量,傳播跨站腳本蠕蟲,網頁掛馬等。
  • 結合其他漏洞,如 CSRF 漏洞,實施進一步的攻擊。

2. XSS 分類

XSS 分類

3. XSS 防禦

3.1 方法1:瀏覽器自帶防禦 (X-XSS-Protection )

現今主流瀏覽器(Internet Explorer,Chrome 和 Safari)帶有 HTTP X-XSS-Protection 響應頭,當檢測到跨站腳本攻擊(XSS)時,瀏覽器將停止加載頁面。

X-XSS-Protection 響應頭有以下 4 個值:

  • X-XSS-Protection: 0

禁止XSS過濾。

  • X-XSS-Protection: 1

啓用XSS過濾(通常瀏覽器是默認的)。 如果檢測到跨站腳本攻擊,瀏覽器將清除頁面(刪除不安全的部分)。

  • X-XSS-Protection: 1; mode=block

啓用XSS過濾。 如果檢測到攻擊,瀏覽器將不會清除頁面,而是阻止頁面加載。

  • X-XSS-Protection: 1; report=<reporting-uri>

啓用XSS過濾。 如果檢測到跨站腳本攻擊,瀏覽器將清除頁面並使用CSP report-uri指令的功能發送違規報告。

注意:

這並不能完全防止反射型 XSS,而且也並不是所有瀏覽器都支持 X-XSS-Protection,存在兼容性問題。

它只對反射型 XSS 有一定的防禦力,其原理也只是檢查 URL 和 DOM 中元素的相關性。

3.2 方法2:轉義

即將常用特殊字符進行轉義,避免攻擊者使用構造特殊字符來注入腳本。需要在客戶端和服務端,都對用戶輸入的數據進行轉義。

常見需要轉義的特殊字符如 <>&"'

轉義方法:

 

function escapeHTML(str) {
    if (!str) return '';
    str = str.replace(/&/g, "&amp;");
    str = str..replace(/</g, "&lt;");
    str = str..replace(/>/g, "&gt;");
    str = str..replace(/"/g, "&quot;");
    str = str..replace(/'/g, "&#39;");
    return str;
};

3.3 方法3:過濾

常見於富文本內容,因爲其需要保留 HTML,所以不能直接使用轉義方法,而可以通過使用白名單,來允許特定的 HTML 標籤及屬性,來抵禦 XSS 攻擊。

3.4 方法4:內容安全策略(CSP)

內容安全策略(Content Security Policy,CSP),實質就是白名單制度,開發者明確告訴客戶端,哪些外部資源可以加載和執行,大大增強了網頁的安全性。

兩種方法可以啓用 CSP。

  1. 通過 HTTP 頭信息的 Content-Security-Policy 的字段:

 

Content-Security-Policy: script-src 'self'; 
                         object-src 'none';
                         style-src cdn.example.org third-party.org; 
                         child-src https:
  1. 通過網頁的 <meta> 標籤

 

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">

上面代碼中,CSP 做了如下配置:

  • 腳本: 只信任當前域名
  • <object>標籤: 不信任任何 URL,即不加載任何資源
  • 樣式表: 只信任 cdn.example.orgthird-party.org
  • 頁面子內容,如 <frame><iframe>: 必須使用HTTPS協議加載
  • 其他資源: 沒有限制
  • 啓用後,不符合 CSP 的外部資源就會被阻止加載。

參考文章

  1. 《跨域資源共享 CORS 詳解》
  2. 《CSRF & CORS》
  3. 《淺談CSRF攻擊方式》
  4. 《跨站腳本攻擊—XSS》

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章