ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Meteor 安全策略配置指南:browser-policy 包的 CSP 与 X-Frame-Options 详解

Meteor 安全策略配置指南:browser-policy 包的 CSP 与 X-Frame-Options 详解 Meteor 安全策略配置指南browser-policy 包的 CSP 与 X-Frame-Options 详解【免费下载链接】meteorMeteor, the JavaScript App Platform项目地址: https://gitcode.com/gh_mirrors/me/meteorbrowser-policy是 Meteor 中用于配置浏览器安全策略的官方包族通过下发Content-Security-PolicyCSP与X-Frame-Options响应头帮助应用预防和缓解跨站脚本攻击XSS与点击劫持Clickjacking。本文基于 browser-policy 官方文档结合仓库内packages/browser-policy*的真实源码与测试用例系统讲解该包族的默认策略、完整 API、推荐配置步骤与底层实现机制读完即可在自己的 Meteor 应用中落地一套安全的浏览器策略。一、browser-policy 包族是什么browser-policy属于 Webapp 体系的一部分是一组在服务器端设置安全相关 HTTP 响应头的包。加入应用后浏览器会强制实施这些策略从而预防和缓解常见攻击跨站脚本攻击XSS通过限制脚本、样式等内容的加载来源降低恶意脚本注入后的破坏面。点击劫持Clickjacking通过限制哪些网站可以以 frame/iframe 方式嵌入你的应用防止恶意站点套壳欺骗用户操作。包族由三个成员组成源码见 packages/browser-policy、packages/browser-policy-content、packages/browser-policy-framing包名作用备注browser-policy聚合包同时引入 content 与 framing 两个子包启用默认的 CSP 与 X-Frame-Optionsbrowser-policy-content配置 Content-Security-Policy只关心内容加载来源附带X-Content-Type-Options: nosniffbrowser-policy-framing配置 X-Frame-Options只关心谁可以 frame 你的应用如果你只想使用 CSP 或 X-Frame-Options 中的一项可以只添加browser-policy-content或browser-policy-framing而不必引入完整的browser-policy。安装方式在应用根目录执行meteor add browser-policy或meteor add browser-policy-content/meteor add browser-policy-framing。所有BrowserPolicyAPI 只能在服务端代码中使用。二、默认策略开箱即得的基线安全当应用添加browser-policy或browser-policy-content后会立即启用一组默认策略其定义位于 browser-policy-content.js 的setDefaultPolicyawait BrowserPolicy.content.setPolicy( default-src self; script-src self unsafe-inline; connect-src *; img-src data: self; style-src self unsafe-inline; );对应到浏览器行为即内容来源图片、脚本、字体等默认只能从应用自身源self加载网络连接XMLHttpRequest 与 WebSocket 可以连接到任意源connect-src *因为 Meteor 应用尤其是部署在 meteor.com 的应用经常需要向多个不同源建立 WebSocket 连接脚本允许内联脚本unsafe-inline因为 Meteor 默认会把运行时配置放在内联script标签里但默认禁止eval()这类字符串转代码的函数样式默认允许内联样式unsafe-inline图片除自身源外还允许data:URL。在 browser-policy-tests.js 中测试对默认策略的断言与上述一致注意实际构造出的头还会合并connect-src * selfdefault-src的来源会复制到未显式列出的指令中default-src self; script-src self unsafe-inline; connect-src * self; img-src data: self; style-src self unsafe-inline;而browser-policy-framing的默认值是X-Frame-Options: SAMEORIGIN见 browser-policy-framing.js即只有与你的应用同源的网页才能以 frame 方式嵌入它。三、推荐配置步骤官方文档给出了大多数应用应遵循的步骤添加browser-policy开启起步策略客户端代码只能从自身源加载内容图片、脚本、字体等但 XHR 与 WebSocket 可连接任意源客户端不能使用eval()等字符串转代码函数应用只能被同源页面 frame。按需调用下述 API 定制策略。如果你的应用完全不需要内联 JavaScript例如没有内联script标签强烈建议在服务端代码中调用BrowserPolicy.content.disallowInlineScripts();这会让页面加载时多一次额外的网络往返因为原本内联在脚本中的 Meteor 运行时配置需要改为单独请求获取但可以禁用除script src加载之外的所有脚本从而显著降低 XSS 风险。调用时机只能在服务端Meteor 在服务器启动时确定浏览器策略因此应在服务端顶层代码或Meteor.startup中调用BrowserPolicy系列函数不能在客户端代码中使用BrowserPolicy。从源码看browser-policy-content与browser-policy-framing的Package.onUse均将主模块注册为server见 package.js 与 browser-policy-framing/package.jsbrowser-policy同样只把入口暴露给服务端packages/browser-policy/package.jsBrowserPolicy全局对象也只在服务端导出。四、Frame 选项控制谁能 frame 你的应用添加browser-policy或browser-policy-framing后默认只允许同源页面 frame 你的应用。以下函数用于修改该策略对应实现见 browser-policy-framing.jsAPI效果底层 header 值BrowserPolicy.framing.disallow()应用永远不会在 frame/iframe 中渲染X-Frame-Options: DENYBrowserPolicy.framing.restrictToOrigin(origin)应用只允许被指定origin的页面以 frame 方式加载X-Frame-Options: ALLOW-FROM originBrowserPolicy.framing.allowAll()移除 X-Frame-Options 头任何网页都可以 frame 应用无不设置该头使用restrictToOrigin时需要注意只能调用一次且只能传一个 origin不支持通配符也不能指定多个允许源这是 X-Frame-Options 协议本身的限制。源码中如果检测到xFrameOptions已以ALLOW-FROM开头再次调用会直接抛出Error(You can only specify one origin that is allowed to frame this app.)见 browser-policy-framing.js测试用例也验证了这一行为browser-policy-tests.js。origin示例http://example.com、https://foo.example.com。ALLOW-FROM尚未被 Chrome 与 Safari 支持会被这两个浏览器忽略。如果需要 Chrome/Safari 支持或需要允许多个域名 frame 你的应用应改用 CSP 的frame-ancestors指令通过BrowserPolicy.content.allowFrameAncestorsOrigin()实现。测试对 framing 的断言如下browser-policy-tests.js默认SAMEORIGIN→disallow()后为DENY→allowAll()后为null→restrictToOrigin(foo.com)后为ALLOW-FROM foo.com重复 restrict 抛异常。五、Content 选项脚本与样式策略BrowserPolicy.content系列函数控制不同类型内容的加载方式核心实现位于 browser-policy-content.js。5.1 脚本JavaScriptAPI说明BrowserPolicy.content.allowInlineScripts()允许内联script标签、javascript:URL 与内联事件处理器。默认策略已允许内联脚本因此该调用是幂等冗余的测试 browser-policy-tests.js 验证了这一点。BrowserPolicy.content.disallowInlineScripts()禁止内联 JavaScript。调用后页面加载会多一次网络往返Meteor 运行时配置不再以内联脚本下发。BrowserPolicy.content.allowEval()允许通过eval()等函数从字符串创建 JavaScript 代码。BrowserPolicy.content.disallowEval()禁止 eval 及同类函数。默认策略禁止 eval但对几乎所有 Meteor 应用而言它会被dynamic-imports包重新启用动态 import 需要字符串转代码能力。从源码看allowInlineScripts/disallowInlineScripts除了修改script-src指令外还会同步调用WebAppInternals.setInlineScriptsAllowed()见 browser-policy-content.js保证 Webapp 侧的运行时内联脚本开关与 CSP 保持一致。5.2 样式CSSAPI说明BrowserPolicy.content.allowInlineStyles()允许内联 style 标签与 style 属性。默认策略已允许内联样式。BrowserPolicy.content.disallowInlineStyles()禁止内联 CSS。注意样式的内联开关只作用于 CSP 头不会像脚本那样联动 Webapp 内联脚本设置。六、Content 白名单按内容类型管理加载来源对于script、object、image、media、font、frame、frame-ancestors、style、connect这 9 种内容类型browser-policy-content都提供了一组同名模板函数生成逻辑见 browser-policy-content.js 的resources数组。6.1 按类型的标准 APIAPI 模板说明BrowserPolicy.content.allowContentTypeOrigin(origin)允许该类型内容从指定 origin 加载。可多次调用以累积白名单。BrowserPolicy.content.allowContentTypeDataUrl()允许该类型内容从data:URL 加载。BrowserPolicy.content.allowContentTypeSameOrigin()允许该类型内容从应用自身源加载。BrowserPolicy.content.disallowContentType()完全禁止该类型内容script例外见下。origin参数规则源码见addSourceForDirectivebrowser-policy-content.js是字符串可包含可选的协议如http、https、开头的通配符、以及可选的通配端口。示例example.com、https://*.example.com、example.com:*。未指定协议时会同时允许 HTTP 与 HTTPS传example.com等价于同时放行http://example.com与https://example.com源码会主动把http://src与https://src都加入指令以抹平不同浏览器对无协议源的解释差异。传入的源会自动去除尾部斜杠foo.com/与foo.com视为一致见 browser-policy-tests.js 的验证。支持 CSP2 的nonce-*与sha256-*/sha384-*/sha512-*摘要写法源码会对这类值自动加单引号测试见 browser-policy-tests.js。6.2 关于disallowContentType()的特例disallowScript()不能用于完全禁止脚本因为那会导致应用自身脚本也无法运行它实际是把script-src置空并关闭 Webapp 内联脚本。disallowObject()等其余类型的disallow则是把对应指令置空构造 header 时会渲染为none。6.3 常见用法示例// 禁止所有 font 标签 BrowserPolicy.content.disallowFont(); // 允许图片的 src 指向 https://example.com 上托管的图片 BrowserPolicy.content.allowImageOrigin(https://example.com); // 允许 XMLHttpRequest 与 WebSocket 连接 https://example.com BrowserPolicy.content.allowConnectOrigin(https://example.com); // 允许你的站点在 frame/iframe 中加载 https://example.com 的内容 BrowserPolicy.content.allowFrameOrigin(https://example.com);需要特别区分两组“frame” APIBrowserPolicy.framing.*X-Frame-Options控制哪些站点可以 frame 你的站点即别人把你嵌进他们的页面BrowserPolicy.content.allowFrameOrigin(origin)CSPframe-src控制你的站点可以在自己页面内 frame 哪些外部内容。若想用 CSP 取代 framing 实现“允许哪些站点 frame 我”则应使用BrowserPolicy.content.allowFrameAncestorsOrigin(origin)frame-ancestors指令参见 browser-policy-tests.js 的验证。七、全局策略 API一次性作用于所有内容类型以下函数可同时对全部内容类型生效源码见 browser-policy-content.jsAPI说明BrowserPolicy.content.allowSameOriginForAll()所有类型内容都允许从自身源加载等价于向每个指令加入self。BrowserPolicy.content.allowDataUrlForAll()所有类型内容都允许data:URL。BrowserPolicy.content.allowOriginForAll(origin)所有类型内容都允许从指定 origin 加载加入default-src并扩散到各指令。BrowserPolicy.content.disallowAll()完全禁止所有内容加载default-src none并关闭 Webapp 内联脚本。综合示例允许https://foo.com加载所有类型内容但禁用object标签BrowserPolicy.content.allowOriginForAll(https://foo.com); BrowserPolicy.content.disallowObject();测试 browser-policy-tests.js 验证了类似的组合disallowAll()后执行allowOriginForAll(foo.com)object-src也会被加入http://foo.com https://foo.com——说明allowOriginForAll会向包括曾被置空在内的所有指令追加源。另外源码注释中保留了历史别名allowAllContentOrigin(origin)、allowAllContentDataUrl()、allowAllContentSameOrigin()、disallowAllContent()TypeScript 声明browser-policy-common.d.ts中亦有收录可按你的偏好使用。八、关闭内容类型嗅探X-Content-Type-Options添加browser-policy-content后还会额外告诉浏览器不要对内容类型做嗅探避免出现“把文本文件当 JavaScript 执行”之类的降级风险——对应响应头X-Content-Type-Options: nosniff。如需恢复内容类型嗅探调用BrowserPolicy.content.allowContentTypeSniffing();该行为由contentSniffingAllowed标志控制默认false源码见 browser-policy-content.js测试 browser-policy-tests.js 验证了默认返回nosniff、调用allowContentTypeSniffing()后不再设置该头。九、这些 Header 是如何被写进响应的browser-policy一族本身不直接写响应头而是通过共享包browser-policy-common挂接 Webapp 中间件完成。其实现位于 browser-policy-common.jsWebApp.handlers.use(...)在每个请求上调用BrowserPolicy.framing._constructXFrameOptions()与BrowserPolicy.content._constructCsp()若返回值非空则分别写入X-Frame-Options与Content-Security-Policy头第 13-29 行。WebApp.rawHandlers.use(...)对所有请求包括静态文件设置X-Content-Type-Options第 37-47 行。测试运行期间BrowserPolicy._runningTest()为 true会跳过写头避免污染其他测试第 13-16、37-40 行。CSP 头的实际构造由_constructCsp完成browser-policy-content.js把内部cspSrcs字典按指令渲染为directive source1 source2;形式并拼接空指令渲染为none并带有缓存cachedCsp避免重复拼接。CSP 解析parseCsp要求策略必须包含default-src否则抛出异常第 74-77 行且default-src的来源会自动合并复制到其他指令第 78-83 行——这与第 2 节中connect-src出现self的现象完全吻合。十、常见问题与注意事项为什么disallowInlineScripts()会多一次往返Meteor 的运行时配置如 DDP 端点、autoupdate 等默认以内联脚本注入 HTML禁止内联脚本后这部分配置改为通过独立请求获取因此首次加载多一次 round-trip。若应用对首屏延迟敏感需权衡安全性与性能。restrictToOrigin在 Chrome/Safari 下不生效请改用BrowserPolicy.content.allowFrameAncestorsOrigin()走 CSPframe-ancestors。为什么默认允许eval关闭却又“好像”开着默认策略确实不含unsafe-eval但dynamic-imports包会在其内部启用动态 import 所需的字符串转代码能力因此多数 Meteor 应用实际可正常使用动态 import无需手动调用allowEval()。所有配置必须在服务器启动阶段完成BrowserPolicy函数在服务端顶层代码或Meteor.startup中调用才有效客户端调用无意义且不可用。组合使用的覆盖顺序后调用的指令级函数如disallowObject()会覆盖allowOriginForAll()在对应指令上的配置测试 browser-policy-tests.js 展示了“先全局 same-origin、再 disallowObject”得到object-src none的结果但再次调用allowOriginForAll()也会向之前被置空的指令追加源。理解这一“增量修改、同指令覆盖”的语义才能准确预测最终下发的 header。十一、进一步阅读官方文档原文docs/source/packages/browser-policy.md聚合包入口packages/browser-policy/package.js 与 browser-policy.jsCSP 实现packages/browser-policy-content/browser-policy-content.jsFraming 实现packages/browser-policy-framing/browser-policy-framing.js中间件挂接与头写入packages/browser-policy-common/browser-policy-common.jsTypeScript 声明packages/browser-policy-common/browser-policy-common.d.ts单元测试packages/browser-policy/browser-policy-tests.js覆盖默认策略、全部 content/framing API 与 header 生成结果【免费下载链接】meteorMeteor, the JavaScript App Platform项目地址: https://gitcode.com/gh_mirrors/me/meteor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表