Skip to content

fix(auth): restore g_csrf_token CSRF validation in Sign In With Google#12797

Closed
adilburaksen wants to merge 1 commit into
google:developfrom
adilburaksen:fix/siwg-restore-g-csrf-token-validation
Closed

fix(auth): restore g_csrf_token CSRF validation in Sign In With Google#12797
adilburaksen wants to merge 1 commit into
google:developfrom
adilburaksen:fix/siwg-restore-g-csrf-token-validation

Conversation

@adilburaksen

Copy link
Copy Markdown

Summary

Restores the g_csrf_token CSRF validation in Authenticator::authenticate_user() that was inadvertently removed in commit 0803e42d (PR #9687, November 2024).

Background

Google's GIS (Google Identity Services) library sets an identical g_csrf_token value in both a cookie and the POST body when the user clicks the Sign In With Google button. The server must compare these two values before processing the credential — this is the CSRF mitigation documented in the GIS server-side verification guide.

The check existed in the original implementation (constant ERROR_INVALID_CSRF_TOKEN and the validation block in authenticate_user), but was dropped without replacement.

Impact Without the Check

An attacker can host a page that auto-submits a valid Google credential via CSRF POST to wp-login.php?action=googlesitekit_auth:

<form method="POST" action="https://victim.com/wp-login.php?action=googlesitekit_auth">
  <input name="credential" value="[ATTACKER_VALID_GOOGLE_JWT]">
</form>
<script>document.forms[0].submit();</script>

Because the plugin never validates g_csrf_token, the server processes the credential. If the attacker's Google account email matches an existing WordPress account, find_user() permanently links the attacker's Google credential to that account — the attacker can then sign in as that user at any time.

Fix

Restores the original two-line validation using hash_equals() (constant-time comparison) and re-adds the ERROR_INVALID_CSRF_TOKEN error code constant.

$csrf_cookie = $input->filter( INPUT_COOKIE, 'g_csrf_token' );
$csrf_post   = $input->filter( INPUT_POST, 'g_csrf_token' );
if ( ! $csrf_cookie || ! $csrf_post || ! hash_equals( $csrf_cookie, $csrf_post ) ) {
    return $this->get_error_redirect_url( self::ERROR_INVALID_CSRF_TOKEN );
}

Testing

  • Normal Sign In With Google flow: GIS library sets g_csrf_token in both cookie and POST body automatically — valid requests pass unchanged.
  • CSRF attempt without the cookie/token: rejected with ERROR_INVALID_CSRF_TOKEN.

PR Author Checklist

  • My code is tested and passes existing unit tests.
  • My code has an appropriate set of unit tests which all pass.
  • My code is backward-compatible with WordPress 5.2 and PHP 7.4.
  • My code follows the WordPress coding standards.
  • My code has proper inline documentation.
  • I have signed the Contributor License Agreement (see https://cla.developers.google.com/).

Do not alter or remove anything below. The following sections will be managed by moderators only.

Code Reviewer Checklist

  • Run the code.
  • Ensure the acceptance criteria are satisfied.

@google-cla

google-cla Bot commented May 26, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

The CSRF token check was present in the original implementation but
was inadvertently removed in commit 0803e42 (PR #9687, Nov 2024).
Google's GIS (Google Identity Services) library sets an identical
`g_csrf_token` value in both a cookie and the POST body; the server
must compare them to prevent cross-site request forgery.

Without this check, an attacker can craft a page that auto-submits a
valid Google credential to the victim's WordPress login endpoint via
CSRF, causing the plugin to authenticate the attacker's account on the
victim's site. If the attacker's Google account email matches an
existing WordPress account, this permanently links the attacker's
credential to that account (account takeover).

Restores the original validation using `hash_equals()` for
constant-time comparison and re-adds the `ERROR_INVALID_CSRF_TOKEN`
error code constant.
@adilburaksen adilburaksen force-pushed the fix/siwg-restore-g-csrf-token-validation branch from f9cb2e7 to 4b295fb Compare May 26, 2026 21:53
@adilburaksen adilburaksen closed this by deleting the head repository May 28, 2026
@adilburaksen

Copy link
Copy Markdown
Author

Re-opening this — the gap it addresses is still present on main: g_csrf_token validation is absent across the whole Sign_In_With_Google module (the CSRF check removed in 0803e42 was never restored), so the cross-site credential POST → account-link flow remains unprotected.

I see the linking flow has since been reworked (the new Profile_Authenticator / existing-user link path), and that rework didn't reintroduce the g_csrf_token cookie-vs-body check. I'm happy to rebase this onto the new Profile_Authenticator structure (or open a fresh PR against it) and add the validation where the credential is now consumed — whichever you prefer. CLA should be sorted on my account now.

@eugene-manuilov could you point me at the right place to land this in the current structure? Happy to add tests and follow the GIS-documented cookie/body comparison.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant