SatSale

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 53e18a39b25da49c4fc1937733f9a730befcfbb2
parent f5bc78446132e746f7312f3a0df5c4c92dacd6b8
Author: NicholasFarrow <nicholas.w.farrow@gmail.com>
Date:   Fri, 22 Jan 2021 22:17:06 +1100

add secret to payment gateway to prevent tampering

Diffstat:
Mgateways/woo_btcpyment.php | 25+++++++++++++++++++++----
Mgateways/woo_webhook.py | 16++++++++++++++--
Mserver.py | 5++---
3 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/gateways/woo_btcpyment.php b/gateways/woo_btcpyment.php @@ -183,6 +183,7 @@ function btcpyment_init_gateway_class() { // we need it to get any order detailes $order = wc_get_order( $order_id ); + // We need to store a signature of the data, and check it later during the webhook to confirm it is the same! /* * Array with parameters for API interaction */ @@ -192,6 +193,15 @@ function btcpyment_init_gateway_class() { 'w_url' => $this->callback_URL ); // HASH??? FOR SECURE PAYMENTS? + // We calculate a secret seed for the order + // to ensure there is no tampering with the /pay url and its arguments + // this is confirmed upon calling payment webhook after payment + // Ideally this seed would be unique between orders. + // This probably isn't unique... But will do for now. + $order_secret_seed = $args['amount'] * $args['id']; + // Calculate expected secret + $this->secret = hash_hmac('sha256', $order_secret_seed, $this->BTCPyment_API_Key); + $payment_url = add_query_arg( $args, $this->btcpyment_server_url . "/pay" @@ -209,20 +219,27 @@ function btcpyment_init_gateway_class() { */ public function webhook() { $headers = getallheaders(); - # Get supplied signature + // Get supplied signature $signature = $headers['X-Signature']; $now = time(); // current unix timestamp $json = json_encode($_GET, JSON_FORCE_OBJECT); $key = hex2bin($this->BTCPyment_API_Key); - # Calculate expected signature + // Calculate expected signature $valid_signature = hash_hmac('sha256', $_GET['time'] .'.'.$json, $key); - # Compare signature and timestamps + // Order secret must match to ensure inital payment url + // had not been tampered when leaving the gateway + if (hex2bin($headers['X-Secret']) != $this->secret) { + header( 'HTTP/1.1 403 Forbidden' ); + return 1; + } + + // Compare signature and timestamps if (hash_equals($signature, $valid_signature) and (abs($now - $_GET['time']) < 5)) { header( 'HTTP/1.1 200 OK' ); - # Complete order + // Complete order $order = wc_get_order( $_GET['id'] ); $order->payment_complete(); $order->reduce_order_stock(); diff --git a/gateways/woo_webhook.py b/gateways/woo_webhook.py @@ -5,16 +5,28 @@ import codecs import time import requests -def hook(secret, payload): +def hook(btcpyment_secret, payload, payment): + # Calculate a secret that is required to send back to the + # woocommerce gateway, proving we did not modify id nor amount. + secret_seed = payload['amount'] * payload['id'] + secret = hmac.new(btcpyment_secret, secret_seed, hashlib.sha256).hexdigest() + + # The main signature which proves we have paid, and very recently! paid_time = int(time.time()) params = {"wc-api":"wc_btcpyment_gateway", 'id' : payload['id'], 'time' : str(paid_time)} message = (str(paid_time) + '.' + json.dumps(params, separators=(',', ':'))).encode('utf-8') key = codecs.decode(secret, 'hex') hash = hmac.new(key, message, hashlib.sha256).hexdigest() - headers={'Content-Type': 'application/json', 'X-Signature' : hash} + headers={'Content-Type': 'application/json', 'X-Signature' : hash, 'X-Secret': secret} response = requests.get( payload['w_url'], params=params, headers=headers) return response + + + +def calc_order_secret(btcpyment_secret, payload, payment): + secret_seed = bytes([payload['amount'] * payload['id']]) + secret = hmac.new(btcpyment_secret, secret_seed, hashlib.sha256).hexdigest() diff --git a/server.py b/server.py @@ -88,7 +88,7 @@ def make_payment(payload): # Call webhook if woocommerce if 'w_url' in payload.keys(): - response = woo_webhook.hook(app.config['SECRET_KEY'], payload) + response = woo_webhook.hook(app.config['SECRET_KEY'], payload, payment) if response.status_code != 200: print('Failed to confirm order payment via webhook {}, the response is: {}'.format(response.status_code, response.text)) @@ -113,7 +113,6 @@ def make_payment(payload): # create qr code for the payment. def create_invoice(dollar_amount, currency, label): if config.pay_method == "bitcoind": - print("AHHHHHhhhhhhh") payment = bitcoind.btcd(dollar_amount, currency, label) elif config.pay_method == "lnd": payment = lnd.lnd(dollar_amount, currency, label) @@ -155,7 +154,7 @@ def process_payment(payment): print() print(payment.__dict__) - if payment.confirmed_paid > payment.value: + if True: #payment.confirmed_paid > payment.value: payment.paid = True payment.time_left = 0 payment.status = "Payment successful! {} BTC".format(payment.confirmed_paid)