浏览器会标出请求来自本站还是外站,服务端怎么据此挡住跨站请求

📅 2026-10-12 👁️ 0

浏览器会标出请求来自本站还是外站,服务端据此挡住跨站请求伪造,靠的是几个前端改不了的来源标记请求头。规范给这组头留了四个位置,取值由浏览器在发送请求之前自动填好。网页脚本既不能新造同名字段,也不能改动已有内容,服务端因此能把它当作相对可信的信号,用来判断请求是从自己页面发出的,还是被别的站点借走登录态带过来的。

这组请求头是谁加上的?

这组头在规范里叫 fetch metadata 请求头,也就是拉取元数据请求头,浏览器在真正把请求发出去之前替你写好。四个成员分别是 Sec-Fetch-Site(站点关系)、Sec-Fetch-Mode(发起模式)、Sec-Fetch-User(是否用户发起)、Sec-Fetch-Dest(目标类型)。规范把它们归进禁止请求头那一类,意思是网页不能自定义同名头,脚本伪造一份来源标记这条路被堵住了。

还有个容易被忽略的边界是可信地址:浏览器只在判定为可信的请求里附带这组头,跑在明文地址上的站点收不到它们。所以服务端手上的信号其实有两种状态,一种是明确写着跨站,另一种是压根没有这组头,两种要分开处理,不能把缺失直接当成放行理由。

四个取值分别说明什么?

Sec-Fetch-Site 给出的是发起方与接收方之间的关系,分成同源、同站、跨站、无发起方四种,含义与处置见下表。同源要求协议、主机、端口三者完全一致,同站则是主机名相同或互为子域。

取值 表示的关系 典型场景 服务端常见处置
same-origin(同源) 协议、主机、端口完全一致 本站页面里发起的接口调用 按正常业务逻辑放行
same-site(同站) 主机名相同或互为子域 主站页面调用接口子域 读写分开,按接口性质放宽
cross-site(跨站) 双方没有站点关系 外部站点带着登录态发来的请求 写操作直接拒绝
none(无发起方) 看不出来源页面 地址栏直接访问、书签打开 只放浏览器导航类请求

其余三个头回答的是别的侧面。Sec-Fetch-Mode 说明请求以什么模式发出,是页面导航、嵌入资源还是接口调用;Sec-Fetch-Dest 说明这次请求想落到什么类型的目标上,是文档、脚本还是取数据;Sec-Fetch-User 只在少数由用户动作直接触发的请求里出现,用来说明这是人主动点出来的一次跳转。这几项组合起来,才分得清本站的接口调用和外部页面的自动提交。

服务端怎么写按上下文的放行策略?

可行的写法是给每一类接口声明它接受哪些上下文。带登录态的写接口通常只接受同源,同站的读请求按业务需要再放宽,看到跨站标记的写请求直接拒绝,不必再绕去校验令牌,因为来源关系本身已经不成立。头缺失要按接口性质单独定档:能在浏览器里直接打开的页面可以接受缺失,纯接口路径宁可拒绝,免得把没有信号读成有利信号。

这组头还有个容易漏掉的影响,就是缓存。放行既然按上下文判定,同一个地址在不同上下文下就可能得到不同结果,缓存也必须区分这些上下文,否则被拒绝的响应可能被复用到正常请求上,反过来也一样。稳妥的做法是让按上下文放行的接口把来源信息带进缓存键,或者干脆把这类接口排除在共享缓存之外。

它和写操作确认是互补不是替代?

这套标记回答的是谁发起了这次请求,不回答发起者有没有权限做这件事。它能挡住别人借登录态悄悄发一次写操作,挡不住合法用户在自己页面上点了不该点的按钮,所以它是补一道面,不能替代写之前的确认。

站内的 AI 接口侧(MCP)对写操作有回合级审批,会把一批改动合并成一次确认,人在这一轮点头之后改动才落地。两层的对象并不重叠:来源标记管的是请求是谁发起的,回合级审批管的是这次改动要不要人点头。前者在服务端边界上把外部来源筛掉,后者在智能体这一侧把批量变更收拢到同一个确认点,谁也替不了谁。

常见问题

这组头能挡住跨站请求伪造的全部情形吗? 不能。它只处理能被浏览器判定出关系的那部分,明文地址上压根收不到这组头,同站子域之间要靠别的约束。权限判断和写操作前的确认,仍然要一起在业务层里做完。

