Как наиболее распространенные уязвимости проявляются в full-stack приложениях и как предотвратить их появление.
Безопасность веб-приложений — одна из самых сложных и динамично развивающихся областей. Количество терминов, аббревиатур и быстро меняющихся тенденций может запутать даже опытных разработчиков. Пока хакеры и аналитики ведут бесконечную борьбу за уязвимости, новые угрозы появляются с невероятной скоростью.
Разработчики JavaScript уже перегружены появлением новых архитектур — от React Server Components и Next.js App Router до Astro Islands. В таких условиях сложно уделять внимание безопасности в полной мере.
Однако, если изучить наиболее частые уязвимости, можно значительно повысить защищенность своего приложения. В этой статье мы ознакомимся с OWASP Top 10 — списком самых распространенных уязвимостей, составленным ведущими ИБ-специалистами.
Что такое OWASP Top 10?
OWASP (Open Worldwide Application Security Project) — некоммерческая организация, занимающаяся глобальным повышением уровня безопасности ПО. Она поддерживает open-source проекты и разрабатывает образовательные ресурсы. Среди них OWASP Top 10 — перечень десяти самых распространенных уязвимостей, которым подвержены веб-приложения.
В этой статье подробно разберем каждую из этих уязвимостей на примере full-stack приложений, используя Next.js. Однако все концепции применимы и к другим архитектурам, даже за пределами экосистемы JavaScript.
№10: Server-Side Request Forgery (SSRF)
Многие знакомы с серверным рендерингом (SSR — Server-Side Rendering). SSRF (Server-Side Request Forgery) — это его зловещий "близнец".
SSRF можно кратко описать как возможность атакующего отправлять запросы через серверное окружение приложения. Помимо того, что это может привести к увеличению расходов на хостинг, главная проблема заключается в том, что злоумышленник получает уровень доступа, аналогичный уровню доверия сервера. В сложной архитектуре это означает возможность атаковать внутренние приватные сервисы, используя компрометированный сервер.
Как выглядит уязвимость?
Пример: приложение позволяет пользователю вводить URL и получать краткое описание содержимого страницы с помощью AI SDK. Злоумышленник вводит localhost:3000 вместо обычного веб-сайта. В результате сервер отправляет запрос самому себе или другим сервисам, работающим на порту 3000. Это классическая SSRF-уязвимость!
Чтобы предотвратить подобные атаки, необходимо осторожно обрабатывать запросы, зависящие от пользовательского ввода, особенно на серверной стороне.
№9: Ошибки логирования и мониторинга безопасности
Было бы удобно, если бы сервер Node.js мог общаться с разработчиками телепатически. Вместо этого приходится полагаться на поток неструктурированного текста, называемый "логами".
Логирование важно не только для отладки или оптимизации производительности, но и для обнаружения и устранения проблем безопасности.
Как улучшить логирование?
- Логировать ключевые транзакции (авторизация, регистрация, платежи, отправка писем и т. д.).
- В крупных проектах стоит рассмотреть телеметрические решения:
- Open Telemetry
- Sentry
- Datadog
- Если используется React Server Components, придется продумать стратегию логирования, так как их нельзя дебажить прямо в браузере.
№8: Ошибки целостности программного обеспечения и данных
Этот пункт охватывает множество угроз, связанных с нарушением целостности данных и программных компонентов. Одна из самых актуальных проблем в этой категории — атаки на цепочку поставок (Supply Chain Attacks).
Пример: уязвимость Log4J
В 2021 году широкую огласку получила критическая уязвимость в Log4J — широко используемой Java-библиотеке для логирования. Эта уязвимость позволяла злоумышленникам удаленно выполнять код на серверах, что привело к массовым атакам.
Почему это опасно?
- Хакеры могут использовать взломанные зависимости для проникновения в систему.
- Уязвимые пакеты могут оставаться в коде незамеченными годами.
- Современные приложения часто используют десятки тысяч зависимостей через NPM, PyPi, Maven и другие менеджеры пакетов.
Как защититься?
- Проверять библиотеки перед установкой
- Есть ли активная поддержка?
- Насколько важна библиотека для приложения?
- Кто ее автор?
- Есть ли ошибки в названии при установке (ошибочные пакеты часто создаются для атак)?
- Использовать анализ уязвимостей в цепочке поставок (Supply Chain Analysis, SCA)
- GitHub Dependabot (бесплатное решение).
- Snyk или Datadog (платные решения).
- Контролировать используемые версии зависимостей
- Обновлять пакеты до последних безопасных версий.
- Использовать инструменты автоматической проверки уязвимостей.
№7: Ошибки идентификации и аутентификации
Одним из ключевых элементов безопасности является надежная система идентификации и аутентификации. Если в ней есть слабые места, это может привести к утечке аккаунтов, компрометации паролей и атакам на систему аутентификации.
Пример: утечка пароля администратора
Один из самых частых сценариев:
- Пароль администратора утекает в сеть.
- Хакер находит его.
- Полный доступ к системе оказывается в руках злоумышленника.
Как предотвратить атаки на систему аутентификации?
Одной из распространенных техник атак является перебор паролей (brute-force).
Решение:
- Ограничение частоты запросов (rate limiting).
- Блокировка подозрительных IP-адресов.
В Next.js для этого можно использовать edge middlewares.
Middlewares — это небольшие прокси, написанные на JavaScript. Они обрабатывают запросы очень-очень быстро — быстрее, чем обычные конечные точки на Node.js, например. Middlewares хорошо подходят для низкоуровневой обработки, вроде блокировки вредоносных IP-адресов или переадресации пользователей на правильную языковую версию страницы.
Один из интересных вариантов использования — rate limiting (ограничение количества запросов). Можно быстро повысить безопасность приложения, ограничив частоту обращений к POST-эндпоинтам, особенно для входа в систему и регистрации.
Можно пойти ещё дальше и настроить WAF. WAF (Web Application Firewall) — это защитный экран между пользователем и сервером. Он анализирует HTTP-запросы и блокирует вредоносные запросы. WAF может фильтровать атаки на уровень приложения, предотвращая SQL-инъекции, XSS и brute-force атаки.
№6: Уязвимые и устаревшие компоненты (Vulnerable And Outdated Components)
Часто бывает, что специалисты по безопасности обнаруживают уязвимости заранее — ещё до того, как злой хакер даже подумает их эксплуатировать. Спасибо им за это! Когда подобное происходит, они регистрируют так называемый CVE (Common Vulnerabilities and Exposure) и заносят его в общедоступную базу данных.
Спасение то же, что и в случае с атаками на цепочку поставок: настройте решение для SCA, вроде Dependabot, Snyk, WhiteSource., чтобы оно регулярно проверяло пакеты в вашем приложении на наличие уязвимостей. И следите за базами данных уязвимостей (CVE, NVD).
№5: Неправильная конфигурация безопасности (Security Misconfiguration)
Существует множество настроек, которые можно сделать неправильно. Здесь мы сконцентрируемся на самом важном для веб-разработчика, изучающего безопасность: HTTP-заголовках.
С помощью HTTP-заголовков в ответе сервера вы можете передавать браузеру пользователя массу информации о том, что на вашем сайте разрешено, а что нет.
Например, установив нужным образом заголовок Permissions-Policy, вы можете указать, что вашему сайту никогда не понадобится доступ к камере пользователя. Это мощная мера защиты на случай, если в ваш сайт всё-таки внедрится вредоносный скрипт (XSS). Даже если хакеру удастся запустить скрипт в браузере жертвы, тот всё равно не разрешит доступ к камере.
Изучите настройки безопасности любого шаблона или boilerplate, который вы используете для создания собственных сайтов. Понимаете ли вы их? Можете ли что-то улучшить? Ответы на эти вопросы неизбежно приведут к тому, что ваш сайт станет гораздо безопаснее!
№4: Небезопасная архитектура (Insecure Design)
Дизайн — это не только про код, но и про то, как мы используем наши средства разработки для создания программных продуктов.
В контексте full-stack-фреймворков освоить Next.js действительно непросто. Next.js основан на гибридизации клиентской и серверной логики, и некоторые подходы к нему не подходят для конкурирующих фреймворков с иной архитектурой — например, Astro.js или Remix.
Однако команда Next.js создала множество бесплатных материалов, включая статьи и документацию, специально посвящённые вопросам безопасности. Если вы используете Next.js в профессиональной среде, подумайте о проведении полноценного обучения по безопасности, прежде чем браться за серьёзные проекты.
№3: Инъекции (Injection)
Из-за уязвимостей инъекций в React функция для внедрения HTML называется довольно недружелюбно — dangerouslySetInnerHTML. React не хочет, чтобы вы включали пользовательский ввод, который может содержать вредоносный скрипт.
На скриншоте ниже показана демонстрация инъекции с помощью изображений. Это может произойти, например, на форуме. Злоумышленник неправильно использует систему размещения изображений, подставляя URL, который ссылается не на реальное изображение, а на GET-эндпоинт API.
Когда пользователь видит такой «пост» в своём браузере, выполняется аутентифицированный GET-запрос к вашему бэкенду, который, например, проводит автоплатеж. Наличие GET-эндпоинта, запускающего побочные эффекты вроде платежей, также создаёт риск Cross-Site Request Forgery (CSRF).
Даже опытные разработчики могут попасться в ловушку. Помните, что динамические параметры маршрутов — это тоже пользовательский ввод. Например, [language]/page.jsx в приложении на Next.js или Astro. Часто в логах можно увидеть грубые попытки атаки, когда вместо языка подставляют путь вида ../../../../passwords.txt.
Zod — популярная библиотека для серверной валидации пользовательских данных. Вы можете добавить шаг преобразования (transform), чтобы «очищать» (sanitize) данные, которые пойдут в запросы к базе данных или туда, где они могут выполняться как код.
№2: Ошибки криптографии (Cryptographic Failures)
Данная уязвимость в основном касается бэкенд-разработчиков, которые работают с конфиденциальными данными (PII) или паролями.
Пароли должны храниться в невосстановимом виде, то есть с помощью хэширующих алгоритмов. Смысл в том, что если база с паролями утечёт вместе с ключом шифрования, то даже тогда взломать аккаунт будет сложно (нельзя просто «обратить» результат хэша). Можно использовать вход без пароля (passwordless) с magic link на почту и хэшированием email — так что даже администратор не может вычислить адрес пользователя по хэшу в базе.
№1. Нарушение контроля доступа (Broken Access Control).
Речь о том, что пользователи могут получать доступ к чужим аккаунтам или ресурсам, к которым они не должны иметь доступа. Например, проверка авторизации в layout может оставить содержимое страниц в Next.js без защиты. Это не ошибка фреймворка, а следствие того, что React Server Components имеют другую модель, чем клиентские компоненты, и это влияет на работу layout.
Ниже пример кода, где админ пытается реализовать платный доступ (paywall) в Next.js, но фактически ничего не защищает:
// app/layout.jsx
// Используем аутентификацию на базе cookie, как обычно
async function checkPaid() {
const token = cookies.get("auth_token");
return await db.hasPayments(token);
}
// Выполняем проверку оплаты в layout, чтобы она применялась ко всем страницам
// Но дело в том, что Next.js работает иначе.
export default async function Layout() {
// Это не сработает, как ожидается
const hasPaid = await checkPaid();
if (!hasPaid)
redirect("/subscribe");
// Рендерим вложенную страницу
return <div>{children}</div>;
}
// К этой странице можно получить прямой доступ,
// добавив “RSC=1” к запросу, который её подгружает.
export default function Page() {
return <div>PAID CONTENT</div>
}
Чему мы научились
Большинство уязвимостей напрямую связано с тем, как мы проектируем приложения:
- Мы копируем чью-то конфигурацию, не до конца её понимая.
- Мы не до конца осознаём внутренние принципы работы используемого фреймворка. Next.js — это сложная штука.
- Мы выбираем неподходящий алгоритм для конкретной задачи.
Большинство хакеров действует массово и не обладает исключительными навыками, так что они терпят фиаско, когда сталкиваются с опытными разработчиками, которые умеют вовремя заметить и устранить наиболее распространённые уязвимости.
Заключение
Безопасность веб-приложений — это постоянный процесс, а не одноразовое действие. OWASP Top 10 предоставляет не просто список уязвимостей, а направление, в котором разработчикам следует двигаться, чтобы минимизировать риски. Понимание этих угроз и применение соответствующих мер защиты на всех уровнях разработки позволит значительно повысить безопасность приложений.
Однако не стоит ограничиваться только этими десятью категориями — угрозы постоянно эволюционируют, а хакеры находят новые способы обхода защитных механизмов. Регулярные проверки, обновления зависимостей, мониторинг логов и использование инструментов анализа уязвимостей помогут держать безопасность вашего приложения на высоком уровне. В конечном счете, чем глубже разработчик понимает безопасность, тем труднее злоумышленникам будет атаковать его продукт.