Правила безопасности (Security)¶
Правила безопасности обнаруживают паттерны, которые могут привести к уязвимостям в вашем коде. Эти правила направлены на поиск учетных данных, секретов и другой конфиденциальной информации, которая не должна быть захардкожена в исходном коде.
Ограничения области применения
Эти правила безопасности обнаруживают только паттерны прямого использования суперглобальных переменных ($_GET, $_POST и т.д.). Они НЕ выполняют taint-анализ и не отслеживают поток данных через переменные. Для более глубокого анализа безопасности с отслеживанием потоков данных рекомендуем специализированные инструменты: PHPStan Security, Psalm Taint Analysis или SonarQube.
Захардкоженные учетные данные (Hardcoded Credentials)¶
Идентификатор правила: security.hardcoded-credentials
Серьезность: Error
Что измеряет¶
Обнаруживает захардкоженные учетные данные в PHP-коде -- строковые литералы, присвоенные переменным, свойствам, константам, ключам массивов и параметрам с именами, связанными с учетными данными.
Паттерны обнаружения:
- Присвоение переменной:
$password = 'secret'; - Элемент массива:
['api_key' => 'abc123'] - Константа класса:
const DB_PASSWORD = 'root'; - Вызов
define():define('API_KEY', '...'); - Значение по умолчанию свойства:
private string $token = 'x'; - Значение по умолчанию параметра:
function f($pwd = 'root')
Сопоставление чувствительных имен:
- Суффиксные слова (совпадение в любом месте):
password,passwd,pwd,secret,credential(s) - Составные с "key" (только с квалификатором):
apiKey,secretKey,privateKey,encryptionKey,signingKey,authKey,accessKey - Составные с "token" (только с квалификатором):
authToken,accessToken,bearerToken,apiToken,refreshToken
Имена вроде $passwordHash, $tokenStorage, $cacheKey, OPTION_PASSWORD исключаются (неучетный контекст).
Фильтрация значений: пустые строки, строки короче 4 символов и строки из одинаковых символов (***, xxx) пропускаются.
Пример¶
class DatabaseConfig
{
// Плохо: учетные данные захардкожены напрямую
private const DB_PASSWORD = 'super_secret_123';
private string $apiKey = 'sk-live-abc123def456';
public function connect(string $password = 'root'): void
{
$apiToken = 'ghp_xxxxxxxxxxxxxxxxxxxx';
// ...
}
}
Как исправить¶
-
Используйте переменные окружения:
-
Используйте менеджер секретов (Vault, AWS Secrets Manager и т.д.)
-
Используйте конфигурацию фреймворка:
Внимание
Захардкоженные учетные данные в исходном коде -- это серьезный риск безопасности. Они могут утечь через систему контроля версий, логи, сообщения об ошибках или скомпилированные артефакты.
SQL-инъекция (SQL Injection)¶
Идентификатор правила: security.sql-injection
Серьезность: Error
Что измеряет¶
Обнаруживает использование суперглобальных переменных ($_GET, $_POST, $_REQUEST, $_COOKIE) в SQL-контексте, где несанитизированный пользовательский ввод может привести к SQL-инъекции.
Обнаруживаемые паттерны:
- Конкатенация строки с SQL-ключевыми словами:
"SELECT * FROM users WHERE id = " . $_GET['id'] - Аргумент небезопасной функции запроса:
mysql_query($_GET['q']),mysqli_query($conn, $_POST['sql']),pg_query($_REQUEST['q']) sprintf()с SQL-шаблоном:sprintf("SELECT * FROM users WHERE id = %s", $_GET['id'])
Пример¶
// Плохо: суперглобальная переменная напрямую в SQL-запросе
$result = mysqli_query($conn, "SELECT * FROM users WHERE id = " . $_GET['id']);
// Плохо: суперглобальная переменная как аргумент функции запроса
$result = pg_query("SELECT * FROM orders WHERE status = '" . $_POST['status'] . "'");
// Плохо: sprintf с несанитизированным вводом
$sql = sprintf("DELETE FROM sessions WHERE token = '%s'", $_COOKIE['session']);
Как исправить¶
Используйте параметризованные запросы (prepared statements) вместо конкатенации строк:
// Хорошо: подготовленный запрос PDO
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
// Хорошо: подготовленный запрос mysqli
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $_GET['id']);
$stmt->execute();
Внимание
SQL-инъекция -- одна из самых опасных и распространённых уязвимостей веб-приложений. Никогда не подставляйте пользовательский ввод в SQL-строки конкатенацией, даже если считаете его "безопасным".
XSS (Cross-Site Scripting)¶
Идентификатор правила: security.xss
Серьезность: Error
Что измеряет¶
Обнаруживает операторы echo/print, выводящие суперглобальные переменные ($_GET, $_POST, $_REQUEST, $_COOKIE) без должной санитизации, что может привести к межсайтовому скриптингу (XSS).
Нарушение не сообщается, если значение обёрнуто в функцию санитизации:
htmlspecialchars()htmlentities()strip_tags()intval()- приведение типов
(int)или(float)
Пример¶
// Плохо: несанитизированная суперглобальная переменная выводится напрямую
echo $_GET['name'];
print("Привет, " . $_POST['username']);
// Хорошо: санитизированный вывод
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
echo (int) $_GET['page'];
Как исправить¶
Всегда санитизируйте пользовательский ввод перед выводом в HTML:
// Используйте htmlspecialchars с ENT_QUOTES и явной кодировкой
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
// Для целочисленных значений приводите к int
echo (int) $_GET['id'];
// В шаблонах используйте автоэкранирование вашего фреймворка (Twig, Blade и т.д.)
Внимание
XSS позволяет злоумышленнику внедрить произвольный скрипт в страницу, которую видят другие пользователи. Всегда экранируйте вывод, даже во "внутренних" админ-панелях.
Внедрение команд (Command Injection)¶
Идентификатор правила: security.command-injection
Серьезность: Error
Что измеряет¶
Обнаруживает суперглобальные переменные ($_GET, $_POST, $_REQUEST, $_COOKIE), переданные аргументами функциям выполнения shell-команд, что может привести к внедрению команд.
Обнаруживаемые функции: exec(), system(), passthru(), shell_exec(), proc_open(), popen()
Нарушение не сообщается, если значение обёрнуто в:
escapeshellarg()escapeshellcmd()
Пример¶
// Плохо: суперглобальная переменная напрямую передаётся shell-функции
exec("convert " . $_GET['filename'] . " output.png");
system("ping " . $_POST['host']);
$output = shell_exec("grep " . $_REQUEST['pattern'] . " /var/log/app.log");
// Хорошо: значение правильно экранировано
exec("convert " . escapeshellarg($_GET['filename']) . " output.png");
Как исправить¶
-
Используйте
escapeshellarg()для экранирования отдельных аргументов: -
По возможности избегайте shell-команд вовсе. Используйте встроенные функции PHP:
Внимание
Внедрение команд позволяет злоумышленнику выполнять произвольные команды на вашем сервере. Даже с экранированием предпочитайте нативные для PHP альтернативы shell-командам, когда они существуют.
Чувствительные параметры (Sensitive Parameter)¶
Идентификатор правила: security.sensitive-parameter
Серьезность: Warning
Что измеряет¶
Обнаруживает параметры функций и методов с чувствительными именами, у которых отсутствует атрибут #[\SensitiveParameter] (доступен с PHP 8.2). Без этого атрибута чувствительные значения вроде паролей и токенов появляются в открытом виде в stack traces, логах ошибок и отчётах об исключениях.
Сопоставление чувствительных параметров использует ту же политику, что и поиск захардкоженных
учетных данных: самостоятельные слова password, passwd, pwd, secret,
credential и credentials совпадают, а key и token требуют квалифицирующего префикса,
например api, auth, access, private или refresh. Контексты
passwordHash, tokenStorage, cacheKey и OPTION_PASSWORD исключаются.
Пример¶
// Плохо: чувствительный параметр без атрибута
function authenticate(string $password): bool
{
// Если здесь возникнет исключение, $password будет виден в stack trace
// ...
}
// Хорошо: атрибут #[\SensitiveParameter] скрывает значение в stack traces
function authenticate(#[\SensitiveParameter] string $password): bool
{
// ...
}
Как исправить¶
Добавьте атрибут #[\SensitiveParameter] к параметрам с чувствительными именами:
function connect(
#[\SensitiveParameter] string $password,
#[\SensitiveParameter] string $apiKey,
): void {
// ...
}
Примечание
Атрибут #[\SensitiveParameter] доступен начиная с PHP 8.2. Он заменяет значение параметра на SensitiveParameterValue в stack traces, предотвращая случайную утечку секретов в логи и отчеты об ошибках.
Область обнаружения¶
Правила безопасности (sql-injection, xss, command-injection) используют паттерн-ориентированное обнаружение -- они ищут суперглобальные переменные ($_GET, $_POST, $_REQUEST, $_COOKIE), используемые непосредственно в опасных контекстах. Этот подход быстр и дает ноль ложных срабатываний для прямых паттернов, но имеет свои ограничения.
Что обнаруживается¶
Прямые паттерны "суперглобальная переменная в приемнике", включая конкатенацию и интерполяцию строк:
// Все обнаруживается:
echo $_GET['name']; // прямой вывод
echo "Hello " . $_POST['user']; // конкатенация
echo "Welcome {$_GET['name']}"; // интерполяция
mysqli_query($conn, "SELECT * FROM t WHERE id=" . $_GET['id']); // аргумент SQL-функции
exec("ping " . $_GET['host']); // аргумент shell-функции
Что НЕ обнаруживается¶
Эти правила не выполняют taint-анализ -- они не могут отслеживать поток данных через переменные, возвращаемые значения функций или свойства объектов:
// НЕ обнаруживается -- значение присвоено промежуточной переменной:
$name = $_GET['name'];
echo $name;
// НЕ обнаруживается -- значение передано через функцию:
function getName() { return $_GET['name']; }
echo getName();
// НЕ обнаруживается -- значение сохранено в объекте:
$request->name = $_POST['name'];
echo $request->name;
// НЕ обнаруживается -- непрямая SQL-инъекция:
$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = " . $id;
Рекомендации¶
Для полноценного анализа безопасности с отслеживанием потоков данных используйте специализированные инструменты совместно с Qualimetrix:
- PHPStan Security Advisories -- расширение PHPStan для проверок безопасности
- Psalm Taint Analysis -- отслеживает «загрязненные» данные через присвоения, вызовы функций и возвращаемые значения
- SonarQube -- коммерческий инструмент с глубоким анализом потоков данных
- Snyk Code -- сканирование безопасности на основе AI с отслеживанием потоков данных
Правила безопасности Qualimetrix лучше всего использовать как первую линию обороны для обнаружения наиболее очевидных паттернов. Они дополняют, но не заменяют специализированные инструменты анализа безопасности.
Конфигурация¶
# qmx.yaml
rules:
security.hardcoded-credentials:
enabled: true # или false для отключения
security.sql-injection:
enabled: true
security.xss:
enabled: true
security.command-injection:
enabled: true
security.sensitive-parameter:
enabled: true
Также можно отключить через CLI: