CVE-2026-32714
SQL Injection in the SciTokens KeyCache allows for arbitrary SQL execution.
- CVSS 9.8
- CWE-89
- Input Validation and Sanitization
- Remote
SciTokens is a reference library for generating and using SciTokens. Prior to version 1.9.6, the KeyCache class in scitokens was vulnerable to SQL Injection because it used Python's str.format() to construct SQL queries with user-supplied data (such as issuer and key_id). This allowed an attacker to execute arbitrary SQL commands against the local SQLite database. This issue has been patched in version 1.9.6.
- CWE
- CWE-89
- CVSS base score
- 9.8
- Published
- 2026-03-31
- OWASP
- A03 Injection
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Algorithm
- Category
- Input Validation and Sanitization
- Subcategory
- SQL Injection
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- SciTokens
- Fixed by upgrading
- Yes
Solution
Upgrade SciTokens to version 1.9.6 or later.
Vulnerable code sample
import sqlite3
import os
class KeyCache:
"""
A persistent, on-disk cache for public keys.
This is a simplified representation of the vulnerable class.
"""
def __init__(self, cache_path=":memory:"):
"""
Initializes the key cache.
:param str cache_path: Path to the SQLite3 database file.
"""
self._conn = sqlite3.connect(cache_path)
self._setup_db()
def _setup_db(self):
"""
Sets up the database table if it doesn't exist.
"""
cursor = self._conn.cursor()
try:
cursor.execute(
"CREATE TABLE IF NOT EXISTS keys "
"(issuer TEXT, key_id TEXT, public_key TEXT, "
"PRIMARY KEY (issuer, key_id))"
)
finally:
cursor.close()
def add_key(self, issuer, key_id, public_key):
"""
Adds a key to the cache.
"""
cursor = self._conn.cursor()
try:
cursor.execute(
"INSERT INTO keys (issuer, key_id, public_key) VALUES (?, ?, ?)",
(issuer, key_id, public_key),
)
self._conn.commit()
return True
except sqlite3.IntegrityError:
return False # Key already exists
finally:
cursor.close()
def get_key(self, issuer, key_id):
"""
Retrieves a key from the cache.
This method is vulnerable to SQL injection.
"""
cursor = self._conn.cursor()
try:
# VULNERABLE LINE: Using str.format() to build the query
query = "SELECT public_key FROM keys WHERE issuer = '{}' AND key_id = '{}'".format(
issuer, key_id
)
cursor.execute(query)
result = cursor.fetchone()
if result:
return result[0]
return None
finally:
cursor.close()
def demonstration():
"""
Demonstrates the SQL injection vulnerability.
"""
print("Setting up the KeyCache and adding a legitimate key.")
key_cache = KeyCache()
legit_issuer = "https://demo.scitokens.org"
legit_key_id = "key-a2"
legit_public_key = "SAMPLE_PUBLIC_KEY_DATA"
key_cache.add_key(legit_issuer, legit_key_id, legit_public_key)
print(f"Added key for issuer '{legit_issuer}' with key_id '{legit_key_id}'.\n")
# Attacker crafts a malicious key_id
# The payload closes the string literal for key_id, then uses OR 1=1 to make
# the WHERE clause true for any row, and -- comments out the rest of the query.
malicious_key_id = "' OR 1=1 --"
print(f"[*] Attacker attempts to retrieve a key using a malicious key_id:")
print(f" Issuer: {legit_issuer}")
print(f" Key ID: {malicious_key_id}\n")
# The vulnerable get_key method is called
retrieved_key = key_cache.get_key(legit_issuer, malicious_key_id)
if retrieved_key:
print(f"[+] Vulnerability Exploited! The first key in the database was returned:")
print(f" {retrieved_key}")
# In a real scenario, the attacker might not know the key_id, but can
# still retrieve a valid key's data by bypassing the intended logic.
else:
print("[-] Exploit failed.")
if __name__ == "__main__":
demonstration()Patched code sample
import sqlite3
import os
# This code represents a simplified version of the SciTokens KeyCache class
# to demonstrate the fix for the SQL injection vulnerability.
# The original vulnerability (CVE-2024-32714, not CVE-2026-32714) was due to using
# string formatting (str.format()) to build SQL queries.
# The fix involves using parameterized queries, which is the standard,
# safe way to pass data to SQL queries in Python.
DB_FILE = "keycache_fixed.db"
class PatchedKeyCache:
"""
A patched version of the KeyCache demonstrating the fix.
"""
def __init__(self, db_path):
self._conn = sqlite3.connect(db_path)
self._setup_database()
def _setup_database(self):
"""Initialize the database schema."""
cursor = self._conn.cursor()
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS scitokens_key_cache (
issuer TEXT,
key_id TEXT,
key TEXT,
PRIMARY KEY (issuer, key_id)
)
"""
)
# Add some sample data for demonstration
try:
cursor.execute(
"INSERT INTO scitokens_key_cache (issuer, key_id, key) VALUES (?, ?, ?)",
("https://demo.scitokens.org", "key-rs256", "SAMPLE_KEY_1")
)
cursor.execute(
"INSERT INTO scitokens_key_cache (issuer, key_id, key) VALUES (?, ?, ?)",
("https://another.issuer.com", "key_abc", "SAMPLE_KEY_2")
)
self._conn.commit()
except sqlite3.IntegrityError:
# Data already exists
pass
def get_key(self, issuer, key_id):
"""
Fetches a key from the cache using a patched, secure method.
THE FIX:
The vulnerable version of this function used Python's `str.format()` to
construct the SQL query, like so:
cursor.execute(
"SELECT key FROM scitokens_key_cache WHERE issuer = '{0}' AND key_id = '{1}'".format(
issuer, key_id
)
)
This is vulnerable to SQL injection. The corrected version below uses
parameterized queries (the '?' placeholders). The database driver
safely handles the values, preventing them from being interpreted as
SQL commands.
"""
cursor = self._conn.cursor()
# Patched code using parameterized query
cursor.execute(
"SELECT key FROM scitokens_key_cache WHERE issuer = ? AND key_id = ?",
(issuer, key_id),
)
result = cursor.fetchone()
return result[0] if result else None
def close(self):
"""Close the database connection."""
self._conn.close()
if __name__ == "__main__":
# Ensure the database file doesn't exist before we start
if os.path.exists(DB_FILE):
os.remove(DB_FILE)
cache = PatchedKeyCache(DB_FILE)
# 1. Demonstrate a legitimate query
print("--- 1. Legitimate Query ---")
issuer_legit = "https://demo.scitokens.org"
key_id_legit = "key-rs256"
key = cache.get_key(issuer_legit, key_id_legit)
print(f"Query for issuer='{issuer_legit}', key_id='{key_id_legit}'")
print(f"Result: {key}\n")
assert key == "SAMPLE_KEY_1"
# 2. Demonstrate a failed SQL injection attempt on the patched code
# The attacker provides a malicious key_id to try to bypass the WHERE clause.
print("--- 2. SQL Injection Attempt (Patched) ---")
issuer_malicious = "https://demo.scitokens.org"
# This input would cause the vulnerable code to return all rows.
key_id_malicious = "' OR '1'='1"
key = cache.get_key(issuer_malicious, key_id_malicious)
print(f"Query for issuer='{issuer_malicious}', key_id='{key_id_malicious}'")
print(f"Result: {key}\n")
if key is None:
print("SUCCESS: The injection was prevented. The query correctly found no key with the literal malicious ID.")
else:
print("FAILURE: The injection succeeded. The patch is ineffective.")
# Clean up
cache.close()
if os.path.exists(DB_FILE):
os.remove(DB_FILE)Payload
'; DROP TABLE keys; --
Cite this entry
@misc{vaitp:cve202632714,
title = {{SQL Injection in the SciTokens KeyCache allows for arbitrary SQL execution.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-32714},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-32714/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
