跨域

跨域

同源策略
浏览器故意设计的一个功能限制
CORS
突破浏览器限制的一个方法
JSONP
IE 时代的妥协

同源定义

window.origin 或 location.origin 可以得到当前原
源 = 协议 + 域名 + 端口号
如果两个url的
协议
域名
端口号
完全一致,那么这两个 url 就是同源的
举例
https://qq.comhttps://www.baidu.com 不同源
https://baidu.comhttps://www.baidu.com 不同源
完全一致才算同源

同源策略定义

不同源的页面之间,不准互相访问数据

浏览器规定

如果 JS 运行在 源A里,那么就只能获取源 A 的数据
不能获取 源 B 的数据,即不允许跨域
举例
假设 lucas.com/index.html 引用了 cdn.com/1.js
那么就是说 1.js 运行在源 lucas.com 里
这跟cdn.com 没有关系,虽然 1.js 从那下载
所以 1.js 就只能获取 lucas.com 的数据
不能获取 1.lucas.com 或者 qq.com 的数据
运行在哪个域名里,就只能访问这个域名的数据
这是浏览器功能
为了保护用户隐私

如果没有同源策略

以 QQ 空间为例

源为 https://user.qzone.qq.com
假设,当前用户已经登录(用 Cookie)
假设,AJAX 请求 /friends.json 可获取用户好友列表
到目前为止都很正常
黑客来了
假设好基友(黑客伪装)分享了一个链接给你(https://qzone-qq.com)(钓鱼网站)
当你点开这个网页时,这个网页也请求你的好友列表
https://user.qzone.qq.com/friends.json
黑客成功获取好友列表

问题的根源

无法区分发送者

QQ空间页面里的 JS 和黑客网页里的 JS
法的请求几乎没有区别(referer 有区别)
如果后台开发者没有检查 referer,那么就完全没区别
所以如果没有同源策略,任何页面都能偷 QQ空间的数据
甚至支付宝的余额
安全原则:安全链条的强度取决于最弱的一环
万一这个网站的后端开发工程师忘了检查referer
所以浏览器应该主动预防这种偷数据的行为
总之,浏览器为了用户隐私,设置了严格的同源策略

事件模拟

步骤

创建目录
qq-com 里面有一个 server.js,用来模拟 QQ空间
lucas.com 里面也有一个 server.js,用来模拟黑客网站
qq-com
/index.html 是首页
/qq.js 是 JS 脚本文件
/friends.json 是模拟的好友数据
端口监听为 8800,访问 http://127.0.0.1:8800
lucas-com
/index.html 是首页
/lucas.js 是 JS 脚本文件
端口监听为9900,访问 http://127.0.0.1:8800
完成以上步骤就可以测出端口号不同无法互相访问

hosts

设置本地域名映射
让 qq.com 映射到 127.0.0.1
就可以访问 http://qq.com:8800/index.html
让 lucas.com 映射到 127.0.0.1
就可以访问 http://lucas.com:9900/index.html
如何设置hosts
修改时需要用管理员权限操作

跨域 AJAX

正常使用 AJAX
在 qq.com:8800 里运行的 JS 可以访问 /friends.json
黑客偷数据
请求发送成了,因为 qq.com 后台有 log,访问状态也是200
但黑客拿不到响应,浏览器不给数据给黑客
在 lucas.com:9900 里运行的 JS 不能访问 qq.com:8800/里的数据
浏览器需要 CORS 才能访问

!可以跨域使用 CSS、JS 和图片等

同源策略限制的是数据的访问,引用 CSS、JS 和图片的时候,其实并不知道其内容,只是在引用

如何跨域

方法一:CORS

问题根源

浏览器默认不同源之间不能互相访问数据
但是 自己的网站之间有时候是需要互相访问的,但还是被浏览器阻止了

用CORS(跨域资源共享)

如果需要共享网站数据,需要提前声明
共享网站响应头写上前来拿数据的网站就行

1
response.setHeader('Access-Control-Allow-Origin','http://lucas.com')

方法二:JSONP

定义

JSONP 和 JSONP 没什么关系
当没有CORS时,怎么跨域 (兼容 IE)
虽然不能访问 .json
但是可以引用 .js
只要让 JS 包含数据就可以了

优点:

  1. 它不像 Ajax 请求那样受到同源策略的限制, JSONP 可以跨越同源策略;
  2. 兼容 IE 可以在更加古老的浏览器运行

缺点:

  1. 没有 AJAX 精确,没有状态码,也不知道响应头是什么,只知道成功失败
  2. 由于是 script 标签,所以只能发 get 请求 ,不支持 post 请求

步骤

lucas.com 访问 qq.com
qq.com 将数据写到 /friends.js
lucas.com 用 script 标签引用 /friends.js
/friends.js 执行事先定义好的 window.xxx 函数
/friends.js 执行window.xxx({friends:[…]})
然后 lucas.com 就通过 window.xxx 获取到数据了
window.xxx 就是一个回调

1
2
3
4
5
6
7
8
9
if(request.headers['referer'].indexOf('http://lucas.com:9999/')===0){
const string = "window['{{random}}']({{data}})"
const data = fs.readFileSync('./public/friends.json').toString()
const string2 = string.replace('{{data}}',data).replace('{{random}}',query.callback)
response.write(string2)
}else{
response.statusCode = 404
}
response.end()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function jsonp(url,callbackName) {
return new Promise((resolve, reject) => {
const random = (callbackName||'lucasJSONPCallbackName') + Math.random()
window[random] = (data) => {
resolve(data)
}
const script = document.createElement('script')
script.src = `${url}?callback=${random}`
document.body.appendChild(script)
script.onload = () => {
script.remove()
}
script.onerror=()=>{
reject()
}
})
}
getByJSONP.onclick = () => {
jsonp('http://qq.com:8888/friends.js').then(resolve=>{
console.log(resolve);
},console.log)
// ajax('Get','http://qq.com:8888/friends.js').then(console.log,console.log)
}

跨域
http://blog.climbed.online/2023/11/24/Web -- Knowledge is infinite/前端/JavaScript/跨域/
作者
Z.K.
发布于
2023年11月24日
许可协议