Определить заголовок HTTP CSP в приложении Electron
Следуя документации API, я не понимаю, как определить HTTP-заголовок Content-Security-Policy для рендеринга моего приложения Electron. Я всегда получаю предупреждение в DevTools.
Я старался:
1) Скопируйте/Вставьте код в API Doc, слепо:
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 http-equiv="Content-Security-Policy" content="default-src 'self'>
Ответы
Ответ 1
Не знаете, почему в документации содержится этот сломанный код. Это сбило меня с ума, но я нашел рабочее решение путем проб и ошибок:
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({ responseHeaders: Object.assign({
"Content-Security-Policy": [ "default-src 'self'" ]
}, details.responseHeaders)});
});
Поэтому аргумент заголовков должен быть объектом с той же структурой, что и исходные заголовки, полученные в details.responseHeaders
. И исходные заголовки также должны быть включены в переданный объект, потому что этот объект, по-видимому, полностью заменяет исходные заголовки ответов.
Опция extraHeaders
не предназначена для заголовков ответов. Это для заголовков запросов, отправленных на сервер.
Ответ 2
Если вы хотите использовать CSP как в режиме разработки (с ресурсами, загружаемыми по протоколу http://
), так и в режиме prod (file://
protocol), то как вы можете это сделать:
Сначала удалите метаданные Content-Security-Policy
из src/index.html
- нам нужно внедрить ее только для режима prod, потому что
-
onHeadersReceived
не будет работать для протокола file://
, как подтверждают документы Electron, а также потому, что - если мы сохраним его в
src/index.html
для режима Dev, он переопределит onHeadersReceived
хотя бы для части ресурсов, а для режима Dev нам потребуются другие настройки.
Затем мы можем добавить его в режим Prod с помощью gulp-inject:
// 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
.
А для режима dev давайте использовать onHeadersReceived, как в примере с документами Electron:
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.
Ответ 3
Как указано в электронных документах, вам придется использовать метатег Content Security Policy (CSP) в html файле при загрузке renderer.html
по схеме file://
(IIRC вы делаете это в приведенный выше пример).
Если вы хотите условно настроить политику безопасности контента для среды prod и dev, вы можете динамически сгенерировать эту строку внутри html на этапе сборки. Я предлагаю использовать шаблонизатор, например, mustache.js
(используется в примере).
Пример (файловые ресурсы)
В моем случае я хотел включить горячую замену модуля (HMR) через веб-сокеты и ресурс file://
в режиме разработки, что требовало ослабления правил 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
(для prod можно использовать значения по умолчанию):
npx mustache cspContent.json index.mustache > index.html
Ресурсы URL
Для использования с ресурсами URL, вы можете придерживаться этого примера:
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src \'none\'']
}
})
})
Убедитесь, что ваши пользовательские заголовки ответов CSP объединены с заголовками по умолчанию - вы не сделаете этого в приведенном выше примере. Здесь вы также можете проверить условия для окружающей среды.
Надеюсь, это поможет.
Ответ 4
В вашем вопросе недостаточно подробностей, чтобы знать, есть ли у вас проблемы с начальной загрузкой или последующими веб-запросами, но моя проблема была связана с начальной загрузкой файла. С приложением Electron, использующим React, я получал предупреждения об использовании встроенных сценариев даже с кодом kayahr. Это связано с тем, что метод onHeadersReceived перехватывает только те запросы, которые были сделаны после первоначальной загрузки приложения. Это не остановит никаких предупреждений от начальной загрузки приложения.
В итоге мне пришлось использовать шаблоны во время сборки приложения, чтобы добавить одноразовый номер во встроенный скрипт и стиль, а также в заголовок CSP в файле HTML, который приложение загружает изначально.
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, если кому-то интересно, хотя есть еще одно предупреждение при запуске приложения, потому что один из пакетов, которые я использую, тянет в response-beautiful-dnd, который добавляет встроенные стили, но в настоящее время не принимает одноразовые.
Ответ 5
Установите следующий метатег в средствах визуализации.
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-xxx or sha256-yyy' " />
Пожалуйста, ознакомьтесь с моим репозиторием на github electronic-renderer-CSP-sample, содержащим образцы как для nonce, так и для Методы SHA для внутреннего & внешние js файлы.