Web安全策略之Secure Cookie
背景
提到cookie想必大家都不陌生吧,用来维护http对话身份的一个属性。提到了身份那就一定涉及到安全问题,如果别人拿到了cookie呢。在web应用中传输主流就是http协议,但是http又是不安全的,所以我们需要给cookie颁发一个属性那就是Secure属性,这样的cookie只能在https协议中传递。
http协议中的cookie
在裸奔的http协议传输过程中cookie是暴漏的,从中间拦截是可以看到cookie的。
https协议中的cookie
如果颁发了了ssl协议,在ssl下传输,cookie则不会暴漏出来。
Secure属性的使用
Secure三种设置方式
- true 只能通过https传递,如果是http场景则cookie不能被设置成功
- false 关闭该属性
- 'auto' 通过请求响应自动设置对应的值,当http与https共存的时候(https系统一般不允许http协议传递,但是http系统是可以允许https协议)一旦设置成true(Secure)后续http将丢失cookie
nodejs生成cookie配置(express)
import session from 'express-session';
app.use(
session({
name: `${process.env.SESSION_NAME}`,
secret: `${process.env.SERVER_SESSION_SECRETKEY}`,
client: sessionRedisClient,
}),
saveUninitialized: false,
resave: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000,
sameSite: 'lax',
},
})
);
app.listen(process.env.PORT || 3000, () => {
console.log('http server');
});
如果在http协议中设置secure属性会有什么效果呢
cookie: {
maxAge: 24 * 60 * 60 * 1000,
sameSite: 'lax',
secure: true, // 新增配置
},
我们会发现session id没有生成,结局就是登陆失败。且其他属性均提示⚠️(如下图)
结论如果设置了secure属性,cookie将不会传输。
我们为了验证secure的有效性,我们可以将node server变成https服务器
var server = https.createServer(
{
key: fs.readFileSync(path.join(__dirname, '../../server.key')),
cert: fs.readFileSync(path.join(__dirname, '../../server.crt')),
},
app
);
server.listen(process.env.PORT || 3000, () => {
console.log('https server');
});
响应头颁发cookie成功了,且有session id(skey).到这里就结束了吗?并没有,因为实际生产环境与本地环境并不一样,所以不妨模拟一下生产环境。
线上环境https,由于线上环境node server还是http协议,所以我们要把node server改成http协议传输,通过Nginx反向代理的形式来访问我们的server。
结构
client => reverse proxy(nginx) => server
Nginx配置
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# HTTPS server
#
server {
listen 9527 ssl;
server_name 192.168.31.43;
ssl_certificate /Users/deqi.han/frontend_project/Auto_Test/server.crt;
ssl_certificate_key /Users/deqi.han/frontend_project/Auto_Test/server.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
root /Users/deqi.han/frontend_project/Auto_Test/build/;
index index.html;
}
location /infpf/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:3000;
}
}
}
再次登陆
我们会发现session id(skey)又丢了!!!没错登陆又失败了,这是为什么?
我们打印一下请求信息
会发现进来的请求是http请求,且ip是node server的ip。没错这里就是原因,当我们设置了secure属性之后,先不说ip来源出问题http协议就是没办法传输的!!!如何解决?
设置代理信任
app.set('trust proxy', 1);
再次登陆
一切又正常了~打印信息
我们会发现请求协议变成了https,ip也是客户端ip了~,cookie又能传输了。
那么这个代理信任到底干了什么?
- req.hostname 的值源自 X-Forwarded-Host 标头中设置的值,该值可以由客户端或代理设置。
- X-Forwarded-Proto 可以由反向代理设置,告诉应用程序是 https 还是 http,甚至是无效名称。 该值由 req.protocol 反映。
- req.ip 和 req.ips 值使用来自 X-Forwarded-For 的地址列表填充。
从设置代理信任前后结果我们也可以看出来
设置前
req.hostname为192.168.31.43(客户端hostname)
req.protocol为http(Server端协议)
req.ip为127.0.0.1(Server端ip地址)
设置后
req.hostname为192.168.31.43(来源于X-Forwarded-Host)nginx获取的是客户端的hostname,这里体现在headers里面
req.protocol为https(来源于X-Forwarded-Proto)nginx获取客户端为https协议传输,这里体现在headers里面
req.ip为192.168.31.43(来源于X-Forwarded-For)nginx获取客户端ip地址,这里体现在headers里面
Secure Cookie传输与这些属性息息相关
最终配置文件
import session from 'express-session';
app.set('trust proxy', 1);
app.use(
session({
name: `${process.env.SESSION_NAME}`,
secret: `${process.env.SERVER_SESSION_SECRETKEY}`,
client: sessionRedisClient,
}),
saveUninitialized: false,
resave: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000,
sameSite: 'lax',
},
})
);
讲到这里我想小伙伴应该了解了该属性如何使用了吧~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?