Кавычки в PHP: как избежать уязвимостей
Кавычки в PHP и их влияние на безопасность
PHP поддерживает несколько типов кавычек: одинарные ('), двойные ("), heredoc и nowdoc. Каждый тип по-разному обрабатывает escape-последовательности и интерполяцию переменных. Неправильное использование может привести к синтаксическим ошибкам, SQL-инъекциям и XSS-атакам. Ниже рассматриваются основные подходы к безопасной работе с кавычками.
Основное эффективное решение: параметризованные запросы и экранирование вывода
Для работы с базами данных рекомендуется использовать подготовленные выражения (prepared statements) через PDO или MySQLi. Это полностью исключает необходимость ручного экранирования кавычек в SQL-запросах. Для вывода данных в HTML применяется функция htmlspecialchars() с флагом ENT_QUOTES, которая преобразует кавычки в безопасные сущности.
Как защитить приложение от SQL-инъекций при построении запросов?
// PDO prepared statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch();
Этот подход автоматически экранирует специальные символы, включая кавычки, в зависимости от драйвера базы данных.
addslashes возможно обойти экранирование через многобайтовые кодировки (например, атака с использованием %bf%27). Использование prepared statement решает эту проблему полностью.
Вариант 1: Одинарные кавычки и функция addslashes
Как экранировать строку вручную, если нет возможности использовать PDO?
$name = \addslashes($_POST['name']);
$sql = "SELECT * FROM users WHERE name = '$name'";
Функция addslashes экранирует одинарные кавычки, двойные кавычки, обратный слеш и нуль-байт. Однако для SQL она недостаточно надёжна (не учитывает кодировку). Используется только в крайнем случае, когда нет доступа к подготовленным выражениям.
\xbf\x27 может интерпретироваться как кавычка, обходя экранирование. Рекомендуется использовать mysql_real_escape_string (устарело) или перейти на PDO.
Вариант 2: Двойные кавычки и интерполяция переменных
Можно ли безопасно использовать двойные кавычки с прямым включением переменных?
$name = "John";
echo "Hello, $name!"; // выведет Hello, John!
Интерполяция удобна, но опасна при включении пользовательских данных. Переменная должна быть предварительно экранирована для контекста вывода. В SQL такие строки уязвимы, так как не происходит экранирования содержимого переменной.
"$array[key]" может вызвать ошибку парсинга. Правильно: "{$array['key']}". Также двойные кавычки внутри строки необходимо экранировать обратным слешем: "He said \"Hello\"".
Вариант 3: Heredoc и Nowdoc для многострочных строк
Как безопасно оформить большой блок текста с сохранением форматирования?
// Heredoc (интерполяция переменных)
$name = 'Alice';
$text = <<
Heredoc работает как двойные кавычки, nowdoc - как одинарные. Для безопасности вывода пользовательских данных в heredoc необходимо предварительно экранировать переменные.
Вариант 4: Форматирование строк с помощью sprintf
Как безопасно собрать строку с подстановкой значений без риска инъекции?
$name = 'Bob';
$age = 30;
$str = sprintf('User %s is %d years old.', $name, $age);
sprintf не выполняет никакого экранирования, поэтому он безопасен только при условии, что последующее использование строки не требует экранирования (например, для отображения). Для SQL всё равно нужно использовать параметризацию.
sprintf для построения SQL-запроса - это та же конкатенация, только с форматированием. Уязвимость остаётся.
Подробные примеры работы с кавычками в PHP
Пример 1. Экранирование одинарных кавычек
$str = 'It\'s a sunny day, isn\'t it?';
echo $str;
It's a sunny day, isn't it?
Пояснение: обратный слеш перед кавычкой экранирует её, позволяя включить апостроф внутрь строки. Если этого не сделать, PHP воспримет кавычку как конец строки и выдаст синтаксическую ошибку.
Пример 2. Интерполяция переменной в двойных кавычках
$color = 'red';
echo "The apple is $color.";
echo "The apple is {$color}."; // тот же результат
The apple is red. The apple is red.
Пояснение: фигурные скобки рекомендуются для сложных выражений или массивов, но не обязательны для простых переменных.
Пример 3. Интерполяция массива в двойных кавычках
$data = ['name' => 'Alice', 'age' => 25];
echo "Name: {$data['name']}, Age: {$data['age']}";
// Ошибка без скобок: echo "Name: $data['name']"; // синтаксическая ошибка
Name: Alice, Age: 25
Пояснение: при обращении к элементу массива с ключом-строкой в двойных кавычках обязательно использовать фигурные скобки и кавычки вокруг ключа. Иначе интерпретатор не сможет разобрать выражение.
Пример 4. Heredoc с переменными
$title = 'Приветствие';
$message = <<
<h1>Приветствие</h1> <p>Добро пожаловать на сайт.</p>
Пояснение: heredoc интерполирует переменные, как двойные кавычки. Удобно для генерации HTML или многострочных сообщений.
Пример 5. Nowdoc (без интерполяции)
$name = 'Bob';
$text = <<<'EOD'
Привет, $name! Эта строка не содержит переменных.
Обратный слеш: \n - не будет интерпретирован.
EOD;
echo $text;
Привет, $name! Эта строка не содержит переменных. Обратный слеш: \n - не будет интерпретирован.
Пояснение: nowdoc работает как одинарные кавычки: никакой интерполяции и escape-последовательностей, кроме экранирования самого апострофа.
Пример 6. Безопасный вывод в HTML с htmlspecialchars
$userInput = '<script>alert("XSS")</script>';
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
<script>alert("XSS")</script>
Пояснение: функция преобразует символы <, >, &, одинарные и двойные кавычки в HTML-сущности. Это предотвращает XSS-атаки.
Пример 7. Сравнение addslashes и prepared statement
// Уязвимый код с addslashes
$input = "1 OR 1=1";
$safe = addslashes($input);
$sql = "SELECT * FROM users WHERE id = '$safe'";
echo $sql;
// Вывод: SELECT * FROM users WHERE id = '1 OR 1=1'
// Но злоумышленник может обойти: $input = "\\' OR 1=1";
// После addslashes -> '\\\' OR 1=1' -> запрос станет неверным.
// Безопасный prepared statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $input]);
// Такой код защищён от любых инъекций.
SELECT * FROM users WHERE id = '1 OR 1=1'
Пояснение: хотя addslashes экранирует кавычки, этого недостаточно для безопасности SQL. Prepared statement - единственный надёжный способ.
Пример 8. Использование обратных слешей в двойных кавычках
echo "Строка с \"кавычками\" и \n новой строкой.";
Строка с "кавычками" и новой строкой.
Пояснение: в двойных кавычках экранируются не только кавычки, но и управляющие последовательности: \n (новая строка), \t (табуляция), \$ (знак доллара). В одинарных кавычках экранируются только \\ и \'.
Пример 9. Проблема с неверным закрытием heredoc
// Неверно: пробел перед точкой с запятой
$str = <<<EOT
Текст
EOT ;
// Parse error: syntax error, unexpected end of file
// Верно:
$str = <<<EOT
Текст
EOT;
Пояснение: идентификатор закрытия heredoc (здесь EOT) должен располагаться в самом начале строки, без пробелов или табуляции, и сразу после него должна идти точка с запятой (или конец файла). Нарушение этого правила приводит к синтаксической ошибке.