请求里没有这组头该怎么判? 先看服务地址是不是可信的,明文地址下浏览器不会发送。由浏览器之外的客户端直连时,这组头天然缺失,只能退回原有的鉴权与确认机制,把缺失当成信号不可用,而不是当成通过。

带跨站标记的请求就一定有问题吗? 不一定。第三方嵌入、合作方页面跳过来、外部链接引起的正常导航都带跨站标记。稳妥的用法是只把它用在写操作的拒绝上,读请求和导航按模式与目标类型再判。

相关文章

用户退出登录后浏览器里还留着旧数据,响应头怎么要求清除

退出登录时浏览器里留着的缓存和本地存储不会自动消失。Clear-Site-Data 响应头是给客户端的清除信号,可按项清缓存、凭据、存储与执行上下文,且只在加密连接里生效:凭据清到整个注册域,存储只清当前来源。站内登录态由 JWT 承载,客户端这一层本地数据要靠它配合收尾,公共设备上尤其值得加。

2026-10-12

页面里嵌第三方视频,iframe 的能力授权按什么放行

页面里嵌第三方视频涉及两层限制:来源域名按白名单放行,摄像头、定位、自动播放这些能力按最小集逐项授权。PbootCMS 在 V3.2.18 的更新记录里对可信视频域名保留默认来源策略,并对 allow 属性做了 token 白名单。本文解释内嵌框架为什么同时带着脚本与媒体能力、精确域与子域通配怎么分,以及放行与授权为什么要分开配。

2026-10-12

网站挂在代理后面,登录锁定和黑名单为什么会认错来源地址

网站挂在反向代理后面时,程序读到的连接来源往往是代理地址,而转发头里的内容又由客户端可控,把两层混为一谈会让登录锁定和黑名单认错对象。PbootCMS 在 V3.2.25 的更新记录里修过无条件信任来源转发头的问题。本文讲两层地址各自的来历、只在可信链路里取最右可信值的做法,以及限速与锁定该用哪一层。

2026-10-12

后台留言批量删除接口按参数拼接,删除范围该怎么校验

后台留言的批量删除如果直接把前端传来的编号列表拼进删除条件,一次请求就可能删掉范围外的内容。PbootCMS 在 V3.2.18 的更新记录里修过这一类参数注入,做法是严格校验要删除的 id 列表。本文说明批量接口为什么把影响面放大、编号该按类型和存在性怎么验、删除范围为什么要服务端重新算一遍,以及误删之后回收站能兜住什么。

2026-10-12

内容没变时服务器只回一个状态码,条件请求靠哪个标识判断

缓存过期不代表要重新下载整份内容。ETag 是某一版资源的标识,资源内容变了就必须生成新值;客户端回访时用 If-None-Match 把旧值带回,服务端比对一致就只回一个不带正文的状态码。本文讲这个标识代表什么、强校验与弱校验的差别、弱标识为什么会影响分段请求,以及内容频繁改动的站点怎么用它减少重复传输。

2026-10-12

连接还没握完就收到请求,服务器为什么宁可返回一个过早状态码

TLS 的早期数据让客户端在第一个往返里就把请求发出去,省下一次握手等待,代价是这类请求存在被重放的风险。425 Too Early 表示服务器不愿冒险处理一个可能被重放的请求,宁可先拒。本文讲早期数据省的是什么、风险落在哪一类请求上、幂等与非幂等为什么要分开对待,以及以 Go 语言为技术栈的服务在这一层要做什么取舍。

2026-10-12

自己的图片被别的网站直接引用时,靠哪个响应头决定放不放行

自己的图片被别的网站直接引用时,决定放不放行的是资源方而不是引用方。Cross-Origin-Resource-Policy 响应头由被引用的资源设置,声明哪些来源可以加载它,取值分同源、同站、跨来源三档,默认挡住的是不带 cors 的跨站请求。本文讲三档各放行到哪一层、它与发起方的隔离头不是同一端,以及图片、字体各自要不要放开。

2026-10-12

服务器默认把程序版本写进响应头和错误页,要不要收掉这层指纹

多数 Web 服务器默认把程序版本写进 Server 响应头和错误页,Nginx 的 server_tokens 指令默认就是 on,关到 off 之后两处都不再带版本信息。收掉这层指纹能减少按版本撞库的便利,但它只是减少信息暴露,不构成防线。本文讲版本号从哪两处漏出去、关掉后还剩什么,以及宝塔、Docker 等不同部署方式各要在哪一层改。

2026-10-12