Here is an example of Python code that is vulnerable to insecure deserialization:
🥺 Vulnerable Code
import base64
import pickle
@app.route("/cart")
def load_cart():
raw = request.cookies.get("cart", "")
# Vulnerable: pickle runs whatever the payload tells it to run
cart = pickle.loads(base64.b64decode(raw))
return render_template("cart.html", cart=cart)Pickle is not a data format, it is a small program format. A class with a __reduce__ method can ask the interpreter to call os.system during unpickling, so a cookie the attacker fully controls becomes remote code execution as the application user. The same applies to yaml.load without a safe loader, marshal, dill, and jsonpickle.
😎 Secure Code
Here is a version of the same code that is secured against insecure deserialization:
from itsdangerous import BadSignature, URLSafeTimedSerializer
serializer = URLSafeTimedSerializer(app.config["SECRET_KEY"], salt="cart-v1")
@app.route("/cart")
def load_cart():
raw = request.cookies.get("cart", "")
try:
data = serializer.loads(raw, max_age=3600) # JSON payload, signed and expiring
except BadSignature:
return redirect("/cart/new")
cart = CartSchema().load(data) # known keys and types only
return render_template("cart.html", cart=cart)The payload is now plain JSON, so parsing it can never execute code, and the signature plus max_age means a tampered or stale cookie is rejected before the schema even runs. The strict schema then rejects unexpected fields and types. The strongest version of this fix is to keep the cart server-side and put nothing but an opaque session id in the cookie.