環(huán)境:
后臺(tái)使用的python - flask
前臺(tái)使用angular框架
跨域post有多種實(shí)現(xiàn)方式:
1.CORS:http://blog.csdn.net/hfahe/article/details/7730944
2.利用iframe
3.server proxy:https://en.wikipedia.org/wiki/Proxy_server
樣例使用的為iframe,想要證明,在沒有進(jìn)行csrf防御時(shí),隨意攻擊者能夠利用javascript發(fā)送 post 請(qǐng)求。從而簡(jiǎn)單的提交或獲取數(shù)據(jù)資料;
<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 : {}, //提交的參數(shù) delay : 1000, //延遲獲取參數(shù)的時(shí)間。單位為毫秒 onSubmit : function (i) {} //提交成功后的回調(diào)函數(shù),參數(shù)為跳轉(zhuǎn)的IFRAME }; config = $.extend({}, def, config); if (!config.url) { config.onSubmit({error: 'URL is Empty!'}); return; } /****baseMethod****/ /** * 生成隨機(jī)的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; }, /** * 刪除指定的節(jié)點(diǎn) * @param _element 要?jiǎng)h除的節(jié)點(diǎn) */ 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>
這里僅接收POST的請(qǐng)求
@app.route('/test' ,methods=['POST'])def test(): print 'param is :'; print request.form return jsonify(data='2222')
服務(wù)端處理了非站內(nèi)的請(qǐng)求
本地test.html獲取到返回信息
跨站請(qǐng)求攻擊。簡(jiǎn)單地說,是攻擊者通過一些技術(shù)手段欺騙用戶的瀏覽器去訪問一個(gè)自己以前認(rèn)證過的站點(diǎn)并運(yùn)行一些操作(如發(fā)郵件,發(fā)消息,甚至財(cái)產(chǎn)操作如轉(zhuǎn)賬和購(gòu)買商品)。因?yàn)闉g覽器以前認(rèn)證過。所以被訪問的站點(diǎn)會(huì)覺得是真正的用戶操作而去運(yùn)行。
這利用了web中用戶身份驗(yàn)證的一個(gè)漏洞:簡(jiǎn)單的身份驗(yàn)證僅僅能保證請(qǐng)求發(fā)自某個(gè)用戶的瀏覽器,卻不能保證請(qǐng)求本身是用戶自愿發(fā)出的。
你這能夠這么理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發(fā)送惡意請(qǐng)求。CSRF能夠做的事情包含:以你名義發(fā)送郵件。發(fā)消息,盜取你的賬號(hào)。甚至于購(gòu)買商品,虛擬貨幣轉(zhuǎn)賬……造成的問題包含:個(gè)人隱私泄露以及財(cái)產(chǎn)安全。
從上圖能夠看出,要完畢一次CSRF攻擊,受害者必須依次完畢兩個(gè)步驟:
看到這里。你或許會(huì)說:“假設(shè)我不滿足以上兩個(gè)條件中的一個(gè),我就不會(huì)受到CSRF的攻擊”。
是的,確實(shí)如此。但你不能保證下面情況不會(huì)發(fā)生:
1.你不能保證你登錄了一個(gè)站點(diǎn)后,不再打開一個(gè)tab頁(yè)面并訪問另外的站點(diǎn)。2.你不能保證你關(guān)閉瀏覽器了后,你本地的Cookie立馬過期,你上次的會(huì)話已經(jīng)結(jié)束。3.上圖中所謂的攻擊站點(diǎn)??赡苁且粋€(gè)存在其它漏洞的可信任的常常被人訪問的站點(diǎn)。
原理具體:http://www.80sec.com/csrf-securit.html
1.GET類型的CSRF
僅僅須要一個(gè)HTTP請(qǐng)求。就能夠構(gòu)造一次簡(jiǎn)單的CSRF。
樣例:
銀行站點(diǎn)A:它以GET請(qǐng)求來完畢銀行轉(zhuǎn)賬的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危急站點(diǎn)B:它里面有一段HTML的代碼例如以下:
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
首先。你登錄了銀行站點(diǎn)A,然后訪問危急站點(diǎn)B,噢,這時(shí)你會(huì)發(fā)現(xiàn)你的銀行賬戶少了1000塊
為什么會(huì)這樣呢?原因是銀行站點(diǎn)A違反了HTTP規(guī)范,使用GET請(qǐng)求更新資源。
在訪問危急站點(diǎn)B的之前。你已經(jīng)登錄了銀行站點(diǎn)A,而B中的以GET的方式請(qǐng)求第三方資源(這里的第三方就是指銀行站點(diǎn)了,原本這是一個(gè)合法的請(qǐng)求,但這里被不法分子利用了)。所以你的瀏覽器會(huì)帶上你的銀行站點(diǎn)A的Cookie發(fā)出Get請(qǐng)求,去獲取資源http://www.mybank.com/Transfer.php?toBankId=11&money=1000 ,結(jié)果銀行站點(diǎn)服務(wù)器收到請(qǐng)求后,覺得這是一個(gè)更新資源操作(轉(zhuǎn)賬操作),所以就立馬進(jìn)行轉(zhuǎn)賬操作
2.POST類型的CSRF
如上邊的跨域POST樣例
1.提交驗(yàn)證碼
在表單中添加一個(gè)隨機(jī)的數(shù)字或字母驗(yàn)證碼。通過強(qiáng)制用戶和應(yīng)用進(jìn)行交互。來有效地遏制CSRF攻擊。
2.Referer Check
檢查假設(shè)是非正常頁(yè)面過來的請(qǐng)求,則極有可能是CSRF攻擊。
3.token驗(yàn)證
4.在 HTTP 頭中自己定義屬性并驗(yàn)證
這樣的方法也是使用 token 并進(jìn)行驗(yàn)證。這里并非把 token 以參數(shù)的形式置于 HTTP 請(qǐng)求之中,而是把它放到 HTTP 頭中自己定義的屬性里。通過 XMLHttpRequest 這個(gè)類,能夠一次性給全部該類請(qǐng)求加上 csrftoken 這個(gè) HTTP 頭屬性。并把 token 值放入當(dāng)中。這樣攻克了上種方法在請(qǐng)求中添加 token 的不便。同一時(shí)候,通過 XMLHttpRequest 請(qǐng)求的地址不會(huì)被記錄到瀏覽器的地址欄,也不用操心 token 會(huì)透過 Referer 泄露到其它站點(diǎn)中去。
1.引入csrf
from flask_wtf.csrf import CsrfProtectcsrf = CsrfProtect()app = Flask(__name__)csrf.init_app(app)app.config['SECRET_KEY']='myblog'
2.在站內(nèi)頁(yè)面上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.再次測(cè)試跨域post
后臺(tái)輸出:
出現(xiàn)的問題:
服務(wù)端須要限制登陸次數(shù)
解決方法:
client添加登陸間隔。請(qǐng)求一次后,等待x秒才干再次請(qǐng)求
服務(wù)端做cas驗(yàn)證
服務(wù)端須要保存用戶的token,及過期時(shí)間;
解決方法:
能夠?qū)oken 保存在Memcache,數(shù)據(jù)庫(kù)中,redis
當(dāng)然。以上僅僅防君子,不防小人
參考:
跨域post請(qǐng)求:http://blog.csdn.net/doraeimo/article/details/7329779
跨站請(qǐng)求偽造:http://www.jianshu.com/p/7f33f9c7997b
token:http://alvinzhu.me/blog/2014/08/26/10-things-you-should-know-about-tokens/
聯(lián)系客服