feat: CSP enabled via src/middleware/csp.js (unsafe-inline, blocks external/frame/object)

This commit is contained in:
2026-05-30 15:36:22 +03:00
parent f4c9983612
commit 695264e7b4
2 changed files with 54 additions and 2 deletions
+51
View File
@@ -0,0 +1,51 @@
/**
* src/middleware/csp.js — Content Security Policy через helmet.
*
* Текущее ограничение: EJS-шаблоны используют inline <style>, inline <script>
* и атрибуты onclick/style= — поэтому 'unsafe-inline' пока необходим.
* Это всё равно лучше отключённого CSP: блокируются внешние ресурсы,
* фреймирование, object/embed, переопределение base.
*
* Что блокирует эта политика:
* - Загрузку скриптов/стилей с внешних доменов (CDN-инъекции, supply chain)
* - Фреймирование страниц (clickjacking) — frame-ancestors 'none'
* - <object>, <embed>, <applet> — object-src 'none'
* - Переопределение <base href> — base-uri 'self'
* - POST форм на внешние домены — form-action 'self'
*
* TODO (при переносе стилей в .css и скриптов в .js):
* убрать 'unsafe-inline', добавить nonce или hash для оставшихся inline-блоков.
*
* Экспортирует: cspMiddleware — передавать напрямую в app.use().
*/
'use strict';
const helmet = require('helmet');
/**
* Возвращает helmet CSP middleware.
* Вынесено в функцию чтобы легко заменить директивы без правки server.js.
* @returns {import('express').RequestHandler}
*/
function createCspMiddleware() {
return helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
// 'unsafe-inline' нужен пока стили и скрипты inline в EJS.
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:'], // data: для SVG-иконок если появятся
fontSrc: ["'self'"],
connectSrc: ["'self'"],
objectSrc: ["'none'"], // блок <object>/<embed>/<applet>
baseUri: ["'self'"], // блок переопределения <base>
formAction: ["'self'"], // POST только на свой домен
frameAncestors: ["'none'"], // clickjacking protection
},
});
}
const cspMiddleware = createCspMiddleware();
module.exports = { cspMiddleware };