https://stackoverflow.com/questions/51969512/define-csp-http-header-in-electron-app
问题
问 3 年零 10 个月前
9个月前修改
查看 16k 次
2812
按照API 文档,我不明白如何为我的 Electron 应用程序的渲染器定义 Content-Security-Policy HTTP Header。我总是在 DevTools 中收到警告。
我试过了:
1)直接复制/粘贴API Doc中的代码:

  1. app.on('ready', () => {
  2. const {session} = require('electron')
  3. session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  4. callback({responseHeaders: `default-src 'self'`})
  5. })
  6. win = new BrowserWindow(...)
  7. win.loadUrl(...)
  8. }

(顺便说一句,我不明白为什么字符串中缺少“Content-Security-Policy:”。但是添加它不会改变任何东西)
2)用相同的代码修改渲染器的会话:

  1. win = new BrowserWindow(...)
  2. win.loadUrl(...)
  3. const ses = win.webContents.session;
  4. ses.webRequest.onHeadersReceived((details, callback) => {
  5. callback({responseHeaders: `default-src 'self'`})
  6. })

3)向渲染器添加额外的标题:

  1. win = new BrowserWindow(...)
  2. win.loadURL(`file://${__dirname}/renderer.html`,{
  3. extraHeaders: `Content-Security-Policy: default-src 'self'`
  4. });


唯一有效的是在渲染器 HTML 文件中使用 meta 标记:

  1. <meta http-equiv="Content-Security-Policy" content="default-src 'self'>

于 2018 年 8 月 22 日 15:06编辑
弗罗格
2018 年 8 月 22 日 14:47 提问
阳极氧化器

  • 你用的是什么版本的 Electron?3.0.0 也有同样的问题@beta.7 – 滑箱 2018 年 8 月 25 日 0:54
  • 我用的是 Electron 2.0.8 – 阳极氧化器 2018 年 8 月 26 日 9:22
  • 是否有任何其他答案可以更好地展示如何实际使用 CSP? – 影子bq 2018 年 9 月 19 日 0:06
  • 1目前,没有答案显示如何在加载了 file:// 协议的渲染器的 HTTP 标头响应中定义 CSP。 – 阳极氧化器 2018 年 9 月 19 日 21:40


5 个答案

不知道为什么文档中包含了这个损坏的代码。它让我很困惑,但我通过反复试验找到了一个可行的解决方案:

  1. session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  2. callback({ responseHeaders: Object.assign({
  3. "Content-Security-Policy": [ "default-src 'self'" ]
  4. }, details.responseHeaders)});
  5. });

所以 headers 参数这个对象与 details.responseHeaders 接收的 headers 结构必须相同,并且原始 headers 也必须包含在传递的对象中,因为该对象似乎完全替换了原始响应头。
extraHeaders 选项不适用于响应头,它用于发送到服务器的请求头。
于 2018 年 9 月 9 日 9:35编辑
于 2018 年 9 月 9 日 9:29 回答
卡亚尔

  • 1感谢您的回答。这很有意义。不幸的是,它没有用。虽然不报错,但是当我在渲染器中打开 devtools 时,控制台中会出现缺少 CSP 的警告(在删除 HTML 标记之后)。我错过了什么吗? – 阳极氧化器 2018 年 9 月 10 日 16:57
  • 是的。不幸的是,当我尝试以这种方式注入 headers 时,我的页面仍然可以无限制地加载任何资源。 – 滑箱 2018 年 9 月 11 日 16:00
  • 在 main.js 中使用调试器,我在断点处暂停 onHeadersReceived() 以检查details.responseHeaders。在我的渲染器的 defaultSession 和 session 中,似乎有一个默认的CSP配置”default-src ‘none’”。我不明白为什么我们可以使用此 CSP 加载外部内容。这是行不通的。 – 阳极氧化器 2018 年 9 月 11 日 17:28
  • 代码在这里工作正常。配置为 default-src ‘none’ 时我的应用程序不再工作,因为所有资源都被阻止了,使用 self 时工作正常。您是否在 webdev 控制台中测试了加载内容?也许这不受 CSP 的影响。当启用上下文隔离时,会出现 Electron 部分损坏的 CSP 警告。见 github.com/electron/electron/issues/14510卡亚尔 2018 年 9 月 11 日 18:30
  • 我的应用程序仍然适用于 default-src ‘none’。经过更多测试,似乎只有在加载渲染器后发出的请求才会调用 defaultSession.webRequest.onHeadersReceived 中设置的CSP。如果我在 callback(…); 之前添加 console.log(details.url),控制台中只会打印出我在渲染器中编码的外部请求 URL。与 onBeforeRequest 每次访问文件(.css、.js 等)时实际执行的情况不同。 – 阳极氧化器 2018 年 9 月 15 日 16:14

