跨域post 及 使用token防止csrf 攻擊

環境:
後臺使用的python - flask
前臺使用angular框架javascript

1.一個跨域post的樣例:

跨域post有多種實現方式:php

1.CORS:http://blog.csdn.net/hfahe/article/details/7730944
2.利用iframe
3.server proxy:https://en.wikipedia.org/wiki/Proxy_serverhtml

樣例使用的爲iframe,想要證實,在沒有進行csrf防護時,隨意攻擊者可以利用javascript發送 post 請求。從而簡單的提交或獲取數據資料;java

1.本地test.html頁面

<html>
<head>
<title>POST</title>
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
</head>
<body>
<input type="button" onclick="test();" value="test"/>
<script type="text/javascript"> function test() { crossDomainPost({ url: 'http://localhost:5000/test', param: {a: '1', b: '2'}, onSubmit: function (e) { console.log(e); } }) } function crossDomainPost(config) { var def = { url : '', //提交的地址 param : {}, //提交的參數 delay : 1000, //延遲獲取參數的時間。單位爲毫秒 onSubmit : function (i) {} //提交成功後的回調函數,參數爲跳轉的IFRAME }; config = $.extend({}, def, config); if (!config.url) { config.onSubmit({error: 'URL is Empty!'}); return; } /****baseMethod****/ /** * 生成隨機的10位字符,且惟一 * @returns {string} */ var createGuid = function () { var guid = ""; for (var i = 1; i <= 10; i++) { guid += Math.floor(Math.random() * 16.0).toString(16); } return guid; }, /** * 刪除指定的節點 * @param _element 要刪除的節點 */ removeElement = function (_element) { var _parentElement = _element.parentNode; if (_parentElement) { _parentElement.removeChild(_element); } } // Add the iframe with a unique name var iframe = document.createElement("iframe"); var uniqueString = createGuid(); document.body.appendChild(iframe); iframe.style.display = "none"; iframe.contentWindow.name = uniqueString; // construct a form with hidden inputs, targeting the iframe var form = document.createElement("form"); form.target = uniqueString; form.action = config.url; form.method = "POST"; // repeat for each parameter for (var item in config.param) { var input = document.createElement("input"); input.type = "hidden"; input.name = item; input.value = config.param[ item ]; form.appendChild(input); } document.body.appendChild(form); try{ form.submit(); }catch(e){ console.log('error'); consoel.log(e); } setTimeout(function () { config.onSubmit(iframe); removeElement(form); //移除form }, config.delay); } </script>
</body>
</html>

2.後臺接收代碼

這裏僅接收POST的請求python

@app.route('/test' ,methods=['POST'])
def test():
    print 'param is :';
    print request.form
    return jsonify(data='2222')

3.控制檯輸出:

服務端處理了非站內的請求jquery

這裏寫圖片描寫敘述

4.瀏覽器輸出:

本地test.html獲取到返回信息git

這裏寫圖片描寫敘述


2.CSRF

跨站請求攻擊。簡單地說,是攻擊者經過一些技術手段欺騙用戶的瀏覽器去訪問一個本身之前認證過的站點並運行一些操做(如發郵件,發消息,甚至財產操做如轉帳和購買商品)。由於瀏覽器之前認證過。因此被訪問的站點會以爲是真正的用戶操做而去運行。github

這利用了web中用戶身份驗證的一個漏洞:簡單的身份驗證僅僅能保證請求發自某個用戶的瀏覽器,卻不能保證請求自己是用戶自願發出的。web

你這可以這麼理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發送惡意請求。CSRF可以作的事情包含:以你名義發送郵件。發消息,盜取你的帳號。甚至於購買商品,虛擬貨幣轉帳……形成的問題包含:我的隱私泄露以及財產安全。redis

1.原理

這裏寫圖片描寫敘述

從上圖可以看出,要完畢一次CSRF攻擊,受害者必須依次完畢兩個步驟:

  • 登陸受信任站點A,並在本地生成Cookie。
  • 在不登出A的狀況下,訪問危急站點B。

看到這裏。你或許會說:「假設我不知足以上兩個條件中的一個,我就不會受到CSRF的攻擊」。

是的,確實如此。但你不能保證下面狀況不會發生:

1.你不能保證你登陸了一個站點後,再也不打開一個tab頁面並訪問另外的站點。
2.你不能保證你關閉瀏覽器了後,你本地的Cookie立馬過時,你上次的會話已經結束。
3.上圖中所謂的攻擊站點。多是一個存在其它漏洞的可信任的經常被人訪問的站點。

原理具體:http://www.80sec.com/csrf-securit.html

2.常見的攻擊類型:

1.GET類型的CSRF
僅僅需要一個HTTP請求。就可以構造一次簡單的CSRF。
樣例:
銀行站點A:它以GET請求來完畢銀行轉帳的操做,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危急站點B:它裏面有一段HTML的代碼例如如下:

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

首先。你登陸了銀行站點A,而後訪問危急站點B,噢,這時你會發現你的銀行帳戶少了1000塊

爲何會這樣呢?緣由是銀行站點A違反了HTTP規範,使用GET請求更新資源。

在訪問危急站點B的以前。你已經登陸了銀行站點A,而B中的以GET的方式請求第三方資源(這裏的第三方就是指銀行站點了,本來這是一個合法的請求,但這裏被不法分子利用了)。因此你的瀏覽器會帶上你的銀行站點A的Cookie發出Get請求,去獲取資源http://www.mybank.com/Transfer.php?toBankId=11&money=1000 ,結果銀行站點服務器收到請求後,以爲這是一個更新資源操做(轉帳操做),因此就立馬進行轉帳操做

2.POST類型的CSRF
如上邊的跨域POST樣例

3.怎樣防護CSRF

1.提交驗證碼
在表單中添加一個隨機的數字或字母驗證碼。經過強制用戶和應用進行交互。來有效地遏制CSRF攻擊。

2.Referer Check
檢查假設是非正常頁面過來的請求,則極有多是CSRF攻擊。

3.token驗證

  • 在 HTTP 請求中以參數的形式添加一個隨機產生的 token,並在服務器端創建一個攔截器來驗證這個 token,假設請求中沒有
    token 或者 token 內容不對,則以爲多是 CSRF 攻擊而拒絕該請求。

  • token需要足夠隨機
  • 敏感的操做應該使用POST。而不是GET,以form表單的形式提交。可以避免token泄露。

4.在 HTTP 頭中本身定義屬性並驗證
這樣的方法也是使用 token 並進行驗證。這裏並不是把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中本身定義的屬性裏。經過 XMLHttpRequest 這個類,可以一次性給所有該類請求加上 csrftoken 這個 HTTP 頭屬性。並把 token 值放入當中。這樣攻克了上種方法在請求中添加 token 的不便。同一時候,經過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用操心 token 會透過 Referer 泄露到其它站點中去。


4.關於token

  1. Token 應該被保存起來(放到 local / session stograge 或者 cookies
  2. Tokens 除了像 cookie 同樣有有效期。並且你可以有不少其它的操做方法。一旦 token 過時,僅僅需要又一次獲取一個。你可以使用一個接口去刷新 token。你甚至可以把 token 原來的公佈時間也保存起來。並且強制在兩星期後又一次登陸什麼的。假設你需要撤回 tokens(當 token 的生存期比較長的時候這很是有必要)那麼你需要一個 token 的生成管理器去做檢查。
  3. Local / session storage 不會跨域工做,請使用一個標記 cookie
  4. 有需要的話,要加密並且簽名 token
  5. 將 JSON Web Tokens 應用到 OAuth 2

使用Token

1.引入csrf

from flask_wtf.csrf import CsrfProtect
csrf = CsrfProtect()

app = Flask(__name__)

csrf.init_app(app)
app.config['SECRET_KEY']='myblog'

2.在站內頁面上head中,添加token

<meta name="csrf-token" content="{{ csrf_token() }}">

3.配置angular提交表頭

app.config(function ($httpProvider) {
    $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
});

4.再次測試跨域post
後臺輸出:
這裏寫圖片描寫敘述
前臺輸出:
這裏寫圖片描寫敘述


關於webapp跨域Post使用token思路:

  1. 移動端登陸時,服務端驗證表單信息。登錄成功,生成token,返回給client;
  2. client將token存在localstorage/sessionstorage中。每次提交表單。都需要攜帶token;
  3. 服務端獲取請求。假設沒有token,則忽略請求;

出現的問題:

  1. 服務端需要限制登錄次數
    解決方法:
    client添加登錄間隔。請求一次後,等待x秒才幹再次請求
    服務端作cas驗證

  2. 服務端需要保存用戶的token,及過時時間;
    解決方法:
    可以將token 保存在Memcache,數據庫中,redis

  3. client存token時。需要對token加密
    解決方法:
    在存儲的時候把token進行對稱加密存儲,用時解開
    將請求URL、時間戳、token三者進行合併加鹽簽名。服務端校驗有效性

固然。以上僅僅防君子,不防小人

參考:
跨域post請求:http://blog.csdn.net/doraeimo/article/details/7329779
跨站請求僞造:http://www.jianshu.com/p/7f33f9c7997b
token:http://alvinzhu.me/blog/2014/08/26/10-things-you-should-know-about-tokens/

相關文章
相關標籤/搜索