QueryErrors.php in Security Review 8
File
src/Checks/QueryErrors.php
View source
<?php
namespace Drupal\security_review\Checks;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\security_review\Check;
use Drupal\security_review\CheckResult;
class QueryErrors extends Check {
public function getNamespace() {
return 'Security Review';
}
public function getTitle() {
return 'Query errors';
}
public function run() {
if (!$this
->moduleHandler()
->moduleExists('dblog')) {
return $this
->createResult(CheckResult::INFO, [], FALSE);
}
$result = CheckResult::SUCCESS;
$findings = [];
$last_result = $this
->lastResult();
$visible = FALSE;
$query = $this
->database()
->select('watchdog', 'w');
$query
->fields('w', [
'severity',
'type',
'timestamp',
'message',
'variables',
'hostname',
]);
$query
->condition('type', 'php')
->condition('severity', RfcLogLevel::ERROR);
if ($last_result instanceof CheckResult) {
$query
->condition('timestamp', $last_result
->time(), '>=');
}
$db_result = $query
->execute();
$entries = [];
foreach ($db_result as $row) {
if ($row->variables === 'N;') {
$message = $row->message;
}
else {
$message = $this
->t($row->message, unserialize($row->variables));
}
$ip = $row->hostname;
$message_contains_sql = strpos($message, 'SQL') !== FALSE;
$message_contains_select = strpos($message, 'SELECT') !== FALSE;
if ($message_contains_sql && $message_contains_select) {
$entry_for_ip =& $entries[$ip];
if (!isset($entry_for_ip)) {
$entry_for_ip = 0;
}
$entry_for_ip++;
}
}
if (!empty($entries)) {
foreach ($entries as $ip => $count) {
if ($count > 10) {
$findings[] = $ip;
}
}
}
if (!empty($findings)) {
$result = CheckResult::FAIL;
$visible = TRUE;
}
return $this
->createResult($result, $findings, $visible);
}
public function help() {
$paragraphs = [];
$paragraphs[] = $this
->t('Database errors triggered from the same IP may be an artifact of a malicious user attempting to probe the system for weaknesses like SQL injection or information disclosure.');
return [
'#theme' => 'check_help',
'#title' => $this
->t('Abundant query errors from the same IP'),
'#paragraphs' => $paragraphs,
];
}
public function evaluate(CheckResult $result) {
$findings = $result
->findings();
if (empty($findings)) {
return [];
}
$paragraphs = [];
$paragraphs[] = $this
->t('The following IPs were observed with an abundance of query errors.');
return [
'#theme' => 'check_evaluation',
'#paragraphs' => $paragraphs,
'#items' => $result
->findings(),
];
}
public function evaluatePlain(CheckResult $result) {
$findings = $result
->findings();
if (empty($findings)) {
return '';
}
$output = $this
->t('Suspicious IP addresses:') . ":\n";
foreach ($findings as $ip) {
$output .= "\t" . $ip . "\n";
}
return $output;
}
public function getMessage($result_const) {
switch ($result_const) {
case CheckResult::FAIL:
return $this
->t('Query errors from the same IP. These may be a SQL injection attack or an attempt at information disclosure.');
default:
return $this
->t('Unexpected result.');
}
}
}