Перейти к основному содержимому

Content Security Policy для ShopStory

Content Security Policy (CSP) — это дополнительный уровень безопасности, который помогает предотвратить XSS, clickjacking и другие атаки на ваш сайт при встраивании внешнего контента, включая ShopStory плееры и виджеты.


🎯 Быстрый старт: Минимальная CSP

Если вы встраиваете ShopStory плеер через iframe:

HTML meta tag
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
frame-src https://watch.shopstory.live;
connect-src https://app.shopstory.live;
img-src https://cdn.shopstory.live https:;
style-src 'self' 'unsafe-inline';
">

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

/etc/nginx/sites-available/yoursite.conf
server {
listen 443 ssl http2;
server_name yoursite.com;

# Content Security Policy
add_header Content-Security-Policy "
default-src 'self';
frame-src https://watch.shopstory.live;
connect-src 'self' https://app.shopstory.live;
img-src 'self' https://cdn.shopstory.live https: data:;
style-src 'self' 'unsafe-inline';
script-src 'self' 'unsafe-inline';
" always;

# Другие security headers
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;

location / {
root /var/www/yoursite;
try_files $uri $uri/ /index.html;
}
}

📋 Полная CSP конфигурация

Для сайтов с ShopStory интеграцией

Продвинутая Nginx CSP конфигурация
# Определяем CSP политику как переменную для удобства
map $request_uri $csp_policy {
default "
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' https://cdn.shopstory.live https: data:;
media-src 'self' https://cdn.shopstory.live;
connect-src 'self' https://app.shopstory.live https://www.google-analytics.com;
frame-src 'self' https://watch.shopstory.live;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
";
}

server {
listen 443 ssl http2;
server_name yoursite.com;

# SSL configuration
ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

# CSP Header
add_header Content-Security-Policy $csp_policy always;

# Дополнительные security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
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 Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

location / {
root /var/www/yoursite;
try_files $uri $uri/ /index.html;

# Кэширование статики
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}

🔧 Директивы CSP для ShopStory

Обязательные директивы

ДирективаЗначениеНазначение
frame-srchttps://watch.shopstory.liveРазрешает встраивание iframe с плеером
connect-srchttps://app.shopstory.liveРазрешает AJAX запросы к API
img-srchttps://cdn.shopstory.live https:Разрешает загрузку изображений

Опциональные (для расширенной интеграции)

ДирективаЗначениеНазначение
media-srchttps://cdn.shopstory.liveДля прямой загрузки видео (если используете)
worker-src'self' blob:Для Service Workers и PWA
manifest-src'self'Для Web App Manifest

🌐 Apache конфигурация

/etc/apache2/sites-available/yoursite.conf
<VirtualHost *:443>
ServerName yoursite.com

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/yoursite.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yoursite.com/privkey.pem

# Content Security Policy
Header always set Content-Security-Policy "\
default-src 'self'; \
script-src 'self' 'unsafe-inline'; \
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
font-src 'self' https://fonts.gstatic.com; \
img-src 'self' https://cdn.shopstory.live https: data:; \
connect-src 'self' https://app.shopstory.live; \
frame-src 'self' https://watch.shopstory.live; \
frame-ancestors 'self'; \
"

# Дополнительные security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

DocumentRoot /var/www/yoursite
<Directory /var/www/yoursite>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

💻 Клиентская CSP (JavaScript)

Динамическое добавление CSP через meta tag

Добавление CSP в SPA приложениях
/**
* Добавить CSP meta tag для ShopStory интеграции
*/
function setupShopStoryCSP() {
const existingCSP = document.querySelector('meta[http-equiv="Content-Security-Policy"]');

// Если CSP уже есть, обновляем
if (existingCSP) {
console.warn('CSP уже установлен, обновите вручную');
return;
}

const cspMeta = document.createElement('meta');
cspMeta.httpEquiv = 'Content-Security-Policy';
cspMeta.content = `
default-src 'self';
frame-src https://watch.shopstory.live;
connect-src 'self' https://app.shopstory.live;
img-src 'self' https://cdn.shopstory.live https: data:;
style-src 'self' 'unsafe-inline';
script-src 'self' 'unsafe-inline';
`.replace(/\s+/g, ' ').trim();

document.head.appendChild(cspMeta);
console.log('✅ CSP для ShopStory установлен');
}

// Вызываем при загрузке приложения
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupShopStoryCSP);
} else {
setupShopStoryCSP();
}

React приложения

CSP для Create React App
// public/index.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<!-- Content Security Policy -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' https://cdn.shopstory.live https: data:;
font-src 'self' data:;
connect-src 'self' https://app.shopstory.live;
frame-src https://watch.shopstory.live;
worker-src 'self' blob:;
">

<title>Your App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

🧪 Режим report-only (для тестирования)

Перед применением CSP в продакшене, протестируйте в режиме report-only:

CSP в режиме мониторинга
server {
listen 443 ssl http2;
server_name yoursite.com;

# Report-Only режим - не блокирует, только логирует
add_header Content-Security-Policy-Report-Only "
default-src 'self';
frame-src https://watch.shopstory.live;
connect-src 'self' https://app.shopstory.live;
report-uri /csp-violation-report;
" always;

# Endpoint для получения CSP нарушений
location = /csp-violation-report {
access_log /var/log/nginx/csp_violations.log;
return 204;
}
}

