Logo Craft Homelab Docs Контакты Telegram
Security Headers и CSP — защита веб-приложений
Sat Dec 13 2025

Security Headers и Content Security Policy

Security headers — HTTP заголовки, которые сообщают браузеру о политиках безопасности. Правильная настройка критически важна. Эти заголовки защищают от XSS, clickjacking, MIME-sniffing и других атак на уровне браузера.

Essential Headers

Набор необходимых заголовков безопасности можно добавить через библиотеку Helmet для Express или аналогичные для других фреймворков.

Helmet в Express

const helmet = require('helmet');

app.use(helmet());

// Или с кастомными настройками
app.use(helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'"],
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"],
            connectSrc: ["'self'"],
            fontSrc: ["'self'"],
            objectSrc: ["'none'"],
            mediaSrc: ["'self'"],
            frameSrc: ["'none'"]
        }
    },
    hsts: {
        maxAge: 63072000,
        includeSubDomains: true,
        preload: true
    },
    referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
    permissionsPolicy: {
        geolocation: [],
        microphone: [],
        camera: []
    }
}));

Отдельные заголовки

app.use((req, res, next) => {
    // HSTS
    res.setHeader('Strict-Transport-Security', 
        'max-age=63072000; includeSubDomains; preload');
    
    // X-Frame-Options
    res.setHeader('X-Frame-Options', 'DENY');
    
    // X-Content-Type-Options
    res.setHeader('X-Content-Type-Options', 'nosniff');
    
    // X-XSS-Protection (устарел, но для совместимости)
    res.setHeader('X-XSS-Protection', '1; mode=block');
    
    // Referrer-Policy
    res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
    
    // Permissions-Policy
    res.setHeader('Permissions-Policy', 
        'geolocation=(), microphone=(), camera=(), payment=()');
    
    // Content-Security-Policy
    res.setHeader('Content-Security-Policy', "default-src 'self'");
    
    next();
});

Content Security Policy

Basic CSP

Content-Security-Policy: 
    default-src 'self';
    script-src 'self';
    style-src 'self' 'unsafe-inline';
    img-src 'self' data: https:;
    font-src 'self';
    connect-src 'self' https://api.example.com;
    frame-ancestors 'none';
    base-uri 'self';
    form-action 'self';

Директивы CSP

  • default-src — Фоллбек для других. Пример: default-src 'self'
  • script-src — JavaScript. Пример: script-src 'self' 'unsafe-inline'
  • style-src — CSS. Пример: style-src 'self' 'unsafe-inline'
  • img-src — Изображения. Пример: img-src 'self' data: https:
  • font-src — Шрифты. Пример: font-src 'self' https://fonts.gstatic.com
  • connect-src — Fetch/XHR/WebSocket. Пример: connect-src 'self' https://api
  • frame-ancestors — Фреймы. Пример: frame-ancestors 'none'
  • base-uri<base> tag. Пример: base-uri 'self'
  • form-action — Формы. Пример: form-action 'self'

CSP с nonce

// Server-side
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('base64');

res.setHeader('Content-Security-Policy', 
    `script-src 'self' 'nonce-${nonce}';`);

// HTML
// <script nonce="${nonce}">...</script>
// CSP middleware с nonce
app.use((req, res, next) => {
    res.locals.nonce = crypto.randomBytes(16).toString('base64');
    
    res.setHeader('Content-Security-Policy', 
        `script-src 'self' 'nonce-${res.locals.nonce}';`);
    
    next();
});
<!-- Template -->
<script nonce="<%= nonce %>">
    console.log('Secure script');
</script>

CSP с hash

// Вычисление hash
const crypto = require('crypto');
const hash = crypto.createHash('sha256')
    .update('console.log("inline script")')
    .digest('base64');

res.setHeader('Content-Security-Policy', 
    `script-src 'self' 'sha256-${hash}'`);

X-Frame-Options

X-Frame-Options: DENY              # Запретить во всех фреймах
X-Frame-Options: SAMEORIGIN        # Только на том же-origin
X-Frame-Options: ALLOW-FROM https://example.com  # Устарел
app.use(helmet.frameguard({ action: 'deny' }));

X-Content-Type-Options

X-Content-Type-Options: nosniff

Предотвращает MIME-sniffing браузером.

Referrer-Policy

Параметры политики referrer:

  • no-referrer — Не отправлять referrer
  • no-referrer-when-downgrade — По умолчанию
  • origin — Только origin
  • strict-origin — Origin, только HTTPS
  • strict-origin-when-cross-origin — Полный для same-origin, origin для cross
  • same-origin — Для same-origin
  • origin-when-cross-origin — Полный для cross-origin
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }));

Permissions-Policy

Permissions-Policy: 
    geolocation=(),
    microphone=(),
    camera=(),
    payment=(),
    usb=(),
    vr=()
app.use(helmet.permissionsPolicy({
    geolocation: [],
    microphone: [],
    camera: [],
    payment: [],
    usb: []
}));

CORS

const cors = require('cors');

app.use(cors({
    origin: ['https://example.com', 'https://app.example.com'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    exposedHeaders: ['X-Total-Count'],
    credentials: true,
    maxAge: 86400
}));

COOP и COEP

Cross-Origin-Opener-Policy

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Opener-Policy: unsafe-none

Cross-Origin-Embedder-Policy

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Embedder-Policy: credentialless
app.use((req, res, next) => {
    res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
    res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
    next();
});

Reporting

CSP Report-Only

// Тестирование CSP без блокировки
app.use(helmet.contentSecurityPolicy({
    directives: {
        defaultSrc: ["'self'"],
        reportUri: '/csp-report'
    },
    reportOnly: true
}));
// Endpoint для отчётов
app.post('/csp-report', express.json(), (req, res) => {
    console.log('CSP Violation:', req.body['csp-report']);
    // Отправить в систему мониторинга
    saveToMonitoring(req.body);
    res.status(204).end();
});

Report-To

res.setHeader('Report-To', JSON.stringify({
    group: 'csp-endpoint',
    max_age: 86400,
    endpoints: [{ url: '/reports' }]
}));

Nginx конфигурация

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

Тестирование

# Проверка заголовков
curl -I https://example.com

# CSP анализатор
npx csp-evaluator https://example.com

# Mozilla Observatory
curl -s "https://http-observatory.security.mozilla.org/api/v1/analyze?host=example.com"

Заключение

Security headers — первая линия защиты. Правильная конфигурация CSP, HSTS и других заголовков критически важна для безопасности веб-приложений.