{
  "id": "security/sql-injection-in-stored-procedure-dynamic-sql",
  "signature": "SQL injection in stored procedure due to dynamic SQL concatenation",
  "signature_zh": "由于动态 SQL 连接，存储过程中存在 SQL 注入",
  "regex": "EXEC.*@.*\\+|sp_executesql.*'+|dynamic SQL injection",
  "domain": "security",
  "category": "data_error",
  "subcategory": null,
  "root_cause": "A stored procedure uses dynamic SQL with string concatenation of user input (e.g., via EXEC or sp_executesql without parameterization), allowing SQL injection even though the application uses parameterized queries.",
  "root_cause_type": "generic",
  "root_cause_zh": "存储过程使用动态 SQL，通过字符串连接用户输入（例如，通过 EXEC 或 sp_executesql 而不进行参数化），即使应用程序使用参数化查询，也允许 SQL 注入。",
  "versions": [
    {
      "version": "SQL Server 2022",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "MySQL 8.0",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "PostgreSQL 16",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    }
  ],
  "os_specific": {},
  "dead_ends": [
    {
      "action": "Add input validation to the stored procedure (e.g., escape single quotes)",
      "why_fails": "Input validation is insufficient; attackers can bypass escaping with advanced techniques (e.g., second-order injection, using unicode). Parameterization is the only safe approach.",
      "fail_rate": 0.9,
      "condition": "",
      "sources": []
    },
    {
      "action": "Use a blacklist to block SQL keywords like 'DROP', 'SELECT'",
      "why_fails": "Blacklists are easily bypassed (e.g., using 'SeLeCt', comments, or encoding).",
      "fail_rate": 0.95,
      "condition": "",
      "sources": []
    }
  ],
  "workarounds": [
    {
      "action": "Rewrite the stored procedure to use parameterized dynamic SQL with sp_executesql. Example in T-SQL:\nCREATE PROCEDURE GetUser @username NVARCHAR(50), @tableName NVARCHAR(128)\nAS\nBEGIN\n    DECLARE @sql NVARCHAR(MAX)\n    SET @sql = 'SELECT * FROM ' + QUOTENAME(@tableName) + ' WHERE username = @uname'\n    EXEC sp_executesql @sql, N'@uname NVARCHAR(50)', @uname = @username\nEND",
      "success_rate": 0.95,
      "how": "Rewrite the stored procedure to use parameterized dynamic SQL with sp_executesql. Example in T-SQL:\nCREATE PROCEDURE GetUser @username NVARCHAR(50), @tableName NVARCHAR(128)\nAS\nBEGIN\n    DECLARE @sql NVARCHAR(MAX)\n    SET @sql = 'SELECT * FROM ' + QUOTENAME(@tableName) + ' WHERE username = @uname'\n    EXEC sp_executesql @sql, N'@uname NVARCHAR(50)', @uname = @username\nEND",
      "condition": "",
      "sources": []
    },
    {
      "action": "Replace dynamic SQL with static SQL if possible, using CASE statements or conditional logic to avoid concatenation.",
      "success_rate": 0.9,
      "how": "Replace dynamic SQL with static SQL if possible, using CASE statements or conditional logic to avoid concatenation.",
      "condition": "",
      "sources": []
    }
  ],
  "workarounds_zh": [
    "Rewrite the stored procedure to use parameterized dynamic SQL with sp_executesql. Example in T-SQL:\nCREATE PROCEDURE GetUser @username NVARCHAR(50), @tableName NVARCHAR(128)\nAS\nBEGIN\n    DECLARE @sql NVARCHAR(MAX)\n    SET @sql = 'SELECT * FROM ' + QUOTENAME(@tableName) + ' WHERE username = @uname'\n    EXEC sp_executesql @sql, N'@uname NVARCHAR(50)', @uname = @username\nEND",
    "Replace dynamic SQL with static SQL if possible, using CASE statements or conditional logic to avoid concatenation."
  ],
  "transition_graph": {
    "leads_to": [],
    "preceded_by": [],
    "frequently_confused_with": []
  },
  "official_doc_url": "https://learn.microsoft.com/en-us/sql/relational-databases/security/sql-injection",
  "official_doc_section": null,
  "error_code": "SQL_INJ_005",
  "verification_tier": "ai_generated",
  "confidence": 0.87,
  "fix_success_rate": 0.92,
  "resolvable": "true",
  "first_seen": "2024-04-12",
  "last_confirmed": "2024-06-01",
  "last_updated": "2024-06-01",
  "evidence_count": 1,
  "tags": [],
  "locale": "en",
  "aliases": []
}