| @@ -141,9 +141,9 @@ | ||
| 141 | 141 | * |
| 142 | 142 | * Gate checks (in order): |
| 143 | 143 | * 1. Module master switch (modules.activity_log) |
| 144 | 144 | * 2. Per-type flag via $type_flag_map (types not in the map always pass) |
| 145 | - * 3. Sub-check: failed logins respect log_failed_logins | |
| 145 | + * 3. Login: each of its two checkboxes governs its own half (see below) | |
| 146 | 146 | * 4. User/IP exclusions |
| 147 | 147 | * |
| 148 | 148 | * @param string $type Event type. |
| 149 | 149 | * @param string $action Event action. |
| @@ -160,9 +160,9 @@ | ||
| 160 | 160 | |
| 161 | 161 | // Gate 2: Per-type flag |
| 162 | 162 | $current_options = $this->get_current_options(); |
| 163 | 163 | |
| 164 | - if ( isset( self::$type_flag_map[ $type ] ) ) { | |
| 164 | + if ( isset( self::$type_flag_map[ $type ] ) && 'login' !== $type ) { | |
| 165 | 165 | $flag = self::$type_flag_map[ $type ]; |
| 166 | 166 | if ( empty( $current_options[ $flag ] ) ) { |
| 167 | 167 | return false; |
| 168 | 168 | } |
| @@ -167,11 +167,35 @@ | ||
| 167 | 167 | return false; |
| 168 | 168 | } |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | - // Gate 3: Failed logins sub-check | |
| 172 | - if ( 'login' === $type && in_array( $action, array( 'failed', 'lockout' ), true ) ) { | |
| 173 | - if ( empty( $current_options['log_failed_logins'] ) ) { | |
| 171 | + /* | |
| 172 | + * Gate 3: login is the one type with two checkboxes, offered side by | |
| 173 | + * side as "Successful logins" and "Failed login attempts". Until 2.11.10 | |
| 174 | + * the per-type gate above cut first, so unchecking the first one also | |
| 175 | + * silenced the second and with it everything security relevant this type | |
| 176 | + * carries: failed attempts, lockouts, logins blocked by a forced reset | |
| 177 | + * and the probes of the hidden wp-admin and login. The audit alerts of | |
| 178 | + * the login category went quiet at the same time, because they only fire | |
| 179 | + * for events that get stored, so a site under attack looked calm on both | |
| 180 | + * screens. Found by the file-by-file review of 2.11.10. | |
| 181 | + * | |
| 182 | + * Each checkbox governs its own half now: a successful login answers to | |
| 183 | + * log_logins, and everything else about login, which is attack traffic, | |
| 184 | + * answers to log_failed_logins. | |
| 185 | + */ | |
| 186 | + if ( 'login' === $type ) { | |
| 187 | + /* | |
| 188 | + * Written as the list of what is attack traffic, not as "everything | |
| 189 | + * that is not a success": login_url_notified is an administrative | |
| 190 | + * notice and answering to the failed-attempts checkbox made it | |
| 191 | + * disappear for anyone who had that one unchecked, which is a record | |
| 192 | + * 2.11.9 did keep. Found by the cross review of 2.11.10. | |
| 193 | + */ | |
| 194 | + $attack = array( 'failed', 'lockout', 'lockout_blocked', 'hidden_admin_access', 'hidden_login_access', 'force_reset_login_blocked' ); | |
| 195 | + $flag = in_array( $action, $attack, true ) ? 'log_failed_logins' : 'log_logins'; | |
| 196 | + | |
| 197 | + if ( empty( $current_options[ $flag ] ) ) { | |
| 174 | 198 | return false; |
| 175 | 199 | } |
| 176 | 200 | } |
| 177 | 201 | |