浏览器cookie新的SameSite属性

最近登录时,突然控制台打印了一个新的警告:

某些 Cookie 滥用推荐的“sameSite“属性 
由于 Cookie “_jfinal_captcha”的“sameSite”属性设置为“none”,但缺少“secure”属性,此 Cookie 未来将被拒绝。
若要了解“sameSite“的更多信息,请参阅:https://developer.mozilla.org/docs/Web/HTTP/Cookies

之前没有这样的警告。

搜了一下,是Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

以前,如果SameSite属性没有设置,或者没有得到运行浏览器的支持,那么它的行为等同于None,Cookies会被包含在任何请求中——包括跨站请求。
但是,在新版本的浏览器中,SameSite的默认属性是SameSite=Lax。换句话说,当Cookie没有设置SameSite属性时,将会视作SameSite属性被设置为Lax——这意味着Cookies将会在当前用户使用时被自动发送。如果想要指定Cookies在同站、跨站请求都被发送,那么需要明确指定SameSite为None

可见,是我的火狐浏览器不够“新”,所以默认成了none,所以出了提示。而新版会默认为Lax,也就不会有该提示了。

SameSite可以有下面三种值:
None
浏览器会在同站请求、跨站请求下继续发送cookies,不区分大小写。
Strict
浏览器将只在访问相同站点时发送cookie。(在原有Cookies的限制条件上的加强,如上文“Cookie的作用域” 所述)
Lax
在新版本浏览器中,为默认选项,Same-site cookies 将会为一些跨站子请求保留,如图片加载或者frames的调用,但只有当用户从外部站点导航到URL时才会发送。如link链接


然而,有些用户可能并不能及时升级浏览器版本。

是否会考虑在4.9里面增加cookie默认的SameSite属性?


评论区

JFinal

2020-05-27 11:45

jfinal 4.9 已推送至 maevn 库,所以这版本是加不了了,你的建议我放到下版迭代的需求列表中了,谢谢建议

JFinal

2020-05-27 11:53

我刚大致看了一下,开发者自己应该可以来设置这个值,例如在 Controller 中这么用:
setCookie("SameSite", "Strict");
setCookie("otherKey", "otherValue");

himans

2020-05-27 17:31

好的,我就用setCookie("SameSite", "Lax");解决

JFinal

2020-05-27 17:39

@himans 记得要回来反馈, 实践后的结果极端重要

himans

2020-05-27 20:24

@JFinal
测试:针对LoginController里面的doLogin()方法中的setCookie代码:
setCookie(LoginService.sessionIdName, sessionId, maxAgeInSeconds, true);

注:LoginService.sessionIdName = "jfinalId"

经过测试,无法通过简单调用controller里面的setCookie来设置sameSite属性,这个方法其实是添加了一个新的cookie,其key="sameSite",而不是给key="jfinalId"的这个cookie设置属性。
正确的做法应该是new 一个Cookie cookie,再给cookie设置sameSite,可惜的是,Jfinal中引入的Cookie为javax.servlet.http.Cookie,这个类里面不存在sameSite属性。

研究了文档,采用了2重解决方案。
1、尝试创建了一个BaseCookie继承自Cookie,给BaseCookie增加sameSite属性,然后给新的BaseCookie baseCookie设置setSameSite("Lax"),然后调用response的addCookie(baseCookie)。
结果是,sameSite属性并未加入到响应头的cookie中,研究了代码,发现原因是addCookie方法是安全克隆,过滤掉了没有的属性。
方法1失败。
2、退而求其次,简单一点直接设置secure属性为真。
cookie.setSecure(true),然后addCookie,控制台的这个提示就消失了。
感觉这个方法并不是很安全。
===============
简单结论:
1、可能目前引入的servlet的版本还不支持sameSite,Cookie是否有、或需要新版本,尚未深入研究;
2、Jfinal核心组件CaptchaRender里面直接set了_jfinal_captcha这个cookie,这段代码要从Jfinal层面进行修改,这次没有尝试;
3、在LoginController里面的doLogin()方法中,对Cookie进行setSecure操作可去掉控制台报警,我暂时采用这个方式解决。只是临时解决。
4、上文提到的方法1,可能从Jfinal层面会有比较好的解决。

himans

2020-05-28 21:27

@JFinal 补充一下:Undertow的Cookie里面是有sameSite属性的

JFinal

2020-05-28 21:31

@himans 这个 sameSite 属性是不是一个独立的 cookie ?

还是说这是在原有 cookie 上附加的另一个值?

