跨站资源共享和CSRF跨站请求伪造

跨域AJAX

跨域,跨域名访问,如:http://www.c1.com 域名向 http://www.c2.com域名发送请求。
由于浏览器存在同源策略机制,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。
浏览器同源策略并不是对所有的请求均制约:

  • 制约: XmlHttpRequest
  • 不限制: img、iframe、script等具有src属性的标签

JSONP

JSONP(JSONP - JSON with Padding是JSON的一种“使用模式”),利用script标签的src属性(浏览器允许script标签跨域)。
本质为:在HTML中添加script标签,利用src属性发送请求,返回函数名(数据)的字符串。事先要定义函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>

<p>
<input type="button" onclick="Jsonp1();" value='提交'/>
</p>

<p>
<input type="button" onclick="Jsonp2();" value='提交'/>
</p>

<script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function Jsonp1(){
var tag = document.createElement('script');
tag.src = "http://c2.com:8000/test/";
document.head.appendChild(tag);
document.head.removeChild(tag);
}

     function list(data){
         console.log(data)
     }
function Jsonp2(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'GET',
dataType: 'JSONP',
jsonp:'callback', /URL上多出 ?callback=list
jsonpCallback:'list',  /返回数据后执行list函数
// success: function(data, statusText, xmlHttpRequest){
// console.log(data); // 没有jsonp,jsonpCallback参数,执行此函数。
}
})
}

</script>
</body>
</html>

//远程:
//func_name = request.GET.get('callback')
//return HttpResponse('%s("机密数据")' %func_name)

CORS

技术发展,使得浏览器可以支持主动设置从而允许跨域请求,即:跨站资源共享(CORS,Cross-Origin Resource Sharing)。
本质:设置响应头,使得浏览器允许跨域请求。

简单请求 OR 非简单请求

简单请求条件:
1.请求方式:HEAD、GET、POST
2.请求头信息:
  Accept
  Accept-Language
  Content-Language
  Last-Event-ID
  Content-Type 对应的值为三个中的任意一个(application/x-www-form-urlencoded、multipart/form-data、text/plain)
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

简单请求和非简单请求的区别:
简单请求:一次请求
非简单请求:两次请求,在发数据之前会先发一次请求用作“预检”,预检通过后才再发送一次请求用于传输数据。

预检

  • 请求方式: OPTIONS
  • “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
  • 如何“预检”
    => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
    Access-Control-Request-Method
    
    => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
    Access-Control-Request-Headers
    
Django中间件处理CORS响应头
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

from django.utils.deprecation import MiddlewareMixin


class ResponseHeadersMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if request.method == 'POST':
response['Access-Control-Allow-Origin'] = 'http://localhost:8080'
response['Access-Control-Allow-Credentials'] = 'true'

elif request.method == "GET":
response['Access-Control-Allow-Origin'] = '*'

elif request.method == 'OPTIONS':
response['Access-Control-Allow-Origin'] = 'http://localhost:8080'
response['Access-Control-Allow-Headers'] = 'Content-Type'
response['Access-Control-Allow-Credentials'] = 'true'

return response
基于CORS实现AJAX请求
  1. 支持跨域,简单请求。服务器设置响应头:Access-Control-Allow-Origin = ‘域名’ 或 ‘*’
  2. 支持跨域,复杂请求。

    由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
    “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
    “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
    “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age

  3. 跨域获取响应头。
    默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。
  4. 跨域传输cookie。

    在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。
    如果想要发送:
    浏览器端:XMLHttpRequest的withCredentials为true
    服务器端:Access-Control-Allow-Credentials为true
    注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *

CSRF跨站请求伪造

CSRF

CSRF(Cross Site Request Forgery),跨站请求伪造是指攻击者可以在第三方站点制造HTTP请求并以用户在目标站点的登录态发送到目标站点,而目标站点未校验请求来源使第三方成功伪造请求。

方法

Django开始CSRF保护机制,通过验证的方法如下:

  1. form表单中添加csrf_token标签,表单中和cookie中分别含有两个不同的csrf_token值。提交POST请求时,带上csrfmiddlewaretoken的key和对应的值

    1
    2
    3
    4
    5
    6
    7
    8
    <form>
    {% csrf_token %}
    </form>

    $.ajax({

    data:{"csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val();}
    })
  2. ajax发送请求时,设置全局的数据。

    1
    2
    3
    $.ajaxSetup({
    data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
    });
  3. 在AJAX请求上设置token

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function csrfSafeMethod(method) {
    // HTTP请求方式是GET/HEAD/OPTIONS/TRACE时不发送CSRFtoken
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
    beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
    xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
    }
    });

更多关于Django的CSRF保护机制请查看:https://docs.djangoproject.com/en/1.11/ref/csrf/