CORS 和 CSRF 太容易混淆了,看完本文,你就清楚了。php
先看下圖:html
二者概念徹底不一樣,另外經常咱們也會看到 XSS ,這裏一塊兒介紹:前端
跨來源資源共享(CORS),亦譯爲跨域資源共享,是一份瀏覽器技術的規範,提供了 Web 服務從不一樣網域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現代版。與 JSONP 不一樣,CORS 除了 GET 請求方法之外也支持其餘的 HTTP 請求。用 CORS 可讓網頁設計師用通常的 XMLHttpRequest,這種方式的錯誤處理比 JSONP 要來的好。另外一方面,JSONP 能夠在不支持 CORS 的老舊瀏覽器上運做。現代的瀏覽器都支持 CORS。
—— 維基百科
核心知識: CORS是一個W3C標準,它容許瀏覽器向跨源服務器,發出XMLHttpRequest
請求,從而克服 AJAX 只能同源使用的限制。git
所以,實現 CORS 通訊的關鍵是服務器。只要服務器實現了 CORS 接口,就能夠跨源通訊,即爲了解決跨域問題。github
瀏覽器將 CORS 請求分紅兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。json
簡單請求通常包括下面兩種狀況:segmentfault
狀況 | 描述 |
---|---|
請求方法 | 請求方法爲:HEAD 或 GET 或 POST ; |
HTTP 頭信息 | HTTP 頭信息不超出如下幾種字段:Accept <br/>Accept-Language <br/>Content-Language <br/>Last-Event-ID <br/>Content-Type :只限於三個值 application/x-www-form-urlencoded 、multipart/form-data 、text/plain |
凡是不一樣時知足上面兩個條件,就屬於非簡單請求。api
當瀏覽器發現咱們的 AJAX 請求是個簡單請求,便會自動在頭信息中,增長一個 Origin
字段。跨域
Origin
字段用來講明本次請求的來源(包括協議 + 域名 + 端口號),服務端根據這個值來決定是否贊成這次請求。瀏覽器
當 Origin
指定的源不在許可範圍,服務器會返回一個正常的 HTTP 迴應,但瀏覽器會在響應頭中發現 Access-Control-Allow-Origin
字段,便拋出異常。
當 Origin
指定的源在許可範圍,服務器返回的響應頭中會多出幾個頭信息字段:
除了上面圖中的頭信息,通常會有如下三個相關頭信息:
Access-Control-Allow-Origin
該字段是必須的。表示許可範圍的域名,一般有兩種值:請求時 Origin 字段的值或者 *
(星號)表示任意域名。
Access-Control-Allow-Credentials
該字段可選。布爾值,表示是否容許在 CORS 請求之中發送 Cookie
。若不攜帶 Cookie
則不須要設置該字段。
當設置爲 true
則 Cookie
包含在請求中,一塊兒發送給服務器。還須要在 AJAX 請求中開啓 withCredentials
屬性,不然瀏覽器也不會發送 Cookie
。
let xhr = new XMLHttpRequest(); xhr.withCredentials = true;
注意: 若是前端設置 Access-Control-Allow-Credentials
爲 true
來攜帶 Cookie 發起請求,則服務端 Access-Control-Allow-Origin
不能設置爲 *
。
Access-Control-Expose-Headers
該字段可選。能夠設置須要獲取的字段。由於默認 CORS 請求時,XMLHttpRequest
對象的getResponseHeader()
方法只能拿到如下 6 個基本字段:
Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。
非簡單請求狀況如:請求方法是 PUT / DELETE 或者 Content-Type:application/json
類型的請求。
在非簡單請求發出 CORS 請求時,會在正式通訊以前增長一次 「預檢」請求(OPTIONS方法),來詢問服務器,本次請求的域名是否在許可名單中,以及使用哪些頭信息。
當 「預檢」請求 經過之後,纔會正式發起 AJAX 請求,不然報錯。
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... ...
「預檢」請求 信息中包含兩個特殊字段:
Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的 CORS 請求會用到哪些 HTTP 方法,上例是 PUT
。
Access-Control-Request-Headers
指定瀏覽器 CORS 請求額外發送的頭信息字段,上例是 X-Custom-Header
。
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-
開頭的信息,並在控制檯輸出錯誤信息。
跨站請求僞造(英語:Cross-site request forgery),也被稱爲 one-click attack 或者 session riding,一般縮寫爲 CSRF 或者 XSRF, 是一種挾制用戶在當前已登陸的Web應用程序上執行非本意的操做的攻擊方法。跟跨網站腳本(XSS)相比,XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任。
—— 維基百科
核心知識: 跨站點請求僞造請求。
簡單理解: 攻擊者盜用你的身份,以你的名義發送惡意請求。
常見場景:以你名義發送郵件,發消息,盜取你的帳號,甚至於購買商品,虛擬貨幣轉帳等等。
形成影響:我的隱私泄露以及財產安全。
上面描述了 CSRF 攻擊的流程,其中受害者完成兩個步驟:
能夠理解爲:若以上兩個步驟沒有都完成,則不會受到 CSRF 攻擊。
服務端防護的方式有不少,思想相似,都是在客戶端頁面增長僞隨機數。
最簡單有效方式,由於攻擊者理論上沒法獲取第三方的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%的杜絕,這個不是最好的方法。
思路是:每次用戶提交都須要用戶在表單中填寫一個圖片上的隨機字符串,這個方案能夠徹底解決CSRF,但易用性差,而且驗證碼圖片的使用涉及 MHTML 的Bug,可能在某些版本的微軟IE中受影響。
須要注意「並行會話的兼容」。若是用戶在一個站點上同時打開了兩個不一樣的表單,CSRF保護措施不該該影響到他對任何表單的提交。考慮一下若是每次表單被裝入時站點生成一個僞隨機值來覆蓋之前的僞隨機值將會發生什麼狀況:用戶只能成功地提交他最後打開的表單,由於全部其餘的表單都含有非法的僞隨機值。必須當心操做以確保CSRF保護措施不會影響選項卡式的瀏覽或者利用多個瀏覽器窗口瀏覽一個站點。
php 實現以下:
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{ } } ?>
<?php function gen_input() { gen_stoken(); echo "<input type=\"hidden\" name=\"" . FTOKEN_NAME . "\" value=\"" . $_SESSION[STOKEN_NAME] . "\"> "; } ?>
<?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>
這一步很簡單,不作詳細介紹。
注意: 本文簡單介紹 XSS 知識,具體詳細能夠閱讀 FEWY 寫的 《跨站腳本攻擊—XSS》https://segmentfault.com/a/11...。
跨站腳本(英語:Cross-site scripting,一般簡稱爲:XSS)是一種網站應用程序的安全漏洞攻擊,是代碼注入的一種。它容許惡意用戶將代碼注入到網頁上,其餘用戶在觀看網頁時就會受到影響。這類攻擊一般包含了HTML以及用戶端腳本語言。
—— 維基百科
XSS 攻擊,通常是指攻擊者經過在網頁中注入惡意腳本,當用戶瀏覽網頁時,惡意腳本執行,控制用戶瀏覽器行爲的一種攻擊方式。
常見 XSS 危害有:
現今主流瀏覽器(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 中元素的相關性。
即將經常使用特殊字符進行轉義,避免攻擊者使用構造特殊字符來注入腳本。須要在客戶端和服務端,都對用戶輸入的數據進行轉義。
常見須要轉義的特殊字符如 <
,>
,&
,"
,'
。
轉義方法:
function escapeHTML(str) { if (!str) return ''; str = str.replace(/&/g, "&"); str = str..replace(/</g, "<"); str = str..replace(/>/g, ">"); str = str..replace(/"/g, """); str = str..replace(/'/g, "'"); return str; };
常見於富文本內容,由於其須要保留 HTML,因此不能直接使用轉義方法,而能夠經過使用白名單,來容許特定的 HTML 標籤及屬性,來抵禦 XSS 攻擊。
內容安全策略(Content Security Policy,CSP),實質就是白名單制度,開發者明確告訴客戶端,哪些外部資源能夠加載和執行,大大加強了網頁的安全性。
兩種方法能夠啓用 CSP。
Content-Security-Policy
的字段:Content-Security-Policy: script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:
<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.org
和 third-party.org
<frame>
、<iframe>
: 必須使用HTTPS協議加載本文首發在 pingan8787我的博客,如需轉載請聯繫本人。
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推薦 | https://github.com/pingan8787... |
ES小冊 | js.pingan8787.com |