前者就是这样用的:
setCookie("sameSite", ...)

后者是这样用的:
setCookie("myCookie", "myValue; saveSite=...");

himans

2020-06-01 12:20

@JFinal 是后者,原有cookie上的新属性
哈哈~没想到还可以这样解决~巧妙!...header毕竟文本...

JFinal

2020-06-01 12:22

himans

2020-06-04 16:09

@JFinal
后续跟进

1、setCookie("myCookie", "myValue; saveSite=...");这种方式似乎无效
doLogin代码改成:
setCookie(LoginService.sessionIdName, sessionId + "; sameSite=lax", maxAgeInSeconds, true);
这会导致cookie响应中value多一个引号,同时samesite还是none。

原来,原始头里面的set-cookie是这样的:
jfinalid="d5e7b94296534ef8aad6d16dd2b9cb08 ; sameSite=Lax"; Version=1; Path=/; HttpOnly; Max-Age=604800; Expires=Thu, 11-Jun-2020 07:49:28 GMT

2、没事,继续糊弄浏览器,中间插入双引号分别闭合试试:
setCookie(LoginService.COOKIE_SESSION_NAME, sessionId + "\"; sameSite=\"lax", maxAgeInSeconds, true);

header里面竟然没有转义
Set-Cookie: jfinalid="0c7988af91e04c6fb2d2d6190551591a\" ; sameSite=\"Lax"; Version=1; Path=/; HttpOnly; Max-Age=604800; Expires=Thu, 11-Jun-2020 07:53:56 GMT

3、接着来
setCookie(LoginService.COOKIE_SESSION_NAME, sessionId + "' ; sameSite='Lax", maxAgeInSeconds, true);

Set-Cookie: jfinalid="50798adf661c43648b8090737adc51c9\" ; sameSite=\"Lax"; Version=1; Path=/; HttpOnly; Max-Age=604800; Expires=Thu, 11-Jun-2020 07:58:50 GMT

4、不折腾了,还是自建Cookie,setSecure(true)完事。

5、Login可以手工修改,Jfinal的CaptchaRender里面依然有这个问题。

himans

2020-06-04 16:19

@JFinal
另外,如果之前设置过该cookie的secure值为true,后来改回原代码(不指定sameSite和secure属性,相当于只有sameSite=none)的情况下

浏览器不再传回cookie里面的value值sessionId,会导致登录失败。

JFinal

2020-06-04 16:23

@himans 这个得找个时间好好研究

目前,你可以在项目中进行扩展, CaptchaRender 也可以扩展出 extends MyCaptchaRender来用,然后:
render(new MyCaptchaRender(...))

himans

2020-06-04 16:40

@JFinal 好的。目前对项目没有影响。

只是想找个解决方案,但刚才尝试了其它方式也不行。比如getHeader、setHeader只针对自定义header参数,系统内定的参数,servlet还是进行了相当的防注入措施。

himans

2020-06-05 14:14

备忘:

1、后台语言的支持程度
目前还没有哪个后台语言的 API 支持了 SameSite 属性,比如 php 里的 setcookie 函数,或者 java 里的 java.net.HttpCookie 类,如果你想使用 SameSite,需要使用更底层的 API 直接修改 Set-Cookie 响应头。Node.js 本来就没有专门设置 cookie 的 API,只有通用的 setHeader 方法,不过 Node.js 的框架 Express 已经支持了 SameSite。

2、尝试创建全局Handler对所有response中的cookie抓出来增加samesite属性。
尝试失败。
原因有3:
(1)HttpServletResponse不允许获取cookie,更无法修改cookie内容
(2)HttpServletResponse中,getHeaders只对setHeader(和addHeader)的内容起效,无法获取Set-Cookie内容。因此无法修改。


3、强行不用addCookie方法,用addHeader可行
用addHeader("Set-Cookie", "...")强行设置cookie虽然header里面出现了一个set-Cookie值,且浏览器不再报警。
sameSite属性也正常设置。

... 的内容:
String c = LoginService.COOKIE_SESSION_NAME+"="+sessionId+"; path=/; HttpOnly; Max-Age=604800; Expires=Fri, 12-Jun-2020 05:44:18 GMT; sameSite=Lax";

注意点:addHeader或setHeader里面的value字符串,不论何种类型,不能带任何引号。

有扰了。

himans

2020-06-05 14:16

注意,第3种方法,前提是不能有重复的cookie,若有Controller.setCookie操作要去掉。

JFinal

2020-06-05 14:30