class QueryErrors in Security Review 8
Checks for abundant query errors.
Hierarchy
- class \Drupal\security_review\Check uses DependencySerializationTrait, StringTranslationTrait
- class \Drupal\security_review\Checks\QueryErrors
Expanded class hierarchy of QueryErrors
1 file declares its use of QueryErrors
- security_review.module in ./
security_review.module - Site security review and reporting Drupal module.
File
- src/
Checks/ QueryErrors.php, line 12
Namespace
Drupal\security_review\ChecksView source
class QueryErrors extends Check {
/**
* {@inheritdoc}
*/
public function getNamespace() {
return 'Security Review';
}
/**
* {@inheritdoc}
*/
public function getTitle() {
return 'Query errors';
}
/**
* {@inheritdoc}
*/
public function run() {
// If dblog is not enabled return with hidden INFO.
if (!$this
->moduleHandler()
->moduleExists('dblog')) {
return $this
->createResult(CheckResult::INFO, [], FALSE);
}
$result = CheckResult::SUCCESS;
$findings = [];
$last_result = $this
->lastResult();
$visible = FALSE;
// Prepare the query.
$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) {
// Only check entries that got recorded since the last run of the check.
$query
->condition('timestamp', $last_result
->time(), '>=');
}
// Execute the query.
$db_result = $query
->execute();
// Count the number of query errors per IP.
$entries = [];
foreach ($db_result as $row) {
// Get the message.
if ($row->variables === 'N;') {
$message = $row->message;
}
else {
$message = $this
->t($row->message, unserialize($row->variables));
}
// Get the IP.
$ip = $row->hostname;
// Search for query errors.
$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++;
}
}
// Filter the IPs with more than 10 query errors.
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);
}
/**
* {@inheritdoc}
*/
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,
];
}
/**
* {@inheritdoc}
*/
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(),
];
}
/**
* {@inheritdoc}
*/
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;
}
/**
* {@inheritdoc}
*/
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.');
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
Check:: |
protected | property | The configuration storage for this check. | |
Check:: |
protected | property | The service container. | |
Check:: |
protected | property | Settings handler for this check. | |
Check:: |
protected | property | The State system. | |
Check:: |
protected | property | The check's prefix in the State system. | |
Check:: |
protected | function | Returns the Security Review Checklist service. | |
Check:: |
protected | function | Returns the Config factory. | |
Check:: |
protected | function | Returns the service container. | |
Check:: |
public | function | Creates a new CheckResult for this Check. | |
Check:: |
protected | function | Returns the current Drupal user. | |
Check:: |
protected | function | Returns the database connection. | |
Check:: |
public | function | Enables the check. Has no effect if the check was not skipped. | |
Check:: |
protected | function | Returns the entity type manager. | |
Check:: |
public | function | Returns the namespace of the check. | |
Check:: |
public | function | Returns the machine name of the check. | 5 |
Check:: |
final public | function | Returns the identifier constructed using the namespace and title values. | |
Check:: |
public | function | Returns whether the check is skipped. Checks are not skipped by default. | |
Check:: |
protected | function | Returns the Drupal Kernel. | |
Check:: |
public | function | Returns the last stored result of the check. | |
Check:: |
public | function | Returns the timestamp the check was last run. | |
Check:: |
protected | function | Returns the module handler. | |
Check:: |
public | function | Same as run(), but used in CLI context such as Drush. | 2 |
Check:: |
protected | function | Returns the Security Review Security service. | |
Check:: |
protected | function | Returns the Security Review service. | |
Check:: |
public | function | Returns the check-specific settings' handler. | |
Check:: |
public | function | Marks the check as skipped. | |
Check:: |
public | function | Returns the user the check was skipped by. | |
Check:: |
public | function | Returns the timestamp the check was last skipped on. | |
Check:: |
public | function | Stores a result in the state system. | |
Check:: |
public | function | Returns whether the findings should be stored or reproduced when needed. | 2 |
Check:: |
public | function | Initializes the configuration storage and the settings handler. | 2 |
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
QueryErrors:: |
public | function |
Returns the evaluation page of a result. Overrides Check:: |
|
QueryErrors:: |
public | function |
Evaluates a CheckResult and returns a plaintext output. Overrides Check:: |
|
QueryErrors:: |
public | function |
Converts a result integer to a human-readable result message. Overrides Check:: |
|
QueryErrors:: |
public | function |
Returns the namespace of the check. Overrides Check:: |
|
QueryErrors:: |
public | function |
Returns the human-readable title of the check. Overrides Check:: |
|
QueryErrors:: |
public | function |
Returns the check-specific help page. Overrides Check:: |
|
QueryErrors:: |
public | function |
The actual procedure of carrying out the check. Overrides Check:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |