Chrome 152 升级后本地请求全挂了?是 LNA 策略在搞事

92 阅读7分钟

Chrome 152 本地请求全挂了,真凶是 LNA

客户那边突然不能用了

某天客户那边传来消息:终端信息页面一片空白。我们的系统页面是个 HTTP 站点,浏览器里通过 fetch 调本地插件服务 localhost:xxx 拿终端设备信息,一直跑得好好的。

开发机 Chrome 151,没问题。让客户打开开发者工具一看——控制台全是跨网错误,请求根本发不出去。问了下版本,Chrome 152。

你可能也遇到过类似的事。Google 从 Chrome 138 开始逐步收紧一条安全策略,到 152 版本一脚油门把之前的临时方案全堵死了。这篇讲清楚到底发生了什么,以及怎么从根上解决。

浏览器为什么突然管这么宽

浏览器默认不信任外部网站。一个网页请求公网 API 没问题,但如果想请求你电脑上跑的本地服务(比如 localhost:xxx)或者局域网设备(比如 192.168.x.x),安全上是个灰区——万一是恶意网页探测你的内网设备呢?

以前 Chrome 对这类请求基本放行。现在它装了一道门禁:Local Network Access(LNA) 策略。就像外卖员想进小区得刷门禁,Chrome 152 之后,每个网页想调你本地服务也得先过一道闸。

我们的插件就卡在这扇门禁上:系统页面是 HTTP,调用本地服务本来是合法需求,但 Chrome 152 之后不让了。

Chrome 版本演进:从宽松到彻底封堵

近两年 Google 分阶段收紧 Local Network Access(LNA)权限机制:

在这里插入图片描述

我们自己的开发机还在 flag=Default 的状态,151 版本上 Default 确实等于关闭 LNA。但到了 152,Google 把这个"后门"删了——Default 现在的意思是"跟随灰度策略",也就是大概率会被拦截。

在这里插入图片描述

旧方法为什么彻底废了

之前的临时解法很简单:让用户打开 chrome://flags,把 Local Network Access Restrictions Temporary Opt Out 设为 Enabled,重启浏览器。

这条路到 152 彻底走不通了。一是 flag 本身被移除了,TemporaryOptOut 这个开关在 v152 里已经从 chrome://flags 页面消失,想手动改也没得改。二是就算 flag 还在,也没法用代码自动设置——浏览器的 flag 配置存在内部的 Local State 文件里,Chrome 没有暴露任何官方 API,硬写注册表或 JSON 文件极不可靠,Chrome 运行时会覆盖、可能损坏配置。三是 flag 本身就是临时方案,Google 的公开文档里早就说了,flag 是测试用的,未来版本会逐步清理。

核心解法:Chrome 企业策略

既然改不了 flags,那就得找一个比 flags 优先级更高的机制来覆盖它——这就是企业策略。

Chrome 有三层配置机制,优先级从高到低:

在这里插入图片描述

企业策略本来是给公司 IT 管理员批量管控员工浏览器用的——比如强制开启安全浏览、禁止安装某些扩展之类的。但它有几个关键特性对我们来说特别有用:

  • 最高优先级,写入后直接覆盖浏览器默认行为,不需要改 chrome://flags
  • 有官方支持,Chrome 启动时自动从系统级位置读取,不依赖浏览器自己的配置文件
  • 跨平台一致,Windows 走注册表、Linux 读 JSON 文件、macOS 读 plist

我们要做的就是安装插件的时候,顺便把这组企业策略写进去,放行所有 URL 对本地网络的访问。

五个策略,两套场景

LNA 机制只对 HTTPS 页面生效(安全上下文要求),但我们的系统页面是 HTTP。所以需要两套策略同时生效:

新 LNA 策略(Chrome 142+,HTTPS 场景)

策略名值作用
LocalNetworkAccessAllowedForUrls["*"]预授权所有域名的 LNA 权限,用户不会看到弹窗
LocalNetworkAccessPermissionsPolicyDefaultEnabled1默认自动授予 LNA 权限

旧 PNA 策略(HTTP 场景 —— 我们的关键)

策略名值作用
InsecurePrivateNetworkRequestsAllowedForUrls["*"]允许 HTTP 页面访问本地网络和环回地址
InsecurePrivateNetworkRequestsAllowed1全局兜底,允许所有 HTTP 来源访问

为什么 HTTP 页面走不到 LNA? LNA 要求安全上下文(HTTPS)才能触发权限流程,HTTP 页面直接被旧的 Private Network Access 机制拦截。所以必须同时写入 PNA 策略,才能覆盖 HTTP 来源。

此外再加一个 LocalNetworkAccessRestrictionsTemporaryOptOut = 1,对还在 v152 以下的旧版本 Chrome 有效,双保险。

三种平台怎么写

Windows:注册表

Chrome 从这个路径读策略:

HKLM\SOFTWARE\Policies\Google\Chrome

Edge 同理(内核一样):

HKLM\SOFTWARE\Policies\Microsoft\Edge

注册表结构大概是这样:

LocalNetworkAccessRestrictionsTemporaryOptOut = 1  (DWORD)
InsecurePrivateNetworkRequestsAllowed = 1           (DWORD)
LocalNetworkAccessPermissionsPolicyDefaultEnabled = 1 (DWORD)
LocalNetworkAccessAllowedForUrls\
  └── 1 = "*"                                       (String)
InsecurePrivateNetworkRequestsAllowedForUrls\
    └── 1 = "*"                                     (String)

注意是 HKLM 不是 HKCU,写入 Local Machine 需要管理员权限——这个好办,安装脚本本身就以管理员身份跑。

Linux:JSON 文件

Chrome 在 Linux 上启动时,会自动扫描 /etc/opt/chrome/policies/managed/ 目录下的所有 .json 文件,把内容加载为企业策略。Edge 则是 /etc/opt/edge/policies/managed/。

所以我们的做法很直接:安装插件时,往这个目录写一个 JSON 文件,把五个策略写进去。文件名自己定,比如 allow-local-network.json,后缀是 .json 就行:

/etc/opt/chrome/policies/managed/allow-local-network.json

JSON 内容就是那五个策略:

{
  "LocalNetworkAccessAllowedForUrls": ["*"],
  "InsecurePrivateNetworkRequestsAllowedForUrls": ["*"],
  "InsecurePrivateNetworkRequestsAllowed": 1,
  "LocalNetworkAccessRestrictionsTemporaryOptOut": 1,
  "LocalNetworkAccessPermissionsPolicyDefaultEnabled": 1
}

写入 /etc 需要 root 权限。

macOS:plist 文件

macOS 的 Chrome 策略通过 defaults write 命令写入 /Library/Managed Preferences/ 下的 plist:

/Library/Managed Preferences/com.google.Chrome.plist
/Library/Managed Preferences/com.microsoft.edge.plist

每条策略一条 defaults write 命令,需要 sudo 权限。

代码里怎么落地

我们用 Go 的 build tag 实现跨平台,每个平台一个文件:

chrome_policy_windows.go   // build: windows
chrome_policy_linux.go     // build: linux
chrome_policy_darwin.go   // build: darwin
chrome_policy_other.go     // 空实现,其他平台编译不报错

安装插件时自动调用 SetChromeLNAPolicy(),卸载时 ClearChromeLNAPolicy() 清理掉——用户全程无感。

核心调用就两行:

if action == "install" {
    util.SetChromeLNAPolicy()
}
if action == "uninstall" {
    util.ClearChromeLNAPolicy()
}

验证方式

写完策略后,有三步验证:

第一步:确认策略已加载。 浏览器地址栏输入 chrome://policy,应该能看到上面那五个策略,值都正确。注意这里即使 flag 页面显示 Default 也没关系——企业策略在更高层级生效,不改变 flag 的显示。

第二步:测试请求。 在系统页面 F12 控制台执行:

fetch("http://localhost:xxx/xxx")
  .then(r => r.json())
  .then(d => console.log(d))
  .catch(e => console.error(e))

正常情况下返回成功。

第三步:重启浏览器。 策略写完必须完全关闭浏览器(任务管理器里结束所有 Chrome 进程)再重新打开,才能生效。

一句话总结

Chrome 152 把手动改 flag 这条路彻底堵死了。如果你的产品也依赖"公网页面调 localhost",别再让用户去改 chrome://flags——装的时候顺便写一组 Chrome 企业策略进去,注册表(Windows)、JSON(Linux)、plist(macOS)三端覆盖,用户全程无感。

往后看

Google 的安全方向很明确——逐步缩小网页能自由访问的范围,把越来越多的网络请求纳入权限管控。依赖"localhost 总是能访问"这个假设的架构,迟早会被 LNA 或类似机制撞上。

企业策略这条路目前有效,因为它走的是 Chrome 官方预留的"管理员管控"通道。但也要关注后续版本有没有进一步收紧——一旦哪天企业策略也开始限制,就得考虑 HTTPS + 权限授权模型的升级了。

两种解法对比

对比项手动改 flag企业策略方案
操作方式每个用户手动去改插件安装时自动写入
用户感知要跟客户解释为啥要去改浏览器配置无感
Chrome 152+flag 已移除,改不了仍有效
优先级低高(覆盖 flag 和默认行为)
可编程不可行(无官方 API)可行(写注册表/JSON/plist)
卸载清理用户得手动改回去插件卸载自动清除