More updates to stop using dev or alpha or beta versions.
[yaffs-website] / web / modules / contrib / security_review / src / Checks / FailedLogins.php
1 <?php
2
3 namespace Drupal\security_review\Checks;
4
5 use Drupal\Core\Logger\RfcLogLevel;
6 use Drupal\security_review\Check;
7 use Drupal\security_review\CheckResult;
8
9 /**
10  * Checks for abundant failed logins.
11  */
12 class FailedLogins extends Check {
13
14   /**
15    * {@inheritdoc}
16    */
17   public function getNamespace() {
18     return 'Security Review';
19   }
20
21   /**
22    * {@inheritdoc}
23    */
24   public function getTitle() {
25     return 'Failed logins';
26   }
27
28   /**
29    * {@inheritdoc}
30    */
31   public function run() {
32     // If dblog is not enabled return with hidden INFO.
33     if (!$this->moduleHandler()->moduleExists('dblog')) {
34       return $this->createResult(CheckResult::INFO, [], FALSE);
35     }
36
37     $result = CheckResult::SUCCESS;
38     $findings = [];
39     $last_result = $this->lastResult();
40     $visible = FALSE;
41
42     // Prepare the query.
43     $query = $this->database()->select('watchdog', 'w');
44     $query->fields('w', [
45       'severity',
46       'type',
47       'timestamp',
48       'message',
49       'variables',
50       'hostname',
51     ]);
52     $query->condition('type', 'user')
53       ->condition('severity', RfcLogLevel::NOTICE)
54       ->condition('message', 'Login attempt failed from %ip.');
55     if ($last_result instanceof CheckResult) {
56       // Only check entries that got recorded since the last run of the check.
57       $query->condition('timestamp', $last_result->time(), '>=');
58     }
59
60     // Execute the query.
61     $db_result = $query->execute();
62
63     // Count the number of failed logins per IP.
64     $entries = [];
65     foreach ($db_result as $row) {
66       $ip = unserialize($row->variables)['%ip'];
67       $entry_for_ip = &$entries[$ip];
68
69       if (!isset($entry_for_ip)) {
70         $entry_for_ip = 0;
71       }
72       $entry_for_ip++;
73     }
74
75     // Filter the IPs with more than 10 failed logins.
76     if (!empty($entries)) {
77       foreach ($entries as $ip => $count) {
78         if ($count > 10) {
79           $findings[] = $ip;
80         }
81       }
82     }
83
84     if (!empty($findings)) {
85       $result = CheckResult::FAIL;
86       $visible = TRUE;
87     }
88
89     return $this->createResult($result, $findings, $visible);
90   }
91
92   /**
93    * {@inheritdoc}
94    */
95   public function help() {
96     $paragraphs = [];
97     $paragraphs[] = $this->t('Failed login attempts from the same IP may be an artifact of a malicious user attempting to brute-force their way onto your site as an authenticated user to carry out nefarious deeds.');
98
99     return [
100       '#theme' => 'check_help',
101       '#title' => $this->t('Abundant failed logins from the same IP'),
102       '#paragraphs' => $paragraphs,
103     ];
104   }
105
106   /**
107    * {@inheritdoc}
108    */
109   public function evaluate(CheckResult $result) {
110     $findings = $result->findings();
111     if (empty($findings)) {
112       return [];
113     }
114
115     $paragraphs = [];
116     $paragraphs[] = $this->t('The following IPs were observed with an abundance of failed login attempts.');
117
118     return [
119       '#theme' => 'check_evaluation',
120       '#paragraphs' => $paragraphs,
121       '#items' => $result->findings(),
122     ];
123   }
124
125   /**
126    * {@inheritdoc}
127    */
128   public function evaluatePlain(CheckResult $result) {
129     $findings = $result->findings();
130     if (empty($findings)) {
131       return '';
132     }
133
134     $output = $this->t('Suspicious IP addresses:') . ":\n";
135     foreach ($findings as $ip) {
136       $output .= "\t" . $ip . "\n";
137     }
138
139     return $output;
140   }
141
142   /**
143    * {@inheritdoc}
144    */
145   public function getMessage($result_const) {
146     switch ($result_const) {
147       case CheckResult::FAIL:
148         return $this->t('Failed login attempts from the same IP. These may be a brute-force attack to gain access to your site.');
149
150       default:
151         return $this->t('Unexpected result.');
152     }
153   }
154
155 }