跨域考——论浏览器的城门与狗洞
一、引子:这画地为牢的盛世
未庄的城门寅时开,戌时闭,守城的兵丁举着同源策略的令箭,将HTTP请求当细作盘查。这浏览器里的规矩,倒比赵太爷家的门禁还要森严三分。本是同根生的脚本,偏要分出三六九等——协议、域名、端口差上半分,便成异端。
二、同源礼教吃人录
1. XMLHttpRequest 的镣铐
const xhr = new XMLHttpRequest();xhr.open('GET', 'https://邻村账房/api');xhr.send(); // 触发城门卫兵的呵斥
这ajax的马车分明载的是正经货物,却因路引不全被拦在城门外。控制台的报错红字,倒像是衙门口张贴的缉拿告示。
2. Fetch API 的新枷锁
fetch('https://当铺/v1/典当') .then(res => res.json()) .catch(err => console.error('吃了闭门羹'));
虽换了新式的马车,守城的规矩仍是光绪年间的旧制。CORS错误如同未庄茶馆的茶钱——躲不过,绕不开。
三、越狱者的十八般武艺
1. JSONP 的狗洞
function 收赃(赃物) { console.log(赃物); }const script = document.createElement('script');script.src = 'https://黑市?callback=收赃';document.body.appendChild(script); // 钻过城墙排水沟
这旁门左道的手段,活脱脱是阿Q翻墙偷萝卜的做派。虽解得一时之急,却把身家性命全系在回调函数上,比赵家的借据还要危险。
2. CORS 的通行令
// 邻村账房的响应头Access-Control-Allow-Origin: https://未庄钱庄Access-Control-Allow-Methods: GET,PUTAccess-Control-Allow-Headers: Content-Type
像是衙门颁发的通关文牒,盖着大红印章。预检请求(Preflight)的流程,比七品知县升堂还要繁琐:
OPTIONS /api HTTP/1.1Origin: https://未庄钱庄Access-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Secret-Key
3. 代理服务器的掮客
// 自家后院搭桥app.use('/proxy', createProxyMiddleware({ target: 'https://禁地', changeOrigin: true, // 偷梁换柱 pathRewrite: {'^/proxy': ''}}));
这等中间人勾当,像极了未庄的说合人。表面是帮两家牵线,背地里可能把消息篡改得面目全非,比单四嫂子遇到的庸医还可恨。
四、江湖偏方现形记
1. document.domain 的旧桥
// 主站:https://未庄.govdocument.domain = '未庄.gov';
// 子站:https://茶馆.未庄.govdocument.domain = '未庄.gov'; // 强认亲戚
这自欺欺人的把戏,活似赵白眼硬攀赵太爷做本家。且不说只限同主域名,单是这设置后的安全隐患,就比阿Q睡的土谷祠还要漏风。
2. postMessage 的飞鸽传书
// 钱庄窗口iframe.contentWindow.postMessage(密信, 'https://当铺');
// 当铺窗口window.addEventListener('message', (e) => { if (e.origin !== 'https://钱庄') return; console.log(e.data);});
虽比鸿雁传书可靠,却要双方对暗号。稍有不慎便成孔乙己分茴香豆——消息被各路闲汉劫了去。
3. WebSocket 的暗道
const ws = new WebSocket('wss://黑市通道');ws.onmessage = (e) => console.log(e.data); // 绕过城门守卫
这长连接的密道,倒像是革命党用的电报。虽不受同源所限,却要另开线路,维护成本比维持赵家的祠堂还要费银钱。
五、安全与便利的浮世绘
1. CSRF 的借刀杀人
<img src="https://钱庄/转账?to=假洋鬼子&amount=1000">
同源策略防住了明枪,却躲不过暗箭。这CSRF的攻击,活脱是未庄茶馆里的蒙汗药——借你的手,办他的事。
2. CORS 配置的七出之条
Access-Control-Allow-Origin: * // 如同敞开城门迎流寇
为图省事设了星号,好比把赵太爷家的库房钥匙挂在城门上。等到被XSS攻破时,倒比小D偷了宁式床还要狼狈。
六、结语:这破不了的轮回
跨域之争,恰似未庄的改良运动。旧派要守城门,新派要开民智,中间派忙着搭桥钻洞。TypeScript 的类型守卫防得了明处的错,却拦不住暗处的劫。
今人用 GraphQL 扮作新式学堂,用 BFF 充当前哨驿站,终不过是给旧城墙刷层新漆。待到 WebAssembly 大军压境时,这同源的城墙怕是要比未庄的土墙崩塌得更快。
月光照在控制台的报错上,我仿佛看见无数 404 在跳舞。这浏览器的城门啊,既护不住周全,又拦不住人心。呜呼!我说不出话,只听得夜风里传来 fetch 的悲鸣,一声声喊着:
“预检请求… 预检请求…”