1987WEB视界-分享互联网热点话题和事件

您现在的位置是:首页 > 域名 > 正文

域名

【黑客级方案】跨域向别的域名写入cookie

1987web2023-01-28域名283
一、同源策略

一、同源策略

1995年,Netscape浏览器(Firefox的前身)首次引入同源策略。

两个不同的域名,互相不能访问对方的cookie,即不能用JS的document.cookie这个对象访问。

二、第三方Cookie

如果通过某种方案,A域名往B域名下写入Cookie,这种Cookie称为第三方Cookie。

登录京东之后,在F12里面看到,这个登录操作写入了20多个Cookie,Cookie所属的域名都不是http://jd.com。 这种就是第三方Cookie。

这种跨域写入Cookie的前提是,目标域名愿意、并且主动让京东写入Cookie。换句话说,这些别的域名都是跟京东有合作关系的。它们的目的往往是共享用户信息,一个用户在京东登录之后,其它这些域名都能获取到你的用户信息。

三、为什么要用这种方式共享用户信息?

假如别的域名只是单纯想收集你的用户信息,大可不必在这个环节进行,它们直接在后台,不为人知的地方,自己拷一份数据就行了。

为什么要把这个动作放在浏览器端,让别人F12一眼就能看见呢?

因为,它们要达到这样的效果,根据每个用户的Cookie不同,显示一些个性化的页面内容。典型的有两种应用场景:

  • 精准推送广告

你在百度搜索东西,在百度之外的网站也能看到同样关键字的广告。这里的用户信息有可能是来自第三方Cookie的。

我还以为只有百度和微信知道我在考研, 原来简书也知道我在考研?

  • 无感知登录

所谓无感知登录,是指还没在一个域名进行过任何登录操作,甚至压根都还没访问过一个域名之前,你已经在这个域名处于登录状态了。在京东的例子中,我登录京东之后,它写入的其中一个第三方Cookie是在http://yiyaojd.com域名下的,这是京东大药房的域名。我之前从来没访问过京东大药房,而且我根本都不知道有这么个域名存在,我现在第一次在浏览器中输入https://yiyaojd.com/,我发现我已经处于登录状态了,页面上已经能显示出我的京东用户名,

四、Chrome浏览器路线图

在开发第三方Cookie的过程中(具体代码怎么写后面会讲),我们发现Firefox浏览器能写入第三方Cookie,换成Chrome浏览器就不行了。

这个问题比较复杂, 跟浏览器厂商和版本都有关系。从长远来说,Google计划在2023年年底取消所有的第三方Cookie支持。上面我们看到的那种京东往京东大药房写入Cookie的做法, 可能会在2023年年底更新Chrome浏览器之后失效,到时候会不会有什么替代方案,我们可以关注一下。其实Cookie本身就是一个早已过时的技术,近几年用的都是Local Storage,我觉得干脆把第一方和第三方Cookie全都取消,问题都不大。

目前来说,Chrome浏览器在特定的条件下允许写入第三方Cookie:

  • 目标域名的协议必须是https。即我从A域名往B域名写入Cookie,B域名必须是https开头的。A域名可以是http或者https均可,我试过从http://localhost都能往https写入cookie。
  • Cookie必须携带 SameSite=None; Secure 属性。这是一个固定组合,只有这一种用法,死记即可,别的单用None或者单用Secure都是没有意义的。

Chrome浏览器为什么要规定这两个条件,有https就真的更安全吗?我看也未必。所有的跨域限制都是徒增程序员的负担罢了,黑客真要攻击的话还是有办法,苦的只是我们这些合法调用接口的人。(小区封闭式管理防不住小偷,只是让合法的住户进出更麻烦了~)

由于这个方案需要https,https证书是要花钱买的,一般公司的测试环境没有,所以很可能无法在测试环境测。我这边的做法是在生产环境购买了https证书之后,写了个demo直接放上生产环境验证效果。拿生产环境来测试,能够最准确最真实地发现问题,有效避免false positive,这种模式值得各大公司借鉴,理应得到极大的推广。

(题外话,false positive这个词是今天看尤雨溪的知乎回答学到的。)

五、Vue和Spring Boot代码实现方案

1997年,贝尔实验室的专家提出给HTTP协议增加一个Set-Cookie响应头,从服务端返回给浏览器的响应如果携带Set-Cookie响应头,可以给当前所在的域名写入Cookie。

我们要往B域名写入Cookie,这个响应是从B域名的服务端发出的,B域名要准备好一个接口,让别人去请求,请求了才会有响应。

设B域名的后端用的是Spring Boot,定义一个普通的RequestMapping接口(本方案必须为Get请求),让别人来请求即可。

这里有一个特殊的配置, produces = "application/javascript;charset=UTF-8" ,为什么要这样配置呢?

假如我是用Form表单提交的方式去请求这个接口,每次请求打开一个新的浏览器标签页,这种情况我们请求的就是一个很普通的HTTP接口,直接写一个RequestMapping就行了,不需要指定application/javascript。

