How do I prevent url query tampering using flask request.args.get?
Question:
I have a url http://localhost:8000/savings?sv=30
In Flask I retrieve the value using Jinja templates to display the value of sv
on the screen:
{% set sv = request.args.get('sv', '') %}
<p>{{ sv }}</p>
Is it possible to prevent a user from editing the url to display a different value?
Answers:
For anyone else wondering I have decided to use encode/decode to obscure the value of sv
from users.
I have taken Martijn Pieter’s advice to obscure the value as opposed to encrypting the value: Simple way to encode a string according to a password?
import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
def obscure(data: bytes) -> bytes:
return b64e(zlib.compress(data, 9))
def unobscure(obscured: bytes) -> bytes:
return zlib.decompress(b64d(obscured))
where this link is sent to a user:
link = 'https://localhost:8000/savings?sv=' + bytes.decode(obscure(str.encode(amount)))
Which is then decoded when they click the link:
@onboard.route('/savings', methods=['GET', 'POST'])
def savings():
savings = request.args.get('sv')
savings = str.encode(savings)
savings = unobscure(savings)
savings = bytes.decode(savings)
return render_template('onboard/savings.html', savings=savings)
You can use an HMAC to validate that a parameter is one you previously sent the client:
import hmac
from flask import Flask, request
app = Flask(__name__)
SECRET = "s3cret" # put me in an environment variable instead
@app.get("/")
def index():
outbound_sv = 30
outbound_hmac = hmac.new(
SECRET.encode(), str(outbound_sv).encode(), "sha1"
).hexdigest()
return f"<a href='/check?sv={outbound_sv}&hmac={outbound_hmac}'>click me</a>"
@app.get("/check")
def check():
inbound_sv = request.args.get("sv")
inbound_hmac = request.args.get("hmac")
correct_inbound_hmac = hmac.new(
SECRET.encode(), inbound_sv.encode(), "sha1"
).hexdigest()
assert hmac.compare_digest(inbound_hmac, correct_inbound_hmac)
return "ok"
if the user changes the value of sv
in /check, then you’ll get an error
I have a url http://localhost:8000/savings?sv=30
In Flask I retrieve the value using Jinja templates to display the value of sv
on the screen:
{% set sv = request.args.get('sv', '') %}
<p>{{ sv }}</p>
Is it possible to prevent a user from editing the url to display a different value?
For anyone else wondering I have decided to use encode/decode to obscure the value of sv
from users.
I have taken Martijn Pieter’s advice to obscure the value as opposed to encrypting the value: Simple way to encode a string according to a password?
import zlib
from base64 import urlsafe_b64encode as b64e, urlsafe_b64decode as b64d
def obscure(data: bytes) -> bytes:
return b64e(zlib.compress(data, 9))
def unobscure(obscured: bytes) -> bytes:
return zlib.decompress(b64d(obscured))
where this link is sent to a user:
link = 'https://localhost:8000/savings?sv=' + bytes.decode(obscure(str.encode(amount)))
Which is then decoded when they click the link:
@onboard.route('/savings', methods=['GET', 'POST'])
def savings():
savings = request.args.get('sv')
savings = str.encode(savings)
savings = unobscure(savings)
savings = bytes.decode(savings)
return render_template('onboard/savings.html', savings=savings)
You can use an HMAC to validate that a parameter is one you previously sent the client:
import hmac
from flask import Flask, request
app = Flask(__name__)
SECRET = "s3cret" # put me in an environment variable instead
@app.get("/")
def index():
outbound_sv = 30
outbound_hmac = hmac.new(
SECRET.encode(), str(outbound_sv).encode(), "sha1"
).hexdigest()
return f"<a href='/check?sv={outbound_sv}&hmac={outbound_hmac}'>click me</a>"
@app.get("/check")
def check():
inbound_sv = request.args.get("sv")
inbound_hmac = request.args.get("hmac")
correct_inbound_hmac = hmac.new(
SECRET.encode(), inbound_sv.encode(), "sha1"
).hexdigest()
assert hmac.compare_digest(inbound_hmac, correct_inbound_hmac)
return "ok"
if the user changes the value of sv
in /check, then you’ll get an error