Анализ CSP нарушений

Мониторинг CSP нарушений в браузере
// Слушаем CSP нарушения
document.addEventListener('securitypolicyviolation', (e) => {
console.warn('🚨 CSP Violation:', {
blockedURI: e.blockedURI,
violatedDirective: e.violatedDirective,
originalPolicy: e.originalPolicy,
sourceFile: e.sourceFile,
lineNumber: e.lineNumber
});

// Отправляем в систему мониторинга
fetch('/api/log-csp-violation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
blockedURI: e.blockedURI,
directive: e.violatedDirective,
timestamp: new Date().toISOString()
})
}).catch(err => console.error('Failed to log CSP violation:', err));
});

🔒 CSP для разных окружений

Development (разработка)

<!-- Разрешаем больше для удобства разработки -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
connect-src 'self' https://app.shopstory.live http://localhost:* ws://localhost:*;
frame-src https://watch.shopstory.live;
img-src 'self' https://cdn.shopstory.live https: data:;
">

Staging (тестирование)

<!-- Строже чем dev, но с report-only -->
<meta http-equiv="Content-Security-Policy-Report-Only" content="
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' https://app.shopstory.live;
frame-src https://watch.shopstory.live;
img-src 'self' https://cdn.shopstory.live https: data:;
report-uri /csp-report;
">

Production (продакшн)

<!-- Максимально строгая политика -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self';
style-src 'self';
connect-src 'self' https://app.shopstory.live;
frame-src https://watch.shopstory.live;
img-src 'self' https://cdn.shopstory.live https:;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
">

🎨 Next.js конфигурация

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' https://cdn.shopstory.live https: data:;
font-src 'self' data:;
connect-src 'self' https://app.shopstory.live;
frame-src https://watch.shopstory.live;
worker-src 'self' blob:;
child-src 'self' blob:;
`.replace(/\s{2,}/g, ' ').trim()
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
},
{
key: 'Permissions-Policy',
value: 'geolocation=(), microphone=(), camera=()'
}
]
}
];
}
};

module.exports = nextConfig;

⚡ Vercel конфигурация

vercel.json
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; frame-src https://watch.shopstory.live; connect-src 'self' https://app.shopstory.live; img-src 'self' https://cdn.shopstory.live https: data:; style-src 'self' 'unsafe-inline';"
},
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}
]
}
]
}

🚀 Netlify конфигурация

netlify.toml
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = '''
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' https://cdn.shopstory.live https: data:;
connect-src 'self' https://app.shopstory.live;
frame-src https://watch.shopstory.live;
font-src 'self' data:;
'''
X-Frame-Options = "SAMEORIGIN"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"

🛠️ Тестирование CSP

Инструменты для проверки

  1. Browser DevTools

    Chrome DevTools → Console
    Ищите сообщения "Content Security Policy"
  2. CSP Evaluator (Google)

    https://csp-evaluator.withgoogle.com/
  3. Report URI

    https://report-uri.com/home/tools

Проверка в коде

Проверка CSP в runtime
/**
* Проверить, применена ли CSP
*/
function checkCSPStatus() {
const cspMeta = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
const cspReportMeta = document.querySelector('meta[http-equiv="Content-Security-Policy-Report-Only"]');

if (cspMeta) {
console.log('✅ CSP активен (enforcing mode)');
console.log('Policy:', cspMeta.content);
} else if (cspReportMeta) {
console.log('⚠️ CSP в режиме report-only');
console.log('Policy:', cspReportMeta.content);
} else {
console.warn('❌ CSP не настроен');
}

// Проверяем, можем ли мы загружать ShopStory ресурсы
testShopStoryCSP();
}

/**
* Тест CSP для ShopStory
*/
async function testShopStoryCSP() {
const tests = {
'API Connection': async () => {
const response = await fetch('https://app.shopstory.live/v3/streams?applicationId=test');
return response.ok;
},
'Image Loading': () => {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(true);
img.onerror = () => resolve(false);
img.src = 'https://cdn.shopstory.live/test.jpg';
});
}
};

console.log('🧪 Тестирование CSP для ShopStory...');

for (const [name, test] of Object.entries(tests)) {
try {
const result = await test();
console.log(result ? '✅' : '❌', name);
} catch (error) {
console.log('❌', name, '-', error.message);
}
}
}

// Запуск при загрузке
checkCSPStatus();

📊 Контрольный список CSP

Перед деплоем в production:

  • ✅ CSP заголовки настроены на сервере (Nginx/Apache)
  • frame-src включает https://watch.shopstory.live
  • connect-src включает https://app.shopstory.live
  • img-src включает https://cdn.shopstory.live
  • ✅ Протестировано в режиме report-only
  • ✅ CSP нарушения не блокируют ShopStory функционал
  • ✅ Другие security headers настроены (HSTS, X-Frame-Options)
  • ✅ CSP разный для dev/staging/prod окружений
  • ✅ Настроен мониторинг CSP нарушений
  • ✅ Документация обновлена для команды

🔗 Дополнительные ресурсы


Готово! Теперь ваш сайт защищен CSP при интеграции ShopStory.