Я делаю настольное приложение с помощью Electro и node.js, в котором я пытаюсь реализовать csp /nonce.
До сих пор я разработал скрипт, который должен генерировать Политика безопасности контента: < /p>
const crypto = require('crypto');
class SecurityPolicies {
static NONCE_LENGTH = 32;
static CSP_DIRECTIVES = {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'"],
'style-src': ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
'img-src': ["'self'", "data:", "https:", "blob:"],
'font-src': ["'self'", "data:","https://fonts.gstatic.com"],
'object-src': ["'none'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
'frame-ancestors': ["'none'"],
'connect-src': ["'self'","ws:", "wss:"],
'media-src': ["'self'", "blob:"],
'worker-src': ["'self'", "blob:"],
'manifest-src': ["'self'"]
};
constructor() {
this.nonce = this.generateNonce();
this.contentSecurityPolicy = this.buildCSP();
}
generateNonce() {
return crypto.randomBytes(SecurityPolicies.NONCE_LENGTH).toString('base64');
}
buildCSP() {
const directives = {...SecurityPolicies.CSP_DIRECTIVES};
directives['script-src'] = [
...directives['script-src'],
`'unsafe-inline'`,
`'nonce-${this.nonce}'`
];
directives['style-src'] = [
...directives['style-src'],
`'unsafe-inline'`,
`'nonce-${this.nonce}'`
];
return Object.entries(directives)
.map(([key, values]) => `${key} ${values.join(' ')}`)
.join('; ');
}
applySecurityPolicy(window) {
window.webContents.session.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [this.contentSecurityPolicy],
'X-Content-Type-Options': ['nosniff'],
'X-Frame-Options': ['DENY'],
'X-XSS-Protection': ['1; mode=block'],
'Referrer-Policy': ['strict-origin-when-cross-origin'],
'Permissions-Policy': ['camera=(), microphone=(), geolocation=()']
}
});
});
// Disable navigation
window.webContents.on('will-navigate', (event, url) => {
const parsedUrl = new URL(url);
if (parsedUrl.origin !== 'file://') {
event.preventDefault();
}
});
// Block new window creation
window.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));
// Disable all permission requests
window.webContents.session.setPermissionRequestHandler((_, __, callback) => {
callback(false);
});
// Disable remote content
window.webContents.session.setPreloads([]);
}
static initSecurity(window) {
const securityPolicies = new SecurityPolicies();
securityPolicies.applySecurityPolicy(window);
return securityPolicies;
}
getNonce() {
return this.nonce;
}
getCSP() {
return this.contentSecurityPolicy;
}
}
module.exports = SecurityPolicies;
Основная идея состоит в том, чтобы применить Nonce к элементам, которые затем включают мой index.html выглядит следующим образом:
WebStack Deployer for Docker
Maintenance
Check for Updates
Backup Settings
Clear Cache
Reset Preferences
Exit
Settings
Help
About
─
□
×
WebStack Deployer for Docker
Как вы можете видеть, я реализовал этот токен , который должен быть заменен сгенерированной nonce nonce.
я предположил, что могу использовать загрузчик контента: < /p>
async loadContent() {
const isDev = process.env.NODE_ENV === 'development';
const nonce = this.securityPolicies.getNonce();
if (isDev) {
await this.mainWindow.loadURL('http://localhost:3000');
} else {
const htmlPath = path.join(__dirname, '../index.html');
let htmlContent = readFileSync(htmlPath, 'utf8');
htmlContent = htmlContent.replace(//g, nonce);
await this.mainWindow.loadFile(htmlPath, {
query: {nonce: nonce}
});
}
}
Вот где проблема заключается в том, что если я уже видел, что я генерирую htmlcontent и я не использую ее ... это потому, что для его использования я должен Сгенерировать URI, но я всегда получаю ошибку о уродливом URI, каким другим способом я могу его реализовать? Strong> < /p>
const { BrowserWindow } = require('electron');
const path = require('node:path');
const SecurityPolicies = require('../security/SecurityPolicies');
const {readFileSync} = require("node:fs");
class WindowManager {
constructor() {
this.mainWindow = null;
this.securityPolicies = null;
}
async createMainWindow() {
this.mainWindow = new BrowserWindow({
width: 1200,
height: 800,
frame: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
webSecurity: true,
preload: path.join(__dirname, '../preload.js'),
sandbox: true
}
});
this.securityPolicies = SecurityPolicies.initSecurity(this.mainWindow);
this.mainWindow.maximize();
await this.loadContent();
return new Promise((resolve) => {
this.mainWindow.once('ready-to-show', () => {
resolve(this.mainWindow);
});
this.mainWindow.on('closed', () => {
this.mainWindow = null;
this.securityPolicies = null;
});
});
}
async loadContent() {
const isDev = process.env.NODE_ENV === 'development';
const nonce = this.securityPolicies.getNonce();
if (isDev) {
await this.mainWindow.loadURL('http://localhost:3000');
} else {
const htmlPath = path.join(__dirname, '../index.html');
let htmlContent = readFileSync(htmlPath, 'utf8');
htmlContent = htmlContent.replace(//g, nonce);
await this.mainWindow.loadFile(htmlPath, {
query: {nonce: nonce}
});
}
}
getMainWindow() {
return this.mainWindow;
}
getSecurityPolicies() {
return this.securityPolicies;
}
}
module.exports = WindowManager;
main.js class:
const {app} = require('electron');
const AppConfig = require('./models/AppConfig');
const BackendController = require('./controllers/BackendController');
const WindowManager = require('./views/WindowManager');
const IpcController = require('./controllers/IpcController');
const ApplicationSetup = require('./core/ApplicationSetup');
const DevToolsManager = require('./core/DevToolsManager');
const AppLifecycle = require('./core/AppLifecycle');
class Application {
constructor() {
this.appConfig = new AppConfig();
this.backendController = new BackendController();
this.windowManager = null;
this.mainWindow = null;
this.ipcController = null;
}
async initialize() {
ApplicationSetup.configureCommandLineFlags();
app.setPath('userData', this.appConfig.getUserDataPath());
// Enable sandbox for all renderers
app.enableSandbox();
// Disable navigation outside the app
app.on('web-contents-created', (_, contents) => {
contents.on('will-navigate', (event) => {
event.preventDefault();
});
contents.setWindowOpenHandler(() => ({ action: 'deny' }));
});
try {
await this.backendController.initializeFetch();
await this.setupAppEvents();
return this.ipcController;
} catch (error) {
console.error('Initialization error:', error);
throw error;
}
}
async setupAppEvents() {
await app.whenReady();
try {
await this.backendController.startGoBackend();
this.windowManager = new WindowManager();
this.mainWindow = await this.windowManager.createMainWindow();
if (this.mainWindow) {
this.ipcController = new IpcController(
this.windowManager,
this.backendController,
this.appConfig
);
// Only enable DevTools in development
//if (process.env.NODE_ENV === 'development') {
DevToolsManager.setupDevToolsHandlers(this.mainWindow);
//}
}
AppLifecycle.setupAppEventHandlers(this.windowManager, this.backendController);
ApplicationSetup.setupProcessEventHandlers(this.backendController);
} catch (error) {
console.error('Setup error:', error);
throw error;
}
}
}
const application = new Application();
application.initialize().catch(err => {
console.error('Application initialization failed:', err);
app.quit();
});
module.exports = application;
Подробнее здесь: https://stackoverflow.com/questions/794 ... th-node-js
Использование CSP /NONCE в Electron App с node.js ⇐ Javascript
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Как я могу получить доступ к пользовательскому $ nonce для CSP в шаблонах всех видов?
Anonymous » » в форуме Php - 0 Ответы
- 20 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Как я могу получить доступ к пользовательскому $ nonce для CSP в шаблонах всех видов?
Anonymous » » в форуме Php - 0 Ответы
- 23 Просмотры
-
Последнее сообщение Anonymous
-