首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

通用分頁存儲過程 SQL 注入漏洞深度剖析:動態 SQL 拼接風險與參數化防禦實戰

     編輯:關於SqlServer

「存儲過程天然防注入」是**危險誤區**。當存儲過程內部使用 `EXEC(@sql)` / `sp_executesql` 執行**字符串拼接**的動態 SQL,且拼接了**未經驗證的用戶輸入**,同樣存在注入風險。本文通過最小化漏洞復現、攻擊向量分析、參數化重構、靜態掃描規則,徹底釐清邊界。

漏洞復現:最小化動態 SQL 分頁過程

CREATE PROCEDURE dbo.GetPerson
    @strWhere VARCHAR(100) = ''  -- 用戶輸入:WHERE 子句片段
AS
BEGIN
    DECLARE @strSQL VARCHAR(1000) = 'SELECT TOP 10 * FROM Person WHERE 1=1 ';
    IF @strWhere != ''
        SET @strSQL = @strSQL + @strWhere;  -- 直接拼接,無驗證、無參數化
    PRINT @strSQL;
    EXEC(@strSQL);  -- 執行拼接字符串
END;

攻擊向量構造

正常調用

EXEC dbo.GetPerson @strWhere = ' AND LastName = ''Smith'' ';

生成:`SELECT TOP 10 * FROM Person WHERE 1=1 AND LastName = 'Smith'` ✅

注入攻擊

EXEC dbo.GetPerson @strWhere = ' AND 1=1; DROP TABLE Person; -- ';

生成:`SELECT TOP 10 * FROM Person WHERE 1=1 AND 1=1; DROP TABLE Person; --` 💥 **表被刪除**

數據窃取 (UNION 注入)

EXEC dbo.GetPerson @strWhere = ' AND 1=0 UNION SELECT name, password_hash FROM sys.sql_logins; -- ';

生成:`SELECT TOP 10 * FROM Person WHERE 1=1 AND 1=0 UNION SELECT name, password_hash FROM sys.sql_logins; --` 💥 **密碼哈希洩露**

根因:字符串拼接 ≠ 參數化

模式代碼示例注入風險計劃復用
字符串拼接 + EXEC`EXEC('SELECT * FROM T WHERE c=' + @input)`❌ 極高差 (每次不同字符串新計劃)
sp_executesql + 參數化`EXEC sp_executesql N'SELECT * FROM T WHERE c=@p', N'@p varchar(50)', @input`✅ 無 (參數強類型、計劃分離)優 (參數化計劃復用)
無動態 SQL (靜態)`SELECT * FROM T WHERE c=@input`✅ 無最優

安全重構:參數化動態 SQL 模式

模式一:sp_executesql 參數化 (推薦)

CREATE PROCEDURE dbo.GetPerson_Safe
    @LastName NVARCHAR(50) = NULL,
    @FirstName NVARCHAR(50) = NULL
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX) = N'SELECT TOP 10 * FROM Person WHERE 1=1 ';
    DECLARE @params NVARCHAR(MAX) = N'@LastName NVARCHAR(50), @FirstName NVARCHAR(50)';

    IF @LastName IS NOT NULL
        SET @sql += N' AND LastName = @LastName ';
    IF @FirstName IS NOT NULL
        SET @sql += N' AND FirstName = @FirstName ';

    EXEC sp_executesql @sql, @params, @LastName, @FirstName;
END;
  • 用戶輸入**永不作為 SQL 文本**,僅作為強類型參數值傳遞;
  • 計劃緩存鍵包含參數化文本,復用率高;
  • 支持 `OUTPUT` 參數、返回值、結果集元數據。

模式二:條件邏輯下推 (無動態 SQL)

CREATE PROCEDURE dbo.GetPerson_Static
    @LastName NVARCHAR(50) = NULL,
    @FirstName NVARCHAR(50) = NULL
AS
SELECT TOP 10 * FROM Person
WHERE 1=1
  AND (@LastName IS NULL OR LastName = @LastName)
  AND (@FirstName IS NULL OR FirstName = @FirstName);
  • 最簡單、性能最優、計劃穩定;
  • 缺點:複雜條件組合 (OR/動態排序/動態列) 難以表達;
  • 優化器可能產生「參數嗅探」計劃,配合 `OPTION (RECOMPILE)` 緩解。

模式三:表值參數 TVP 傳遞複雜過濾條件 (2008+)

CREATE TYPE dbo.FilterCondition AS TABLE (
    ColumnName SYSNAME, Operator VARCHAR(10), Value NVARCHAR(MAX)
);
CREATE PROCEDURE dbo.GetPerson_TVP
    @Filters dbo.FilterCondition READONLY
AS
-- 動態構建參數化 SQL,或應用層構建靜態 SQL + TVP 參數值
-- 核心:TVP 內容作為數據,非代碼

靜態掃描規則:CI/CD 管道集成

-- T-SQL 靜態分析正則 (簡化版)
-- 檢測 EXEC(@var) / EXEC(@var + ...) / sp_executesql @sql (無參數定義)
-- 禁止模式:
EXEC\s*\(\s*@\w+\s*\)
EXEC\s*\(\s*@\w+\s*[\+\-]\s*
sp_executesql\s+@\w+\s*(?!.*@params)

-- 允許模式:
sp_executesql\s+N?['"][^'"]*['"]\s*,\s*N?['"]@\w+
  • 工具:`TSQLLint`、 `SQLFluff`、 `SonarQube T-SQL Plugin`、自定義 PowerShell 腳本;
  • 集成:Pre-commit Hook + PR Gate + 夜ly 掃描報告。

避坑指南

  • 「我只拼接列名/表名,不拼接值」:標識符同樣可注入 (`@table = 'Person; DROP TABLE Log; --'`),**白名單驗證** (`IF @table NOT IN ('Person','Order') THROW`);
  • 「sp_executesql 就安全」:若 `@sql` 字符串本身包含拼接值 (`@sql = '... WHERE c=' + @val`),**仍不安全**,必須參數化;
  • 「動態 SQL 不可避免」:90% 場景可通過條件邏輯下推、TVP、JSON 參數 (2016+)、臨時表物化消除動態 SQL;
  • 權限最小化:執行動態 SQL 的過程擁有者權限 = 執行上下文,**簽名證書/模塊簽名**提升特定權限而非 `db_owner`。

總結

存儲過程**不天然防注入**,**動態 SQL 拼接用戶輸入**是注入溫床。鐵律:**用戶輸入永遠作為參數值,絕不作為 SQL 文本片段**。採用 `sp_executesql` 參數化、靜態條件下推、TVP、JSON 參數化,配合靜態掃描門禁,將注入風險壓縮至理論最小。這不是最佳實踐,是**底線合規**。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved