本文要点
Secure针对的风险是中间人攻击
HttpOnly针对的风险是跨站脚本攻击(XSS)
把上面两节合起来看,就能回答标题里的问题

本文目录
先分清两个属性各管哪一段路
如果你在Set-Cookie里看到Secure和HttpOnly,先要弄清它们各自管的是Cookie生命周期里的哪一段。
按MDN的说明,标记为Secure的Cookie只应通过被HTTPS协议加密过的请求发送给服务端,它永远不会使用不安全的HTTP发送(本地主机除外)。这句话管的是“Cookie在网络上怎么走”。
HttpOnly管的是另一件事:JavaScript的Document.cookie API无法访问带有HttpOnly属性的cookie,此类Cookie仅作用于服务器。这句话管的是“Cookie在浏览器里谁能读到”。
所以判断顺序可以这样定:Secure回答“这次发送走的是不是加密通道”,HttpOnly回答“页面里的脚本能不能读到这个值”。两个问题不在同一层,一个属性答不了另一个问题。
适用范围:本文只讲Secure限制Cookie只经HTTPS发送、HttpOnly阻止JavaScript通过document.cookie读取,以及两者各自针对的风险。不讲SameSite取值,不讲Cookie前缀。
一个自建情境(示例,不是对某个真实网站的记录):你在练习目录里维护一个自建页面,用本地或公开的HTTPS地址打开它,在开发者工具的Network面板里查看响应头中的Set-Cookie行。下面所有Cookie名称和值都是练习用的自建数据。
Secure防的是传输途中被截获,不是防脚本读取
Secure针对的风险是中间人攻击。按MDN的说明,Secure意味着中间人攻击者无法轻易访问它。原因是Cookie只在HTTPS加密通道里发送,链路上的人看不到明文。
这里有两个容易误解的地方。
第一,Secure不阻止对cookie中敏感信息的访问。MDN写得很直接:有权访问客户端硬盘的人,或者未设置HttpOnly属性时的JavaScript,都可以读取和修改它。也就是说,加了Secure,脚本照样能读。
第二,不安全的站点无法使用Secure属性设置cookie。MDN说明,在URL中带有http:的站点无法用Secure属性设置cookie。所以Secure不是你想加就能生效的,它要求设置和发送都处在安全来源上。
一个自建示例(练习数据,不是真实站点):在练习页面里让服务器返回下面这一行,然后在Network面板里看它是否被接受。
Set-Cookie: session_id=practice-001; Secure如果这个练习页面是通过http打开的,这条Secure Cookie不会被设置成功。换成HTTPS地址再试,才会被接受。这一步只用来观察Secure对发送通道的要求,不涉及任何真实会话。
HttpOnly防的是XSS脚本偷Cookie,不防传输截获
HttpOnly针对的风险是跨站脚本攻击(XSS)。按MDN的说明,JavaScript可以通过跨站脚本攻击的方式来窃取Cookie,而使用HttpOnly属性可防止通过JavaScript访问cookie值。
这句话的作用范围要看清:HttpOnly只挡住JavaScript这一条读取路径。它不改变Cookie在网络上怎么发送,所以如果通道本身没有加密,HttpOnly帮不上忙。
MDN还给了两个相关事实。第一,通过JavaScript创建的Cookie不能包含HttpOnly标志。也就是说,脚本自己写不出一个HttpOnly Cookie。第二,持久化服务器端会话的Cookie不需要对JavaScript可用,而应具有HttpOnly属性。
一个自建示例(练习数据,不是真实站点):在练习页面里放一段自建脚本,尝试读取document.cookie,观察带HttpOnly和不带HttpOnly的两条练习Cookie分别能不能被读到。
document.cookie = "plain_cookie=practice-a";
console.log(document.cookie);这条通过脚本写入的plain_cookie能被读到。而服务器返回的HttpOnly Cookie不会出现在document.cookie的输出里。这一步只用来观察读取路径的差别,不涉及真实用户数据。
为什么两个属性不能互相替代:一个管通道,一个管读取方
把上面两节合起来看,就能回答标题里的问题。
Secure管的是“这次发送走不走加密通道”,它不限制谁能读到Cookie。所以只加Secure,页面里的脚本仍然可以通过document.cookie拿到它。
HttpOnly管的是“JavaScript能不能读到”,它不限制Cookie在什么通道上发送。所以只加HttpOnly,Cookie仍可能在未加密的HTTP请求里被发送出去,链路上的人仍可能截获。
这就是两者不能互相替代的原因:它们各自堵住一条不同的路径。MDN在“限制访问Cookie”一节里把这两个属性并列,说明它们是两种方法,而不是一种方法的两个参数。
一个自建对照(练习数据,不是真实站点):在练习页面里让服务器返回下面这一行,两个属性同时出现。
Set-Cookie: id=practice-002; Secure; HttpOnly这条练习Cookie同时满足两个条件:只在HTTPS通道发送,且脚本读不到。你可以分别去掉其中一个属性,观察哪一条路径被放开。
在开发者工具里核对这两个属性怎么写、怎么读
核对时不需要额外工具,用浏览器自带的开发者工具就够。
按MDN的说明,要查看Cookie存储,可以在开发者工具中启用存储查看器(Storage Inspector)功能,并在存储树上选中Cookie。
核对顺序建议这样走(以下为编辑整理的观察方法,不是对某个真实站点的记录):
1. 打开练习页面,按F12打开开发者工具,找到存储查看器,在存储树里选中Cookie。
2. 找到你那条练习Cookie,看它的属性栏里有没有Secure、有没有HttpOnly。
3. 切到Network面板,刷新页面,点开请求,看请求头里的Cookie行有没有带上这条练习Cookie。
4. 在控制台里输入document.cookie,看输出里有没有这条练习Cookie的名称。
第3步和第4步回答的是两个不同问题:请求头里有没有它,说的是发送通道;document.cookie里有没有它,说的是脚本能不能读。两个结果要分开记,不要合成一句“这条Cookie安全”。
一个边界:开发者工具里看到的属性是浏览器当前保存的状态。它不告诉你这条Cookie最初是在哪个来源上设置的,MDN也说明服务器无法确认cookie是在安全来源上设置的。
设置时的两条限制和一条常见误判
设置这两个属性时,有几条限制值得单独记。
第一条:Secure要求安全来源。MDN说明,不安全的站点(URL中带http:)无法使用Secure属性设置cookie。所以如果你在本地用http调试,Secure Cookie可能设不上,这不是代码写错了。
第二条:脚本写不出HttpOnly。MDN说明,通过JavaScript创建的Cookie不能包含HttpOnly标志。所以HttpOnly只能由服务器在Set-Cookie响应头里设置。
一条常见误判:把Secure当成“这条Cookie就安全了”。MDN明确写了,Secure不会阻止对cookie中敏感信息的访问。加了Secure,脚本和能访问客户端硬盘的人仍然可以读取和修改它。要同时挡住脚本读取,还得加HttpOnly。
另一条相关提示来自MDN的安全章节:当机器处于不安全环境时,不能通过HTTP Cookie存储、传输敏感信息。这句话说明,属性是缓解手段,不是把敏感信息放进Cookie的理由。
一个自建练习(不是真实站点):在练习目录里准备两个页面,一个用http打开,一个用HTTPS打开,分别让服务器返回带Secure的练习Cookie,观察哪一边被接受。这一步只用来确认Secure对来源的要求。
适用限制:这两个属性各自不覆盖什么
把边界说清楚,能避免把Secure和HttpOnly当成万能开关。
Secure不覆盖的:它不阻止JavaScript读取Cookie,不阻止能访问客户端硬盘的人读取或修改Cookie,也不改变Cookie的作用域。
HttpOnly不覆盖的:它不改变Cookie在什么通道上发送,所以不防传输途中的截获;它也不限制服务器端怎么使用这个Cookie。
两者都不覆盖的:它们不决定Cookie发给哪些主机和路径,那是Domain和Path的事;它们也不决定Cookie在跨站请求里发不发,那是SameSite的事。本文按范围要求不讲SameSite取值,也不讲Cookie前缀。
MDN在安全章节给出的缓解方向里,除了HttpOnly,还提到用于敏感信息的Cookie生存期应较短,并配合SameSite设置。这些属于本文范围之外的内容,这里只说明它们存在,不展开。
一个自建情境(示例,不是对某个真实网站的记录):你在练习目录里维护一个自建页面,只放练习用的Cookie名称和值,不放任何真实会话标识。核对完属性后,把练习Cookie清掉,再继续下一步练习。
观看知识要点视频 18 秒
18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。
参考资料
以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。
请结合原始资料的适用条件阅读。
返回浏览器工具 →