环境:
后台使用的python - flask
前台使用angular框架javascript
跨域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
<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>
这里仅接收POST的请求python
@app.route('/test' ,methods=['POST'])
def test():
print 'param is :';
print request.form
return jsonify(data='2222')
服务端处理了非站内的请求jquery
本地test.html获取到返回信息git
跨站请求攻击。简单地说,是攻击者经过一些技术手段欺骗用户的浏览器去訪问一个本身之前认证过的站点并运行一些操做(如发邮件,发消息,甚至財产操做如转帐和购买商品)。由于浏览器之前认证过。因此被訪问的站点会以为是真正的用户操做而去运行。github
这利用了web中用户身份验证的一个漏洞:简单的身份验证仅仅能保证请求发自某个用户的浏览器,却不能保证请求自己是用户自愿发出的。web
你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF可以作的事情包含:以你名义发送邮件。发消息,盗取你的帐号。甚至于购买商品,虚拟货币转帐……形成的问题包含:我的隐私泄露以及財产安全。redis
从上图可以看出,要完毕一次CSRF攻击,受害者必须依次完毕两个步骤:
看到这里。你或许会说:“假设我不知足以上两个条件中的一个,我就不会受到CSRF的攻击”。
是的,确实如此。但你不能保证下面状况不会发生:
1.你不能保证你登陆了一个站点后,再也不打开一个tab页面并訪问另外的站点。 2.你不能保证你关闭浏览器了后,你本地的Cookie立马过时,你上次的会话已经结束。 3.上图中所谓的攻击站点。多是一个存在其它漏洞的可信任的经常被人訪问的站点。
原理具体:http://www.80sec.com/csrf-securit.html
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样例
1.提交验证码
在表单中添加一个随机的数字或字母验证码。经过强制用户和应用进行交互。来有效地遏制CSRF攻击。
2.Referer Check
检查假设是非正常页面过来的请求,则极有多是CSRF攻击。
3.token验证
4.在 HTTP 头中本身定义属性并验证
这样的方法也是使用 token 并进行验证。这里并不是把 token 以參数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中本身定义的属性里。经过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性。并把 token 值放入当中。这样攻克了上种方法在请求中添加 token 的不便。同一时候,经过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用操心 token 会透过 Referer 泄露到其它站点中去。
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
后台输出:
前台输出:
出现的问题:
服务端需要限制登录次数
解决方法:
client添加登录间隔。请求一次后,等待x秒才干再次请求
服务端作cas验证
服务端需要保存用户的token,及过时时间;
解决方法:
可以将token 保存在Memcache,数据库中,redis
固然。以上仅仅防君子,不防小人
參考:
跨域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/