Jwt Misconfiguration in Flask with Cockroachdb
Jwt Misconfiguration in Flask with Cockroachdb — how this specific combination creates or exposes the vulnerability
JWT misconfiguration in a Flask application that uses CockroachDB as the backend data store can expose authentication bypass and authorization flaws. When tokens are not properly validated, an attacker can manipulate claims, use weak algorithms, or leak sensitive identity information that maps to user records in CockroachDB.
One common pattern is deserializing a JWT in Flask and using a subject or user ID claim to look up a user row in CockroachDB without verifying the token signature or algorithm. If the server accepts alg: none or uses a symmetric key where an asymmetric key is expected, an attacker can forge a token and gain access to another user’s CockroachDB row via Insecure Direct Object Reference (IDOR) or Broken Object Level Authorization (BOLA). For example, a token like eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiYWRtaW4ifQ. (unsigned) can be accepted by a lax Flask app, leading to unauthorized database access.
Another vector is storing sensitive identity metadata in the JWT that should remain in CockroachDB and be fetched server-side. Over-permissive scopes or missing aud / iss checks can cause token misuse across services. If the Flask app logs or reflects token claims without sanitization, it may inadvertently expose PII or API keys that are linked to CockroachDB rows, increasing data exposure risk.
SSRF and unsafe consumption patterns can also intersect: an attacker might supply a malicious JWKS endpoint URL to the Flask app during key discovery, causing the app to fetch keys from an attacker-controlled CockroachDB-backed service. This can lead to trusted-key substitution and token validation bypass. Because CockroachDB often stores user roles and permissions, flawed token-to-role mapping in Flask can result in privilege escalation when querying or modifying records.
Flask extensions like flask-jwt or manual PyJWT usage must explicitly set options={"verify_signature": true}, enforce algorithms=["RS256"], and validate iss, aud, and exp. The CockroachDB queries should use parameterized statements to avoid injection and should not rely on JWT claims alone for access decisions without server-side authorization checks.
Cockroachdb-Specific Remediation in Flask — concrete code fixes
Remediation centers on strict JWT validation and secure database interaction. Use strong asymmetric keys, validate standard claims, and enforce server-side authorization before executing CockroachDB queries.
import jwt
import ssl
from flask import Flask, request, jsonify
import psycopg2
from urllib.parse import urlparse
app = Flask(__name__)
# Use RS256 with a verified JWKS endpoint; do not accept none/algorithms
JWKS_URL = "https://auth.example.com/.well-known/jwks.json"
AUDIENCE = "my-api.example.com"
ISSUER = "https://auth.example.com/"
def get_jwks():
# In production, cache and refresh JWKS responsibly
ctx = ssl.create_default_context()
with urllib.request.urlopen(JWKS_URL, context=ctx) as resp:
return json.loads(resp.read())
def get_key(header, payload):
jwks = get_jwks()
for key in jwks["keys"]:
if key["kid"] == header["kid"]:
return jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key))
raise Exception("Unable to find key")
@app.route("/user/")
def get_user(user_id):
auth = request.headers.get("Authorization")
if not auth or not auth.startswith("Bearer "):
return jsonify({"error": "unauthorized"}), 401
token = auth.split(" ")[1]
try:
decoded = jwt.decode(
token,
key=get_key,
algorithms=["RS256"],
audience=AUDIENCE,
issuer=ISSUER,
options={"verify_signature": True, "require": ["exp", "iss", "aud"]}
)
except jwt.InvalidTokenError:
return jsonify({"error": "invalid_token"}), 401
# Server-side authorization: do not trust decoded["user"] alone
conn = psycopg2.connect(
host=urlparse(os.getenv("COCKROACHDB_URI")).hostname,
port=urlparse(os.getenv("COCKROACHDB_URI")).port,
dbname=urlparse(os.getenv("COCKROACHDB_URI")).path[1:],
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASSWORD"),
sslmode="require"
)
cur = conn.cursor()
# Enforce ownership/role checks in SQL; avoid concatenating user_id
cur.execute("SELECT id, role FROM users WHERE id = %s AND tenant_id = %s", (user_id, decoded["tenant_id"]))
row = cur.fetchone()
cur.close()
conn.close()
if not row or str(row[0]) != user_id:
return jsonify({"error": "forbidden"}), 403
return jsonify({"user": row[0], "role": row[1]})
Key points:
- Always set
algorithms=["RS256"](or another strong asymmetric algorithm) and neveralgorithms=["none"]. - Validate
iss,aud, andexpto prevent token replay across services and tenants. - Use CockroachDB’s PostgreSQL wire protocol with parameterized queries to prevent injection when resolving user identity.
- Perform server-side authorization checks even after a valid JWT is decoded; do not rely on claims alone for row-level permissions.
- Store minimal identity in the JWT; keep sensitive mappings and roles in CockroachDB and verify on each request.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |