Viewing 8 replies - 1 through 8 (of 8 total)
  • Plugin Author Noor Alam

    (@naa986)

    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.

    Thread Starter David Anderson

    (@davidanderson)

    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.

    Thread Starter David Anderson

    (@davidanderson)

    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.

    Plugin Author Noor Alam

    (@naa986)

    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?

    Thread Starter David Anderson

    (@davidanderson)

    So, my Stripe checkout integration makes an AJAX call from my front-end JavaScript to the back-end PHP (which, being WordPress, is usingwp-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.

    Plugin Author Noor Alam

    (@naa986)

    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.

    Thread Starter David Anderson

    (@davidanderson)

    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.

    Plugin Author Noor Alam

    (@naa986)

    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);
Viewing 8 replies - 1 through 8 (of 8 total)
  • You must be logged in to reply to this topic.