而实际的应用场景肯定不是Form表单提交,肯定100%都是Ajax请求,只有Ajax请求能做到用户无感知。既然是Ajax请求,注意我们现在是从A域名往B域名写入Cookie,A域名要去请求B域名提供的接口,这属于Ajax跨域,是请求不了的。所以我们现在的问题是怎样让这个Ajax跨域?

解决Ajax跨域有很多方案,我查了一下感觉都很麻烦,采用CORS的话有预检请求,每个请求都买一送一,额外多一个请求,看着就烦,还有后端写过滤器等等当然也很麻烦。我这里解决跨域采取的是JSONP方案,可能不是唯一的方案,但一定是最简单的,总共也没多少代码。前面之所以指定了 produces = "application/javascript;charset=UTF-8" 这么一个配置,其实这个配置就是JSONP的返回类型,只要是JSONP请求,返回的都是这种类型,我们按规范的话都会写上这一句。而其实这一句不写也是可以的,我之前一直都没写,就是普通的RequestMapping,功能也是正常的,后来有个同事说我这样不符合规范……

采用JSONP还意味着我们这个RequestMapping必须是Get请求,不能是Post请求,所以你看我接收参数都是用@RequestParam来接收的,而不是@RequestBody。

确定了采用JSONP方案,则我们在A域名的前端代码里这样写,

package.json加上JSONP依赖

"dependencies":{"vue-jsonp":"^2.0.0",},

Vue项目入口处(一般是某个js文件)引入JSONP依赖

import{VueJsonp}fromvue-jsonpVue.use(VueJsonp)

业务代码,从A域名请求B域名的接口

letvueInstance=newVue();// JSONP库挂载在Vue实例上letdomain="www.domainb.com";// B域名,你的域名是什么就写什么leturl="https://"+domain+"/controller/crossSiteCookie";// 注意这里一定是https开头的,否则Chrome浏览器不会跨域写入CookievueInstance.$jsonp(url,{targetDomain:"123",// 以下这些都是传的参数,和普通的Rest请求参数没区别,根据你的业务需要来设计即可appId:"123",userId:"haoyu",random:"浩宇"})

这种写法看上去和通常的axios或者ajax请求都不一样,尤其是回调的语法不一样,回调是作为一个参数传进去的。其实它执行的效果跟别的Ajax完全一样,就是发送一个HTTP请求,用户无感知。你把这一段理解为跨域Ajax请求的固定语法即可。

这个请求发到B域名了,B域名Spring Boot Controller写法如下

/*** 跨域写入Cookie**/@RequestMapping(value="/crossSiteCookie",produces="application/javascript;charset=UTF-8")@ResponseBodypublicStringcrossSiteCookie(@RequestParam("userId")StringuserId,// 这些都是普通参数,根据具体业务设计即可@RequestParam("targetDomain")StringtargetDomain,@RequestParam(name="callbackQuery",required=false)StringcallbackQuery,@RequestParam("random")Stringrandom,HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse){addOneCookie(httpServletResponse,"username","haoyu","domainb.com");addOneCookie(httpServletResponse,"username","haoyu","www.domainb.com");returncallbackQuery+"(success)";}/*** 往响应头添加一个Cookie,要传三个参数: Cookie键,Cookie值,Cookie所在域名*/publicstaticvoidaddOneCookie(HttpServletResponsehttpServletResponse,StringcookieKey,StringcookieValue,StringcookieDomain){// Cookie过期时间Calendarcal=Calendar.getInstance();cal.add(Calendar.HOUR,6);// 6小时后过期Datedate=cal.getTime();SimpleDateFormatsdf=newSimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z",Locale.US);StringresponseHeaderString=cookieKey+"="+cookieValue+";";httpServletResponse.addHeader("Set-Cookie",responseHeaderString+"Path=/; Domain="+cookieDomain+"; Expires="+sdf.format(date)+"; SameSite=None; Secure");// Chrome浏览器跨域写Cookie需要带的属性}

此方法的返回值无意义,我只是随便写了一个success,真正写入cookie的操作是在httpServletResponse.addHeader这一句。

执行的效果是,Chrome浏览器会往B域名写入Cookie,整个过程用户无感知,也不会打开新的标签页。用户在完全没有访问过B域名的情况下,已经有了B域名下的cookie。

此外,我还有一个猜想: 就算B域名这个域名的页面根本不存在(只有后端的接口,没有前端页面),我这个方案也会往B域名里写入Cookie。因为从头到尾Chrome浏览器根本没去访问过B域名的页面,假如这个页面不存在,整个流程也是能走完的。

一、绑定域名空间

一般情况下,域名空间上会提供绑定域名空间的服务,如果空间商没有提供,也可以向其资讯具体的绑定方法以及步骤,一般都不会太难

网站域名更换,该如何操作?

部分将域名用作于投资的域名玩家,由于手中所持有的域名数量较多,不会用域名建站,所以很少会涉及到网站域名更换的问题;

  • 下一篇站长网每日播报:Cookie引争议京东启用新域名

    站长网每日播报:Cookie引争议京东启用新域名