Content Security Policy (CSP) — HTTP-заголовок, ограничивающий источники скриптов, стилей и других ресурсов. Правильно настроенный CSP предотвращает выполнение вредоносного кода даже при XSS-уязвимости.
Content-Security-Policy: default-src 'self'; script-src 'self'
При такой настройке timeago.js, загруженный из CDN стороннего сервера, будет заблокирован.
Если библиотека загружается с CDN:
<script src="https://cdn.jsdelivr.net/npm/timeago.js/dist/timeago.min.js"
integrity="sha384-..."
crossorigin="anonymous"></script>
Content-Security-Policy: script-src 'self' https://cdn.jsdelivr.net
Для дополнительной защиты использовать integrity
(Subresource Integrity):
<script src="https://cdn.jsdelivr.net/npm/timeago.js@4.0.2/dist/timeago.min.js"
integrity="sha384-rr8NMDKlGDgJz6/3CdWXGHC5Y5Q6pQfQUfHfQnD3hS6Y2Lv9l1J6KBfQb1yMag="
crossorigin="anonymous"></script>
Когда timeago.js включён в собственный бандл, он загружается с того же домена:
Content-Security-Policy: script-src 'self'
Все скрипты должны поступать с 'self' — дополнительных
разрешений не нужно.
timeago.js не использует inline скрипты, поэтому
unsafe-inline не требуется для самой библиотеки. Если в
проекте нет других inline скриптов, CSP может быть строгим:
Content-Security-Policy: script-src 'self'; object-src 'none'
Content-Security-Policy: script-src 'nonce-{RANDOM_NONCE}'
Только скрипты с атрибутом nonce будут выполнены:
<script nonce="abc123">
// Этот скрипт будет выполнен
import { render } from 'timeago.js';
render(document.querySelector('time'), 'ru');
</script>
<script>
// Этот скрипт будет заблокирован
</script>
Если инлайн-скрипт статичен, можно использовать hash:
Content-Security-Policy: script-src 'sha256-{HASH_OF_SCRIPT}'
# Вычислить хеш скрипта
echo -n "import { render } from 'timeago.js';" | openssl dgst -sha256 -binary | base64
Webpack в development-режиме может использовать
eval:
Content-Security-Policy: script-src 'self' 'unsafe-eval'
В production это недопустимо. Настроить webpack:
// webpack.config.js
module.exports = {
devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
};
source-map не требует unsafe-eval в отличие
от eval-source-map.
// vite.config.ts
export default {
plugins: [{
name: 'csp-headers',
configureServer(server) {
server.middlewares.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'"
);
next();
});
},
}],
};
Открыть DevTools → Console.
При нарушении CSP появится сообщение:
Refused to load the script 'https://...' because it violates the following Content Security Policy directive: ...Network → Headers → Content-Security-Policy —
посмотреть текущую политику.
Content-Security-Policy: script-src 'self'; report-uri /csp-report
// Express — обработчик отчётов
app.post('/csp-report', (req, res) => {
console.log('CSP violation:', req.body);
res.status(204).end();
});
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self' dat a:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self'
При такой политике timeago.js должен быть включён в собственный бандл
(script-src 'self'), а CDN загрузка запрещена.
| Условие | Настройка CSP |
|---|---|
| timeago.js из собственного бандла | script-src 'self' |
| timeago.js с CDN | script-src 'self' https://cdn.jsdelivr.net + SRI |
| Нет inline скриптов | Не нужен unsafe-inline |
| Development webpack | unsafe-eval только для dev |
| Мониторинг нарушений | report-uri /csp-report |