Skip to content

feat: Log IP addresses in cases of failed transactions

stephen requested to merge log-ip-for-failed-transactions into main

As per #119 (closed), it will be easier for us to track and deal with bad behavior if we, at the moment we can reason that abuse of the donate form is likely happening, properly log the IP addresses of offenders, along with a note about what is happening.

The aforementioned ticket mentions several scenarios in which we could be performing such an action. Priority was given to two of these scenarios:

  1. A user passes front-end validation but fails backend validation, almost certainly meaning they are failing the CSRF or CAPTCHA check
  2. A user successfully submits a donation, but the payment processor ultimately rejects the transaction via webhook a few moments later, almost certainly meaning that the card info submitted has been rejected for probable card theft.

This MR provides this logging by tweaking a few things:

  • When the donate form is validated using the internal Django is_valid() method, failure will cause the attempt to be logged with the user's IP.
  • tordonate.civicrm.repository.handle_donation_form_data() is now called with three parameters instead of two, with the first being the HttpRequest object sent to the originating view calling this method. (Only views handling incoming user traffic ever invoke handle_donation_form_data; the titular form_data comes from that user traffic as well.) The IP address is extracted from the request object using tordonate.utils.get_client.ip and added to the bundle of form data that is stashed in Redis for later pairing with webhook data.
  • In tordonate.civicrm.repository.report_donation(), when we test for the existence of the aforementioned form data, if it is found, we check to see if we are handling a webhook message indicating a failed attempt to process a transaction. Not all failed transactions indicate problematic behavior - some simply indicate "a user's subscription lapsed" - but if we have user form data to pair with the webhook data and the payment processing failed vendor-side, then we can reason that the user is most likely using stolen credit card information which passed as valid initially but tripped the payment processor's theft alert when being charged in earnest. Ergo, we take this moment to log the event with the user's IP.
  • Tests of handle_donation_form_data() have been updated to include a mocked HttpRequest.

There is an additional potential scenario for probing for card theft which has so far only been theorized - specifically, when a card payment validates during form submission, but is rejected by the payment processor so quickly that the "thank you!" page displays an error and no resultant webhook is generated. This MR does not address this scenario, as it has not been encountered outside of extremely specific constructed conditions.

Addresses #119 (closed), discussion should be held as to whether it resolves it.

Merge request reports