Skip to content
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

Issue-49: Add nonce to wp-login #56

Merged
merged 21 commits into from
Dec 22, 2023

Conversation

anubisthejackle
Copy link
Contributor

@anubisthejackle anubisthejackle commented Nov 30, 2023

Summary

This PR adds a nonce to wp-login.php as described in issue #49.

Notes for reviewers

Please consider the potential overhead to the login process and the requirement for the page to be uncached when reviewing the changes.

Changelog entries

Added

  • Added nonce to wp-login.php to prevent CSRF attacks and mitigate brute-force attacks, as described in issue #49.

Changed

Deprecated

Removed

Fixed

Security

Summary by CodeRabbit

  • New Features

    • Introduced a new security feature that adds nonce verification to the login process.
  • Bug Fixes

    • Improved error handling and user validation during login.
  • Tests

    • Added tests to ensure the login nonce feature works correctly and securely.
  • Refactor

    • Adjusted the visibility of certain class constants for better encapsulation.
  • Documentation

    • Updated documentation to reflect the addition of the login nonce feature and related methods.

Copy link

coderabbitai bot commented Nov 30, 2023

Walkthrough

The AlleyVate WordPress plugin has introduced a new Login_Nonce class to enhance security on the login form by adding a nonce field. This nonce implementation includes various methods for nonce initialization, validation, and error handling. The changes also include a new feature addition to the plugin's load process and corresponding tests to ensure the functionality works as expected. The nonce feature aims to mitigate CSRF and brute-force attacks, aligning with WordPress security best practices.

Changes

File Path Change Summary
.../features/class-login-nonce.php Added Login_Nonce class with methods for nonce handling in login forms.
.../load.php Included new 'login_nonce' feature in the available_features function.
.../features/test-login-nonce.php Created Test_Login_Nonce class for testing the nonce functionality in login forms.

Related issues

  • Add nonce to wp-login #49: The changes in this PR seem to address the issue's objective by adding a nonce to the wp-login form, which is a security measure that the issue advocates for. The implementation of a proper nonce as described in the issue could be linked to the Login_Nonce class functionality.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on X ?


Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • You can reply to a review comment from CodeRabbit.
  • You can also chat with CodeRabbit bot directly around the specific lines of code or files in the PR by tagging @coderabbitai in a new comment.
  • You can tag @coderabbitai in a PR comment and ask questions about the PR and the codebase.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

@anubisthejackle anubisthejackle marked this pull request as ready for review November 30, 2023 17:03
@anubisthejackle anubisthejackle requested a review from a team as a code owner November 30, 2023 17:03
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 4665bec and 3fcfd15.
Files selected for processing (2)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
  • src/alley/wp/alleyvate/load.php (1 hunks)
Additional comments: 2
src/alley/wp/alleyvate/load.php (2)
  • 36-36: The feature key 'login_none' in the summary does not match the actual key 'login_nonce' in the code. Please verify the correct feature key to ensure consistency in documentation and code.
- 'login_none'                    => new Features\Login_Nonce(),
+ 'login_nonce'                   => new Features\Login_Nonce(),
  • 36-36: The addition of the 'login_nonce' feature to the list of features is correctly implemented.

*/
private function generate_random_nonce_name( string $name ): string {
$parts = [ $name ];
if ( ! empty( $_SERVER ) ) { // phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of $_SERVER variables should be accompanied by isset checks to prevent PHP notices if these variables are not set.

- if ( ! empty( $_SERVER ) ) {
+ if ( isset( $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['HTTP_CLIENT_IP'] ) ) {

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
if ( ! empty( $_SERVER ) ) { // phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
if ( isset( $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['HTTP_CLIENT_IP'] ) ) { // phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders

Using user-controlled headers such as 'HTTP_X_FORWARDED_FOR' and 'HTTP_CLIENT_IP' in the nonce generation process could be a security risk if these headers are spoofed. Consider using server-side generated data that cannot be manipulated by the user.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 3fcfd15 and 9c246be.
Files selected for processing (1)
  • src/alley/wp/alleyvate/load.php (1 hunks)
Additional comments: 1
src/alley/wp/alleyvate/load.php (1)
  • 33-39: The addition of the 'login_nonce' feature to the list of features in the load function appears to be correctly implemented. Ensure that the Features\Login_Nonce class exists, is correctly namespaced, and implements the necessary Feature interface with a boot method.

@jakewrfoster jakewrfoster linked an issue Nov 30, 2023 that may be closed by this pull request
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 9c246be and 10f79a2.
Files selected for processing (1)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)

return;
}

echo sprintf( '<meta http-equiv="refresh" content="%d">', esc_attr( $this->nonce_timeout ) );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nonce timeout is stored in minutes but is used directly in the meta refresh tag, which expects seconds.

- echo sprintf( '<meta http-equiv="refresh" content="%d">', esc_attr( $this->nonce_timeout ) );
+ echo sprintf( '<meta http-equiv="refresh" content="%d">', esc_attr( $this->nonce_timeout * 60 ) );

Committable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
echo sprintf( '<meta http-equiv="refresh" content="%d">', esc_attr( $this->nonce_timeout ) );
echo sprintf( '<meta http-equiv="refresh" content="%d">', esc_attr( $this->nonce_timeout * 60 ) );

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 10f79a2 and 73cabe0.
Files selected for processing (1)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)

* and we know we have a valid login attempt, remove all the filters
* so we don't risk bypassing our nonce validation down the line.
*/
remove_all_filters( 'authenticate' );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of remove_all_filters on the 'authenticate' hook could still pose a security risk by removing all authentication checks. Please ensure that this action does not compromise the security of the system.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 73cabe0 and 0ab20f3.
Files selected for processing (1)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
Additional comments: 1
src/alley/wp/alleyvate/features/class-login-nonce.php (1)
  • 65-65: The previous comment about the nonce timeout being stored in minutes but used directly in the meta refresh tag, which expects seconds, is incorrect. The code correctly stores the nonce timeout in seconds.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 0ab20f3 and 9b3e750.
Files selected for processing (2)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
  • tests/alley/wp/alleyvate/features/test-login-nonce.php (1 hunks)
Additional comments: 2
tests/alley/wp/alleyvate/features/test-login-nonce.php (2)
  • 46-64: The test test_logins_require_nonce correctly simulates a login attempt without a nonce and asserts that the result is a WP_Error with the expected error code. This ensures that the nonce validation is enforced during login.

  • 70-90: The test test_logins_work_with_nonce properly simulates a login attempt with a valid nonce and asserts that the result is a WP_User object, indicating a successful login. This confirms that the nonce functionality allows logins when the nonce is correct.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 9b3e750 and 07c92e2.
Files selected for processing (2)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
  • tests/alley/wp/alleyvate/features/test-login-nonce.php (1 hunks)
Additional comments: 3
src/alley/wp/alleyvate/features/class-login-nonce.php (1)
  • 60-62: The addition of a meta refresh tag to the login page is a good practice to ensure the page refreshes after the nonce timeout, preventing the use of expired nonces.
tests/alley/wp/alleyvate/features/test-login-nonce.php (2)
  • 65-66: The test test_logins_require_nonce correctly asserts that a WP_Error is returned when the nonce is not provided, and the error code is 'nonce_failure'. This ensures that the login process requires a nonce for authentication.

  • 94-95: The test test_logins_work_with_nonce correctly asserts that a WP_User object is returned when a valid nonce is provided, indicating a successful login.

tests/alley/wp/alleyvate/features/test-login-nonce.php Outdated Show resolved Hide resolved
src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 5

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 07c92e2 and b482a32.
Files selected for processing (2)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
  • tests/alley/wp/alleyvate/features/test-login-nonce.php (1 hunks)

src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
tests/alley/wp/alleyvate/features/test-login-nonce.php Outdated Show resolved Hide resolved
tests/alley/wp/alleyvate/features/test-login-nonce.php Outdated Show resolved Hide resolved
tests/alley/wp/alleyvate/features/test-login-nonce.php Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between b482a32 and 97dffa2.
Files selected for processing (1)
  • tests/alley/wp/alleyvate/features/test-login-nonce.php (1 hunks)
Additional comments: 4
tests/alley/wp/alleyvate/features/test-login-nonce.php (4)
  • 51-68: The previous comment about resetting $_POST to its original state after the test is still valid and has not been addressed in the provided hunk.

  • 62-68: The previous comment about improving the test by asserting the contents of the error message or the WP_Error object is still valid and has not been addressed in the provided hunk.

  • 74-101: The previous comment about resetting $_POST to its original state after the test is still valid and has not been addressed in the provided hunk.

  • 71-101: The previous comment about checking that the nonce field is present in the login form HTML output is still valid and has not been addressed in the provided hunk.

@kevinfodness
Copy link
Member

@anubisthejackle this is pretty close - see my comment about the log in button name, and I like your suggestion about not making the nonce name random. Let me know when that's done and I'll re-review

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 3

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 97dffa2 and 567f40f.
Files selected for processing (2)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
  • tests/alley/wp/alleyvate/features/test-login-nonce.php (1 hunks)
Additional comments: 1
tests/alley/wp/alleyvate/features/test-login-nonce.php (1)
  • 83-111: The test test_logins_work_with_nonce correctly tests the nonce functionality by adding and then removing a filter, and asserting the HTTP response code. This ensures that a login with a valid nonce is successful.

@anubisthejackle
Copy link
Contributor Author

@kevinfodness I've added those changes now.

* Initializes the nonce fields. Is only run on `login_init` to restrict nonce data to login page.
*/
public static function action__add_nonce_life_filter(): void {
add_filter( 'nonce_life', fn() => self::NONCE_TIMEOUT );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this check the nonce name & action and only set the timeout for this particular nonce? This way it could avoid changing the nonce timeout of any other nonces that may be added to this page. (May be an unlikely edge case, though.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could, but I'm not sure if its necessary, since this is only added on the login_init action, which would restrict it to the login page.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have a hard time imagining the issue here, although being as precise as possible may help prevent some future compatibility issue that we can't predict. I guess possibilities could be other plugins adding a nonce or WP core adding a nonce.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with leaving this as-is. If core ends up adding a nonce to the login screen, this feature would become irrelevant, and we could remove it :)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 567f40f and d37d812.
Files selected for processing (1)
  • src/alley/wp/alleyvate/load.php (1 hunks)
Additional comments: 1
src/alley/wp/alleyvate/load.php (1)
  • 25-31: The addition of the 'login_nonce' feature to the available_features array is consistent with the PR's objective to enhance security. This change should be reviewed in the context of the entire login process to ensure that it integrates smoothly and does not introduce any unintended side effects.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between d37d812 and afba49f.
Files selected for processing (1)
  • src/alley/wp/alleyvate/features/class-login-nonce.php (1 hunks)
Additional comments: 1
src/alley/wp/alleyvate/features/class-login-nonce.php (1)
  • 112-113: Ensure that the http_response_code( 403 ) is set before wp_die() to properly indicate a forbidden action. This is correctly implemented.

src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
Copy link
Member

@kevinfodness kevinfodness left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor things to address before merge, but otherwise 🍣

* Initializes the nonce fields. Is only run on `login_init` to restrict nonce data to login page.
*/
public static function action__add_nonce_life_filter(): void {
add_filter( 'nonce_life', fn() => self::NONCE_TIMEOUT );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with leaving this as-is. If core ends up adding a nonce to the login screen, this feature would become irrelevant, and we could remove it :)

src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
Comment on lines 102 to 105
$nonce = false;
if ( ! empty( $_POST[ self::NONCE_NAME ] ) ) {
$nonce = sanitize_key( $_POST[ self::NONCE_NAME ] );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic below still works if this defaults to empty string:

Suggested change
$nonce = false;
if ( ! empty( $_POST[ self::NONCE_NAME ] ) ) {
$nonce = sanitize_key( $_POST[ self::NONCE_NAME ] );
}
$nonce = sanitize_key( $_POST[ self::NONCE_NAME ] ?? '' );

src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
src/alley/wp/alleyvate/features/class-login-nonce.php Outdated Show resolved Hide resolved
@anubisthejackle anubisthejackle merged commit ee7a376 into main Dec 22, 2023
7 checks passed
@anubisthejackle anubisthejackle deleted the feature/issue-49/add-nonce-to-wp-login branch December 22, 2023 15:35
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.

Add nonce to wp-login
4 participants