Carding protections?
-
Hi,
Does this plugin have any protections against carding attacks? For example, rate limits on sessions launched by the same IP address? (See: https://docs.stripe.com/disputes/prevention/card-testing#combine-mitigations). I used a different Stripe WP plugin, but it had no protections and this caused a lot of trouble.
David
-
Hi, Thanks for the link. The plugin doesn’t submit any credit card data from your website. The payment information is collected on a Stripe-hosted page.
As the document explains, mitigations are still necessary when using Stripe Checkout. And this isn’t just theory – I’ve coded Stripe Checkout integrations in PHP myself and observed carding attacks (and then implemented suggested solutions). When you request a Stripe session, you should still be applying rate limiting, if you’re following Stripe’s recommendations – this is what I’m asking about.
Unfortunately we just experienced one of the described attacks, resulting in losses to fraud.
Please can you clarify whether you intend to fix this? If not, I think I should review the plugin so that other users are warned and don’t waste their time discovering the same issue and losing money through it.
You mentioned that you coded Stripe Checkout integrations in PHP. Can you please suggest a fix with code examples so that we can look into it?
So, my Stripe checkout integration makes an AJAX call from my front-end JavaScript to the back-end PHP (which, being WordPress, is using
wp-admin/admin-ajax.php
) to open a Stripe session.The PHP code that is opening the Stripe session looks like this; your plugin will have something equivalent:
\Stripe\Stripe::setApiKey($stripe_secret_key);
$session_data = array(
'payment_method_types' => array('card'),
'line_items' => array(array(
'name' => 'Acme Services',
'description' => $description;
'images' => null,
'amount' => $amount,
'currency' => $currency,
'quantity' => 1,
)),
'success_url' => 'https://example.com/card-payment-successful/',
'cancel_url' => 'https://example.com/payments',
);
$session = \Stripe\Checkout\Session::create($session_data);
// Return the result to the browser via JSON
if (!empty($session->id)) {
echo json_encode(array('status' => 'ok', 'session_id' => $session->id]));
} else {
error_log("Stripe_Checkout_Session::create error:" .print_r($session, true));
echo json_encode(array('status' => 'error', 'code' => 'stripe_checkout_session_create_failed'));
}That above code has no protections. One protection I’ve added is one which limits the number of sessions that can be opened by a single IP address in a single day to a maximum of ten, using WordPress transients. This goes before any call to
Session::create()
, since its purpose is to avoid making that call if the caller is being banned:if (!empty($_SERVER['REMOTE_ADDR'])) {
$transient_key = 'stripsess_'.substr(hash('sha256', $_SERVER['REMOTE_ADDR']), 0, 20);
$previous_sessions = (array) get_transient($transient_key);
$time_now = time();
$today_start = $time_now - ($time_now % 86400);
if (!isset($previous_sessions[$today_start])) $previous_sessions[$today_start] = 0;
if ($previous_sessions[$today_start] > 10) {
error_log("IP address ".$_SERVER['REMOTE_ADDR']." blocked from starting a new session - has already started ".$previous_sessions[$today_start]." today");
http_response_code(500);
die(json_encode(['status' => 'error', 'code' => 'disallowed_1', 'data' => 'Too many sessions']));
}
$previous_sessions[$today_start]++;
set_transient($transient_key, $previous_sessions, 86400*3);
}My own code actually has a few more things; such as checking the visitor’s IP address country – I ban attempts to open a session except from approved countries; and for some other countries, I set
$session_data['payment_intent_data'] = ['capture_method' => 'manual'];
so that there’s no there’s not automatic capture of payments, which means site owners never have to refund fraud unless they manually accepted the order first. (A useful feature for your plugin would be an option so that a site owner can choose to set capture mode to manual).It would be helpful if you were to add an
apply_filters()
call on your$session_data
, and another on the decision to allow a session to be created, so that plugin users can customise the logic. e.g. Here again I’d like to block countries I’m not interested in, and set capture method to manual based on other logic. If you provide a filter, then developers can apply their own criteria to maximise protection.Thanks. A filter right before the session creation sounds like a good idea since each user will have different criteria. It also allows you to run your custom code and block accordingly. Please let us know if this solution will work for your setup.
The two filters (one to edit session parameters so that for example you can change the “capture or authorise only” decision, and one to block opening a session at all) will be useful and will allow me or anyone else to apply custom logic. However, I’d still strongly recommend that you also follow Stripe’s recommendations by adding a basic rate-limiting protection to the plugin for everyone.
Thanks. A filter has been added. Here is an example to bind to it.
function example_callback($session_args, $post_data){
//custom code
return $session_args;
}
add_filter('wp_stripe_checkout_before_session_creation', 'example_callback', 10, 2);Just this one filter to edit parameters, or a second filter to allow to decide to stop creating the session too?
You can use it for both. Here is an example code to stop at any point:
wp_die('too many requests.');
Of course; not too user-friendly, but if that’s what there is, it is servicable.
- You must be logged in to reply to this topic.