在the API documentation之后,我不明白如何为Electron应用程序的呈现器定义Content-Security-Policy HTTP标头。我总是在DevTools中收到警告。
我尝试过:
1)盲目复制/粘贴API文档中的代码:
app.on('ready', () => {
const {session} = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({responseHeaders: `default-src 'self'`})
})
win = new BrowserWindow(...)
win.loadUrl(...)
}
(顺便说一句,我不明白为什么字符串中缺少“ Content-Security-Policy:”。但是添加它不会改变任何内容)
2)用相同的代码修改渲染器的会话:
win = new BrowserWindow(...)
win.loadUrl(...)
const ses = win.webContents.session;
ses.webRequest.onHeadersReceived((details, callback) => {
callback({responseHeaders: `default-src 'self'`})
})
3)向该渲染器添加一个额外的标题:
win = new BrowserWindow(...)
win.loadURL(`file://${__dirname}/renderer.html`,{
extraHeaders: `Content-Security-Policy: default-src 'self'`
});
...
唯一有效的方法是在渲染器HTML文件中使用meta标签:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'>
答案 0 :(得分:7)
不确定为什么文档中包含此损坏的代码。这让我很困惑,但是我通过反复试验找到了一个可行的解决方案:
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({ responseHeaders: Object.assign({
"Content-Security-Policy": [ "default-src 'self'" ]
}, details.responseHeaders)});
});
因此headers参数必须是与details.responseHeaders
中收到的原始标头具有相同结构的对象。并且原始标头也必须包含在传递的对象中,因为该对象似乎完全替代了原始响应标头。
extraHeaders
选项不适用于响应头。用于发送到服务器的请求标头。
答案 1 :(得分:0)
如果您的目标是能够同时在开发模式(通过http://
协议加载资源)和生产模式(file://
协议)中使用CSP,则可以这样做:
首先,删除Content-Security-Policy
从元src/index.html
- 我们需要注入它仅用于PROD模式,因为
onHeadersReceived
会为file://
协议作为电子文档confirm,并且还因为src/index.html
中,它将至少在部分资源上覆盖onHeadersReceived
,对于开发模式,我们需要不同的设置。然后我们可以使用gulp-inject将其注入Prod模式:
// in project dir
npm install --save-dev gulp-inject gulp
// src/index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<base href="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- inject:prod-headers -->
<!-- src/prod-headers.html content will be injected here -->
<!-- endinject -->
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root>Loading...</app-root>
</body>
</html>
// src/prod-headers.html
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
// gulpfile.js
var gulp = require('gulp');
var inject = require('gulp-inject');
gulp.task('insert-prod-headers', function () {
return gulp.src('./dist/index.html')
.pipe(inject(gulp.src('./src/prod-headers.html'), {
starttag: '<!-- inject:prod-headers -->',
transform: function (filePath, file) {
// return file contents as string
return file.contents.toString('utf8')
}
}))
.pipe(gulp.dest('./dist'));
});
然后确保npx gulp insert-prod-headers
在例如ng build
生成dist/index.html
。
在开发模式下,我们使用与电子文档example类似的onHeadersReceived:
const args = process.argv.slice(1);
const devMode = args.some((val) => val === '--serve');
app.on('ready', () => {
if (devMode) {
const {session} = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({responseHeaders: `default-src http: ws:`})
})
}
win = new BrowserWindow(...)
win.loadUrl(...)
}
该溶液在Electron 4.0.3上进行了测试。
答案 2 :(得分:0)
如Electron docs中所指出的那样,通过renderer.html
方案(IIRC)加载file://
时,您必须在html文件中使用内容安全策略(CSP)元标记。您可以在上面的示例中执行此操作。
如果要针对产品和开发环境有条件地调整内容安全策略,则可以在构建步骤中在html内部动态生成此字符串。我建议使用mustache.js
之类的模板引擎(在示例中使用)。
就我而言,我想在开发模式下通过websockets和file://
资源启用热模块替换(HMR),这需要放宽CSP规则(但仅在dev
中!)。
index.mustache:
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="{{{cspContent}}}"
/>
</head>
...
用于开发人员的cspContent.json:
{
"cspContent": "default-src 'self'; connect-src 'self' ws:"
}
dev
中的构建步骤(可以使用产品默认值):
npx mustache cspContent.json index.mustache > index.html
要使用URL资源,您可以坚持使用this example:
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src \'none\'']
}
})
})
确保将自定义CSP响应标头与默认标头合并-在上面粘贴的示例中不要这样做。在这里,您还可以有条件地检查环境。
希望,它会有所帮助。
答案 3 :(得分:0)
您的问题中没有足够的细节来知道您是在初始加载还是后续的Web请求方面遇到问题,但是我的问题是初始文件加载。借助使用React的Electron应用程序,即使使用kayahr的代码,我也收到有关使用内联脚本的警告。这是因为onHeadersReceived方法仅捕获应用程序最初加载后发出的请求。它不会停止来自初始应用程序加载的任何警告。
我最终不得不在应用程序构建过程中使用模板,以将随机数添加到内联脚本和样式以及应用程序最初加载的HTML文件中的CSP标头中。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-<%= scriptNonce %>'; style-src 'nonce-<%= styleNonce %>';">
<link rel="stylesheet" type="text/css" href="./index.css" nonce=<%= styleNonce %>>
<title>Basic Electron App</title>
</head>
<body>
<div id="app"></div>
<script type="application/javascript" nonce=<%= scriptNonce %>>
require('./index.js');
</script>
</body>
</html>
index.css
body {
margin: 0px;
}
.hello {
font-family: "Century Gothic";
width: 800px;
margin: 70px auto;
text-align: center;
}
并在gulfile.js中将以下内容添加到已经拥有的内容中,并确保此任务包含在管道中。您也可以只使用以下代码更新当前的html任务。
const template = require('gulp-template');
const uuidv4 = require('uuid/v4');
gulp.task('copy-html', () => {
// Create nonces during the build and pass them to the template for use with inline scripts and styles
const nonceData = {
scriptNonce: new Buffer(uuidv4()).toString('base64'),
styleNonce: new Buffer(uuidv4()).toString('base64')
};
return gulp.src('src/*.html')
.pipe(template(nonceData))
.pipe(gulp.dest('dist/'));
});
这是一个非常简单的示例。如果有人感兴趣,我会在https://github.com/NFabrizio/data-entry-electron-app上提供一个更完整的示例,尽管在运行该应用程序时仍会出现一条警告,因为我使用的其中一个包会拉入react-beautiful-dnd,这会添加内联样式,但不会当前接受随机数。
答案 4 :(得分:0)
在渲染器中设置以下元标记。
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-xxx or sha256-yyy' " />
请结帐我的github存储库electron-renderer-CSP-sample,其中还包含内部和外部js文件的nonce和SHA方法的示例。