T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/save_url_params.py:35
- Finding
- SQL Injection Through Attacker-Controlled URL Parameter Names## Vulnerability Details **File Location**: `scripts/save_url_params.py:35-41` and `scripts/save_url_params.py:62-67` **Vulnerability Type**: SQL injection through unvalidated identifiers **Risk Level**: High **Vulnerable Code**: ```python # Build column definitions column_defs = ", ".join(f"{col} VARCHAR(255)" for col in columns) create_sql = f""" CREATE TABLE IF NOT EXISTS {TABLE_NAME} ( id INT AUTO_INCREMENT PRIMARY KEY, {column_defs} ) """ cursor.execute(create_sql) ``` ```python columns = ", ".join(flat_params.keys()) placeholders = ", ".join(["%s"] * len(flat_params)) values = list(flat_params.values()) insert_sql = f"INSERT INTO {TABLE_NAME} ({columns}) VALUES ({placeholders})" cursor.execute(insert_sql, values) ``` ### Technical Analysis URL query parameter names originate from user-controlled input and are used directly as SQL column identifiers. Although the inserted values use parameter placeholders, placeholders do not protect table or column identifiers. Consequently, a specially crafted parameter name can introduce SQL syntax into both the `CREATE TABLE` statement and the subsequent `INSERT` statement. The exact ability to execute multiple statements depends on the MySQL connector configuration and server behavior. Even where stacked statements are disabled, malicious identifiers can still alter query structure, trigger persistent schema problems, or repeatedly cause database errors and denial of service. ### Attack Path 1. An attacker supplies a URL containing a specially crafted query parameter name. 2. `parse_qs(parsed.query)` preserves that attacker-controlled name as a dictionary key. 3. `flat_params.keys()` is passed to `create_table_if_not_exists()`. 4. The name is interpolated without validation into `column_defs`. 5. The same name is later interpolated into the `INSERT` column list. 6. MySQL parses and executes SQL whose structure is partially controlled by th ...[truncated 750 chars]
- Remediation
- ## Remediation Suggestions - Do not derive database columns directly from URL parameter names. Prefer a fixed normalized schema such as `url_id`, `parameter_name`, and `parameter_value`. - If dynamic identifiers are unavoidable, enforce a strict allowlist such as `^[A-Za-z_][A-Za-z0-9_]{0,63}$`. - Reject reserved words, duplicate normalized names, oversized identifiers, and all names that fail validation. - Quote identifiers using a connector-supported, database-specific mechanism after validation. Do not treat quoting alone as sufficient validation. - Continue using bound parameters for values. - Execute database operations through a dedicated account with access only to the required schema and operations. - Add tests using punctuation, spaces, backticks, parentheses, comments, reserved words, and oversized query keys to verify rejection.
