Security Resources

⌘K
  1. Home
  2. Security Resources
  3. Secure Code Explain
  4. Insecure Deserialization in Python (Pickle)

Insecure Deserialization in Python (Pickle)

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.