14. HTTPS и Security Headers
HTTPS / TLS
Заголовок раздела «HTTPS / TLS»HTTPS — HTTP поверх TLS (Transport Layer Security). Шифрует трафик между браузером и сервером.
Без HTTPS:
- Пароли, токены, куки — всё видно в сети
- Провайдер может видеть и изменять трафик
- MITM атаки становятся тривиальными
- Браузеры помечают сайт как “Небезопасный”
Получение SSL сертификата
Заголовок раздела «Получение SSL сертификата»Let’s Encrypt (бесплатно):
# Certbot на Ubuntu/Debiansudo apt install certbot python3-certbot-nginxsudo certbot --nginx -d example.com -d www.example.com
# Автоматическое обновление (добавляется автоматически)sudo systemctl status certbot.timerCloudflare (CDN + SSL):
- Настрой DNS на Cloudflare
- Включи Proxy (оранжевая иконка)
- SSL/TLS → Full (strict) — шифрование end-to-end
Принудительный HTTPS
Заголовок раздела «Принудительный HTTPS»# Nginx — redirect HTTP → HTTPSserver { listen 80; server_name example.com www.example.com; return 301 https://$server_name$request_uri;}
server { listen 443 ssl http2; server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Современные настройки TLS ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; ssl_prefer_server_ciphers off;
# Session resumption ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_session_tickets off;
# OCSP Stapling ssl_stapling on; ssl_stapling_verify on;}Security Headers
Заголовок раздела «Security Headers»HTTP заголовки, инструктирующие браузер о политиках безопасности.
Strict-Transport-Security (HSTS)
Заголовок раздела «Strict-Transport-Security (HSTS)»Браузер всегда использует HTTPS для сайта:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload// Expressapp.use((req, res, next) => { res.setHeader( 'Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload' ); next();});max-age=31536000— 1 годincludeSubDomains— включая поддоменыpreload— включение в браузерный preload list
Осторожно: после включения HSTS невозможно вернуться на HTTP без долгого ожидания!
Content-Security-Policy (CSP)
Заголовок раздела «Content-Security-Policy (CSP)»Уже рассмотрели в уроке по XSS. Краткое напоминание:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'; style-src 'self' https://fonts.googleapis.comX-Content-Type-Options
Заголовок раздела «X-Content-Type-Options»Запрещает браузеру угадывать MIME тип:
X-Content-Type-Options: nosniffБез этого заголовка браузер может выполнить файл как скрипт, даже если Content-Type: text/plain.
X-Frame-Options
Заголовок раздела «X-Frame-Options»Запрещает загрузку сайта в iframe (защита от Clickjacking):
X-Frame-Options: DENY # никогда не показывать в iframeX-Frame-Options: SAMEORIGIN # только в iframe с того же доменаСовременная альтернатива через CSP:
Content-Security-Policy: frame-ancestors 'none'Referrer-Policy
Заголовок раздела «Referrer-Policy»Контролирует, какой Referer отправляется при переходах:
Referrer-Policy: no-referrer # никогда не отправлятьReferrer-Policy: same-origin # только внутри сайтаReferrer-Policy: strict-origin-when-cross-origin # (recommended)Permissions-Policy
Заголовок раздела «Permissions-Policy»Ограничивает доступ к браузерным API:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()Cross-Origin Headers (CORP/COEP/COOP)
Заголовок раздела «Cross-Origin Headers (CORP/COEP/COOP)»Для современных изолированных окружений:
Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corpCross-Origin-Resource-Policy: same-originНужны для SharedArrayBuffer и high-resolution timers.
Полный набор Security Headers
Заголовок раздела «Полный набор Security Headers»Express с helmet
Заголовок раздела «Express с helmet»npm install helmetimport helmet from 'helmet';
app.use(helmet({ // Content-Security-Policy contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", 'https://fonts.googleapis.com'], fontSrc: ["'self'", 'https://fonts.gstatic.com'], imgSrc: ["'self'", 'data:', 'https:'], connectSrc: ["'self'"], frameSrc: ["'none'"], objectSrc: ["'none'"], }, },
// HSTS strictTransportSecurity: { maxAge: 31536000, includeSubDomains: true, preload: true, },
crossOriginEmbedderPolicy: true, crossOriginOpenerPolicy: { policy: 'same-origin' }, crossOriginResourcePolicy: { policy: 'same-origin' },
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },}));Next.js Security Headers
Заголовок раздела «Next.js Security Headers»const securityHeaders = [ { key: 'X-DNS-Prefetch-Control', value: 'on', }, { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload', }, { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'X-XSS-Protection', value: '1; mode=block', }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin', }, { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=(), browsing-topics=()', }, { key: 'Content-Security-Policy', value: [ "default-src 'self'", "script-src 'self' 'unsafe-eval' 'unsafe-inline'", // unsafe нужен для Next.js "style-src 'self' 'unsafe-inline'", "img-src * blob: data:", "media-src 'none'", "connect-src *", "font-src 'self'", ].join('; '), },];
module.exports = { async headers() { return [ { source: '/(.*)', headers: securityHeaders, }, ]; },};CORS (Cross-Origin Resource Sharing)
Заголовок раздела «CORS (Cross-Origin Resource Sharing)»Контролирует, какие сайты могут обращаться к твоему API:
import cors from 'cors';
// Разрешить конкретные доменыapp.use(cors({ origin: (origin, callback) => { const allowedOrigins = [ 'https://myapp.com', 'https://www.myapp.com', process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : null, ].filter(Boolean);
if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, credentials: true, // разрешить куки methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization', 'X-CSRF-Token'], maxAge: 86400, // кешировать preflight 24 часа}));
// Не делай этого!app.use(cors({ origin: '*', credentials: true })); // credentials не работает с *app.use(cors()); // разрешает все — плохо для production APIПроверка Security Headers
Заголовок раздела «Проверка Security Headers»Онлайн инструменты
Заголовок раздела «Онлайн инструменты»- securityheaders.com — оценка заголовков (от A+ до F)
- observatory.mozilla.org — Mozilla Observatory
- ssllabs.com/ssltest — SSL конфигурация
Через curl
Заголовок раздела «Через curl»curl -I https://example.com | grep -E "(Strict|Content-Security|X-Frame|X-Content)"В Browser DevTools
Заголовок раздела «В Browser DevTools»Network → выбери запрос → Response Headers
Итог: полный Security Headers чеклист
Заголовок раздела «Итог: полный Security Headers чеклист»| Заголовок | Приоритет | Что делает |
|---|---|---|
| HSTS | Критичный | Принудительный HTTPS |
| Content-Security-Policy | Критичный | Защита от XSS |
| X-Content-Type-Options | Высокий | Запрет MIME sniffing |
| X-Frame-Options | Высокий | Защита от Clickjacking |
| Referrer-Policy | Средний | Контроль Referer |
| Permissions-Policy | Средний | Ограничение API |
| CORS | Критичный для API | Контроль cross-origin |
Практические задания
Заголовок раздела «Практические задания»- Установи
helmetв Express приложение и настрой все заголовки - Проверь своё приложение на https://securityheaders.com
- Настрой Let’s Encrypt через certbot для домена
- Добавь Security Headers в next.config.js