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:
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)