1
如果您的目标是能够在开发模式(通过http://协议加载资源)和生产模式(file://协议)中使用 CSP,那么您可以这样做:
首先,删除 src/index.html 中的 Content-Security-Policy 元数据——我们只需要在 prod 模式下注入它,因为

  • onHeadersReceived正如 Electron 文档所确认的那样,不适用于file://协议,并且还因为
  • 如果我们在开发模式下将其保留在src/index.html,它将覆盖 onHeadersReceived,至少部分资源是这样,而对于开发模式,我们需要不同的设置。

然后Prod 模式下我们可以使用 gulp-inject 将其注入 :

  1. // in project dir
  2. npm install --save-dev gulp-inject gulp
  1. // src/index.html
  2. <!doctype html>
  3. <html>
  4. <head>
  5. <meta charset="utf-8">
  6. <base href="">
  7. <meta name="viewport" content="width=device-width, initial-scale=1">
  8. <!-- inject:prod-headers -->
  9. <!-- src/prod-headers.html content will be injected here -->
  10. <!-- endinject -->
  11. <link rel="icon" type="image/x-icon" href="favicon.ico">
  12. </head>
  13. <body>
  14. <app-root>Loading...</app-root>
  15. </body>
  16. </html>
  1. // src/prod-headers.html
  2. <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
  1. // gulpfile.js
  2. var gulp = require('gulp');
  3. var inject = require('gulp-inject');
  4. gulp.task('insert-prod-headers', function () {
  5. return gulp.src('./dist/index.html')
  6. .pipe(inject(gulp.src('./src/prod-headers.html'), {
  7. starttag: '<!-- inject:prod-headers -->',
  8. transform: function (filePath, file) {
  9. // return file contents as string
  10. return file.contents.toString('utf8')
  11. }
  12. }))
  13. .pipe(gulp.dest('./dist')); });

然后确保npx gulp insert-prod-headers在例如 ng build 生成之后运行dist/index.html。
对于开发模式,让我们使用类似于 Electron 文档示例的 onHeadersReceived :

  1. const args = process.argv.slice(1);
  2. const devMode = args.some((val) => val === '--serve');
  3. app.on('ready', () => {
  4. if (devMode) {
  5. const {session} = require('electron')
  6. session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
  7. callback({responseHeaders: `default-src http: ws:`})
  8. })
  9. }
  10. win = new BrowserWindow(...)
  11. win.loadUrl(...) }

该解决方案在 Electron 4.0.3 上进行了测试。
于 2019 年 3 月 30 日 5:08编辑
卢克 H
于 2019 年 2 月 1 日 17:30 回答
阿尔乔姆·瓦西里耶夫

  • 使用 file:/// 协议时,是否无法为每个请求提供动态 csp?例如,我们有一个内部 api,我们在用户通过身份验证后点击以获取客户端的 web api 列表,然后我们将在他们的整个会话期间点击该列表。我们不能在构建期间甚至在启动时完全做到这一点 – 亚伦·范德维伦 2020 年 12 月 11 日 21:17

正如Electron 文档中所指出的,当您通过file://协议加载 renderer.html 时,您必须在 html 文件中使用内容安全策略 (CSP) meta 标签(IIRC 您在上面的示例中执行此操作)。
如果您想有条件地为 prod 和 dev 环境调整内容安全策略,您可以在构建步骤中的 html 内动态生成此字符串。我建议使用模板引擎mustache.js(在示例中使用)。

示例(文件资源)

就我而言,我想通过 websockets 和file://资源在开发模式下启用热模块替换 (HMR),这需要放宽 CSP 规则(但仅限于dev!)。
index.mustache:

  1. <html>
  2. <head>
  3. <meta http-equiv="Content-Security-Policy" content="{{{cspContent}}}" />
  4. </head>


开发人员的 cspContent.json:

  1. { "cspContent": "default-src 'self'; connect-src 'self' ws:" }

构建步骤dev(对于 prod 可以使用默认值):

  1. npx mustache cspContent.json index.mustache > index.html

网址资源

对于 URL 资源的使用,你可以参考这个例子

  1. const { session } = require('electron') session.defaultSession.webRequest.onHeadersReceived((details, callback) => { callback({
  2. responseHeaders: {
  3. ...details.responseHeaders,
  4. 'Content-Security-Policy': ['default-src \'none\''] }
  5. }) })

确保将您的自定义 CSP 响应标头与默认标头合并 - 您在上面粘贴的示例中没有这样做。在这里,您还可以有条件地检查环境。
希望能帮助到你。
于 2019 年 6 月 17 日 13:24编辑
于 2019 年 6 月 17 日 9:12 回答
福特04

  • 使用 file:/// 协议时,是否无法为每个请求提供动态 csp?例如,我们有一个内部 api,我们在用户通过身份验证后点击以获取客户端的 web api 列表,然后我们将在他们的整个会话期间点击该列表。我们不能在构建期间甚至在启动时完全做到这一点 – 亚伦·范德维伦 2020 年 12 月 11 日 21:30
  • 至少 Electron 文档清楚地说明了这一点:“CSP 的首选交付机制是 HTTP 标头,但是在使用 file:// 协议加载资源时无法使用此方法。” - 看看上面第一个提供的链接。 – 福特04 2020 年 12 月 12 日 9:09
  • 是的,我已经读过,因此我的问题。想知道是否有人有运气尝试过允许动态 CSP,甚至可能在响应请求之前重写实际的 index.html 文件的元 CSP 标记。 – 亚伦·范德维伦 2020 年 12 月 14 日 17:56

0
您的问题中没有足够的细节来了解您是否在初始加载或后续 Web 请求时遇到问题,但我的问题是初始文件加载。在使用 React 的 Electron 应用程序中,即使使用 kayahr 的代码,我也会收到有关使用内联脚本的警告。这是因为 onHeadersReceived 方法仅捕获应用程序初始加载后发出的请求。它不会停止来自初始应用程序加载的任何警告。
我最终不得不在我的应用程序构建期间使用模板来为内联脚本和样式以及应用程序最初加载的 HTML 文件中的 CSP 标头添加随机数。
index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-<%= scriptNonce %>'; style-src 'nonce-<%= styleNonce %>';">
  6. <link rel="stylesheet" type="text/css" href="./index.css" nonce=<%= styleNonce %>>
  7. <title>Basic Electron App</title>
  8. </head>
  9. <body>
  10. <div id="app"></div>
  11. <script type="application/javascript" nonce=<%= scriptNonce %>> require('./index.js');
  12. </script>
  13. </body>
  14. </html>

index.css

  1. body {
  2. margin: 0px; }
  3. .hello {
  4. font-family: "Century Gothic";
  5. width: 800px;
  6. margin: 70px auto;
  7. text-align: center; }

并在 gulfile.js 中将以下内容添加到您已有的内容中,并确保此任务包含在您的管道中。您也可以使用下面的代码更新您当前的 html 任务。

  1. const template = require('gulp-template');
  2. const uuidv4 = require('uuid/v4');
  3. gulp.task('copy-html', () => {
  4. // Create nonces during the build and pass them to the template for use with inline scripts and styles
  5. const nonceData = {
  6. scriptNonce: new Buffer(uuidv4()).toString('base64'),
  7. styleNonce: new Buffer(uuidv4()).toString('base64') };
  8. return gulp.src('src/*.html')
  9. .pipe(template(nonceData))
  10. .pipe(gulp.dest('dist/')); });

这是一个非常精简的例子。如果有人感兴趣,我在https://github.com/NFabrizio/data-entry-electron-app有一个更完整的示例,尽管在运行应用程序时仍然有一个警告,因为我正在使用的包之一拉入反应-beautiful-dnd,添加内联样式但目前不接受随机数。
于 2019 年 9 月 6 日 15:54编辑
于 2019 年 9 月 6 日 15:43 回答
晶圆厂
-1
在渲染器中设置以下元标记。

请查看我的 github repo electron-renderer-CSP-sample,其中包含用于内部和外部 js 文件的 nonce 和 SHA 方法的示例。
分享
改进这个答案
跟随