-
Notifications
You must be signed in to change notification settings - Fork 154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add logging function when failed to authenticate #462
base: master
Are you sure you want to change the base?
Add logging function when failed to authenticate #462
Conversation
It's done. For me, it would make more sense to call a method inside the Two_Factor_Provider class to validate the authentication, which would call the child provider method if ( ! $success ) {
$this->log_failure( $user, $response );
}
return $success; Please, let me know what you think about this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we move the logging to the main conditional here:
two-factor/class-two-factor-core.php
Lines 857 to 867 in a112a0a
if ( true !== $provider->validate_authentication( $user ) ) { | |
do_action( 'wp_login_failed', $user->user_login ); | |
$login_nonce = self::create_login_nonce( $user->ID ); | |
if ( ! $login_nonce ) { | |
wp_die( esc_html__( 'Failed to create a login nonce.', 'two-factor' ) ); | |
} | |
self::login_html( $user, $login_nonce['key'], $_REQUEST['redirect_to'], esc_html__( 'ERROR: Invalid verification code.', 'two-factor' ), $provider ); | |
exit; | |
} |
so that (1) we don't need to modify each provider to add this and (2) providers are not responsible for logging.
We could also update the validate_authentication()
methods to return WP_Error
objects which could provide their own error messages which can then be used by the logger as necessary.
* @param Two_Factor_Provider $this The Provider class used during the authentication. | ||
*/ | ||
if ( apply_filters( 'two_factor_log_failure', true, $user, $code, $log_message, $this ) ) { | ||
error_log( $log_message ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we sent an email to admin_email
instead of logging it the error log by default?
The two_factor_user_login_failed
could be used to send it to other destinations such as error log or custom logging utility as needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There appears to be a few instance where WP core is calling error_log()
so we should be OK to use it too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jeffpaul, @georgestephanis What are your thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only uncertainty is if it's a good idea to include the bad code in this. I don't have any strong opposition, but I'm slightly concerned that if it's one-digit off, or enough times combined with the timestamp being one tick off, something may be able to be reversed.
But I'm fine with email or not (someone trying to brute force the two factor code may be ... uhh ... a lot of emails though) -- but I definitely think error_log should be included.
Also, appreciate the filter so a third-party catcher could also listen for failures for alternate logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe support the admin_email but have it disabled and filterable to enable? I think I'd want it enabled on sites I'm using, but I could see where that could get VERY chatty for someone with a large amount of users?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might also depend on the provider; for TOTP it's probably not uncommon to make a typo, and getting an email for that seems like overkill. Emailing after 3 failed attempts could be more reasonable (as long as there's a cap on how often it's sent), but IMO #477 seems like a better way to address brute force attacks.
If a backup code fails, though, I can see it being more useful to email, because it's very out of the ordinary. Related #476.
WebAuthn keys (#427) could also be worth emailing about, since they should be less prone to accidental failure, especially with user verification/PINs disabled.
I don't think gaining access to several TOTPs would let you reverse the secret, but I'm not sure how useful including it is either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid that moving the logging function out of the providers will not allow many customizations from them. I can think of some examples where it's useful to allow providers to set their own log data:
- An email provider can send the email address used to log in instead of the user ID.
- Define their own "failed amount" before adding the log.
- Adding their own log methods.
That sounds fair.
…On Fri, Oct 14, 2022 at 10:49 AM Jeffrey Paul ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In providers/class-two-factor-provider.php
<#462 (comment)>:
> + do_action( 'two_factor_user_login_failed', $user, $code, $this );
+
+ /* translators: %1$d: the user's ID %2$s: the code used to authenticate */
+ $log_message = sprintf( esc_html__( 'The user with ID %1$d failed to login using the code "%2$s"', 'two-factor' ), $user->ID, esc_html( $code ) );
+
+ /**
+ * This filter is triggered to checke whether it's needed to log the authentication failure.
+ *
+ * @param boolean $should_log Whether or not the authentication failure should be logged.
+ * @param WP_User $user WP_User object of the user trying to login.
+ * @param string|false $code The code used to authenticate, if available.
+ * @param string $log_message The generated log message.
+ * @param Two_Factor_Provider $this The Provider class used during the authentication.
+ */
+ if ( apply_filters( 'two_factor_log_failure', true, $user, $code, $log_message, $this ) ) {
+ error_log( $log_message );
Maybe support the admin_email but have it disabled and filterable to
enable? I think I'd want it enabled on sites I'm using, but I could see
where that could get VERY chatty for someone with a large amount of users?
—
Reply to this email directly, view it on GitHub
<#462 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHFXX7FOCMFLQZ6KZS2DIDWDFXHTANCNFSM6AAAAAAQ4FHI74>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Once #427 lands, logging should be added there too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Lucisu, I left a few suggestions. Feel free to ask questions.
} | ||
|
||
if ( ! $success ) { | ||
$this->log_failure( $user, $response ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can cause an error because $response
is an object, but the function accepts string
as the second argument.
do_action( 'two_factor_user_login_failed', $user, $code, $this ); | ||
|
||
/* translators: %1$d: the user's ID %2$s: the code used to authenticate */ | ||
$log_message = sprintf( esc_html__( 'The user %1$s (ID: %2$d) failed to login using the code "%3$s"', 'two-factor' ), esc_html( $user->user_login ), $user->ID, esc_html( $code ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not need to reveal the user id if we print the user name in the message.
* | ||
* @return void | ||
*/ | ||
public function log_failure( $user, $code = false ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should support only string
as a second param. Is there any benefit we are getting by supporting boolean
as an alternative value?
@@ -305,7 +305,13 @@ public function authentication_page( $user ) { | |||
*/ | |||
public function validate_authentication( $user ) { | |||
$backup_code = isset( $_POST['two-factor-backup-code'] ) ? sanitize_text_field( wp_unslash( $_POST['two-factor-backup-code'] ) ) : false; | |||
return $this->validate_code( $user, filter_var( $backup_code, FILTER_SANITIZE_STRING ) ); | |||
$success = $this->validate_code( $user, filter_var( $backup_code, FILTER_SANITIZE_STRING ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we place logging logic in the core class where we validate the authentication provider:
two-factor/class-two-factor-core.php
Line 1174 in c725c9b
if ( true !== $provider->validate_authentication( $user ) ) { |
Are we getting benefits from adding logging logic here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not worry about the format of logging data or internal; the log function should dump logging data in an error log file.
I've added the method
is_valid_authcode
to the classTwo_Factor_Provider
so providers can use it when failing to validate the authentication.By default, it uses the
error_log
function, but there are actions and filters that can be used to extend it.Related issue: #459