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

Merged
merged 6 commits into from
Jan 23, 2025

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 WooCommerce's built-in WC_Rate_Limiter utility. This offers a lightweight API to check and limit the rate of particular actions by arbitrary ID string.

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
@dkoo
Copy link
Contributor Author

dkoo commented Jan 22, 2025

@miguelpeixe refactored this to use WC_Rate_Limiter in lieu of transients. I think this is ready for a review now!

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

Nice work @dkoo!

Works as described. Just one question regarding whether we should be including successful transactions here.

If I do something like make a successful donation, then decide to purchase a monthly subscription product, I have to wait rate limit duration.

It's probably unlikely readers will do these things back to back like this in such a short time, but just in case, should we not trigger the rate limit on successful transactions? Or is there a scenario where a bad actor might want to flood a site with successful orders?

@dkoo
Copy link
Contributor Author

dkoo commented Jan 23, 2025

Great question! My thinking was that in a card-testing scenario, some of the tested cards might result in successful orders, so we should still rate limit those.

Copy link
Contributor

@chickenn00dle chickenn00dle left a comment

Choose a reason for hiding this comment

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

My thinking was that in a card-testing scenario, some of the tested cards might result in successful orders, so we should still rate limit those.

Makes sense, although I don't think we would see back to back successful tested cards. Rate limiting the failures is probably good enough right? The only scenario where this might be necessary is if the successful card is reused, which I don't think would happen, but am not sure if there is some strategy I'm not aware of.

My concern is we inadvertently create friction for more real readers than we are for these card testing attackers.

I'm not super familiar with these attacks though, so will defer to you and approve this. Its still worth noting we should look out for reports from real readers in the future.

@github-actions github-actions bot added [Status] Approved The pull request has been reviewed and is ready to merge and removed [Status] Needs Review The issue or pull request needs to be reviewed labels Jan 23, 2025
@dkoo
Copy link
Contributor Author

dkoo commented Jan 23, 2025

That's definitely a valid concern. I suspect that a reader is extremely unlikely to attempt two real transactions within a 60-second period, but let's stay vigilant for publisher/reader complaints.

@dkoo dkoo merged commit d275524 into trunk Jan 23, 2025
9 checks passed
@dkoo dkoo deleted the feat/rate-limit-checkouts-payment-methods branch January 23, 2025 17:14
Copy link

Hey @dkoo, good job getting this PR merged! 🎉

Now, the needs-changelog label has been added to it.

Please check if this PR needs to be included in the "Upcoming Changes" and "Release Notes" doc. If it doesn't, simply remove the label.

If it does, please add an entry to our shared document, with screenshots and testing instructions if applicable, then remove the label.

Thank you! ❤️

matticbot pushed a commit that referenced this pull request Jan 23, 2025
# [5.13.0-alpha.1](v5.12.2...v5.13.0-alpha.1) (2025-01-23)

### Bug Fixes

* add supported gateways check ([#3650](#3650)) ([74f7773](74f7773))
* **corrections:** replace deprecated sanitize method ([#3694](#3694)) ([ce50e24](ce50e24))
* remove support for legacy form checkout ([#3691](#3691)) ([46a3c16](46a3c16))
* **wcs:** expire manual subscriptions after on-hold duration ([#3681](#3681)) ([658416c](658416c))

### Features

* add custom bylines ([#3667](#3667)) ([3f45a6f](3f45a6f))
* rate limit checkout attempts ([#3678](#3678)) ([d275524](d275524))
@matticbot
Copy link
Contributor

🎉 This PR is included in version 5.13.0-alpha.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

matticbot pushed a commit that referenced this pull request Feb 3, 2025
# [5.13.0](v5.12.5...v5.13.0) (2025-02-03)

### Bug Fixes

* add supported gateways check ([#3650](#3650)) ([74f7773](74f7773))
* **corrections:** replace deprecated sanitize method ([#3694](#3694)) ([ce50e24](ce50e24))
* remove support for legacy form checkout ([#3691](#3691)) ([46a3c16](46a3c16))
* **wcs:** expire manual subscriptions after on-hold duration ([#3681](#3681)) ([658416c](658416c))

### Features

* add custom bylines ([#3667](#3667)) ([3f45a6f](3f45a6f))
* rate limit checkout attempts ([#3678](#3678)) ([d275524](d275524))
* **reader-revenue:** add PayPal Payments gateway to wizard ([#3665](#3665)) ([1476eed](1476eed))
@matticbot
Copy link
Contributor

🎉 This PR is included in version 5.13.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
released on @alpha released [Status] Approved The pull request has been reviewed and is ready to merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants