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

feat: rate limit checkout attempts #3678

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from

Conversation

dkoo
Copy link
Contributor

@dkoo dkoo commented Jan 15, 2025

All Submissions:

Changes proposed in this Pull Request:

Adds support for rate limiting checkout attempts by user. If a NEWSPACK_CHECKOUT_RATE_LIMIT environment constant is defined as an integer, checkout attempts that occur within that number of seconds of a prior checkout attempt by the same user (if logged in) or IP (if not logged in) will be rejected before WooCommerce sends payment info to any payment gateways.

If WooCommerce detects any other checkout validation errors that would prevent the checkout from occurring (such as missing required fields), we won't count this as a checkout attempt, so that readers can attempt successive checkouts in order to correct errors without being rate limited. So in practice, only successful transactions and checkout requests which would result in declined payments should be rate-limited.

The mechanism used is transients: when a checkout request is submitted and has no validation errors, a transient named with the hashed user ID or IP with a value of the UNIX timestamp of the request is created with an expiration time matching NEWSPACK_CHECKOUT_RATE_LIMIT number of seconds (unless NEWSPACK_CHECKOUT_RATE_LIMIT is 0, which is functionally be the same as not defining the constant). If another checkout request is submitted and the transient both exists and is within NEWSPACK_CHECKOUT_RATE_LIMIT number of seconds of the new request, the transient value and expiration are updated and the request is rejected.

Also adds rate-limiting for adding new payment methods in My Account, but because of how this feature is split between the main WooCommerce plugin and many payment gateway extension plugins, it doesn't necessarily rate limit declined payment methods (thus still allowing for card testing with some payment gateways). This will need to be handled in a separate PR, possibly via JS since each payment method behaves differently when deciding when/whether to contact the payment processor.

How to test the changes in this Pull Request:

  1. Check out this branch. Optionally disable reCAPTCHA in Newspack > Connections to make testing easier.
  2. In wp-config.php, define NEWSPACK_CHECKOUT_RATE_LIMIT as something long enough for manual testing like 60 or 90.
  3. As an anonymous reader in a new browser session, start a Woo checkout modal checkout. After completing the first screen (payment details), click "Back" from the second screen and confirm that you can proceed to the second screen again without being rate-limited (validation-only checkout requests are not rate-limited).
  4. Attempt to complete the checkout but with invalid input: for example, by using dev tools to edit the value of the email address field to an invalid email address, then submitting. Confirm that the response includes the error(s) about the invalid input, but not anything about rate limiting. You can also use WP CLI or error logging to confirm that a new transient is not created from this invalid checkout request.
  5. Complete the checkout with valid inputs, but use a test Stripe payment which will be declined.
  6. After getting the "payment declined" error message and within the number of seconds defined in step 2, complete checkout again with a valid/non-declined payment method and confirm that you get an error message to Please wait a moment before trying to complete this transaction again.
  7. Wait until the number of seconds elapses and then submit once more, and confirm that the transaction completes successfully.
  8. Repeat with a new checkout session while logged into a reader account with saved payment details.
  9. Repeat again with a regular (non-modal) Woo checkout.

Testing rate-limiting of adding new payment methods

  1. Log into My Account as a reader and visit the Payment Methods menu link to add a new payment method (alternatively, just plug in /my-account/add-payment-method since this URL isn't blocked even if the menu item is hidden).
  2. Add a new valid payment method, then try to immediately add another new payment method and confirm that the request is rejected with a Please wait a moment before trying to add a new payment method error message. (Note that the error message may flash and disappear after a moment, but this seems to be a Woo/My Account bug and isn't related to the changes in this PR.)
  3. Wait until the number of seconds elapses and then try again, and confirm that the payment method is successfully added.

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable?
  • Have you successfully ran tests with your changes locally?

@dkoo dkoo added the [Status] Needs Review The issue or pull request needs to be reviewed label Jan 15, 2025
@dkoo dkoo self-assigned this Jan 15, 2025
@dkoo dkoo requested a review from a team as a code owner January 15, 2025 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Status] Needs Review The issue or pull request needs to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant