You are here

public function FailedLogins::run in Security Review 8

The actual procedure of carrying out the check.

Return value

\Drupal\security_review\CheckResult The result of running the check.

Overrides Check::run

File

src/Checks/FailedLogins.php, line 31

Class

FailedLogins
Checks for abundant failed logins.

Namespace

Drupal\security_review\Checks

Code

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', 'user')
    ->condition('severity', RfcLogLevel::NOTICE)
    ->condition('message', 'Login attempt failed from %ip.');
  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 failed logins per IP.
  $entries = [];
  foreach ($db_result as $row) {
    $ip = unserialize($row->variables)['%ip'];
    $entry_for_ip =& $entries[$ip];
    if (!isset($entry_for_ip)) {
      $entry_for_ip = 0;
    }
    $entry_for_ip++;
  }

  // Filter the IPs with more than 10 failed logins.
  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);
}