merchant-frontend-examples

ZZZ: Inactive/Deprecated
Log | Files | Refs

commit 3c196d954431c4287852dac18443e2c552137e4a
parent 326904f79fa985771b558ece2f0c3b329c3cab12
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date:   Mon, 27 Mar 2017 17:06:18 +0200

Security checks in python example. The frontend must generate
and save in the state the order_id, because it needs at /pay
time to check if it matches the one mentioned in the deposit
permission.

Without this check, a malicious wallet can use a old deposit
permission to get illimitate products.

The fulfillment URL schema also changed, as it needs a "salty/noncy"
value in it. It used to be always "base_url/fulfillment", for *any* purchase,
so the wallet erroneously picked the first deposit permission associated
with that fulfillment URL and attempted the payment.

Now it looks like "base_url/fulfillment?order_id=<order_id>".

Diffstat:
Mpython/example/example.py | 13++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/python/example/example.py b/python/example/example.py @@ -6,6 +6,7 @@ import base64 import os import logging import json +from random import randint app = flask.Flask(__name__) @@ -15,7 +16,7 @@ logger = logging.getLogger(__name__) CURRENCY = "PUDOS" BACKEND_URL = "http://backend.test.taler.net/" -def make_url(page, *query_params): +def make_url(page, query_params=dict()): """ Return a URL to a page in the current Flask application with the given query parameters (sequence of key/value pairs). @@ -47,7 +48,9 @@ def donate(): def generate_proposal(): DONATION = amount.string_to_amount("0.1:%s" % CURRENCY) MAX_FEE = amount.string_to_amount("0.05:%s" % CURRENCY) + ORDER_ID = str(randint(0, 999999)) order = dict( + order_id = ORDER_ID, nonce=flask.request.args.get("nonce"), amount=DONATION, max_fee=MAX_FEE, @@ -59,7 +62,7 @@ def generate_proposal(): price=DONATION, ), ], - fulfillment_url=make_url("/fulfillment"), + fulfillment_url=make_url("/fulfillment", query_params=dict(order_id=ORDER_ID)), pay_url=make_url("/pay"), merchant=dict( instance="tutorial", @@ -76,6 +79,7 @@ def generate_proposal(): logger.error("failed to POST to '%s'", url) return r.text, r.status_code proposal_resp = r.json() + flask.session["order_id"] = ORDER_ID return flask.jsonify(**proposal_resp) @@ -99,7 +103,10 @@ def pay(): if deposit_permission is None: e = flask.jsonify(error="no json in body") return e, 400 - + if (flask.session["order_id"] != deposit_permission["order_id"]): + e = flask.jsonify(error="Attempting to pay a product different \ + from the ordered one (%s != %s)" % (flask.session["order_id"], deposit_permission["order_id"])) + return e, 406 r = requests.post(urljoin(BACKEND_URL, 'pay'), json=deposit_permission) if 200 != r.status_code: return r.text, r.status_code