真正有效的修复是将所有用户输入从SqlCommand.CommandText中剥离,全部交由SqlParameter管理;必须显式添加参数、明确类型、校验标识符、杜绝拼接,并覆盖全部输入入口。

旧系统里的 SQL 注入漏洞,不能靠加个 Replace("'", "''") 或正则过滤就蒙混过关——这种写法对 1 OR 1=1、宽字节、UNION SELECT 类攻击完全无效。真正有效的修复只有一条路:把所有用户输入从 SqlCommand.CommandText 中彻底剥离,全部交给 SqlParameter 管理。
所有 SqlCommand.CommandText 里含用户输入的地方都必须改
常见错误现象是搜索页、登录页、分页控件回传参数时直接拼字符串,比如:sSql = "SELECT * FROM Users WHERE Name = '" + Request["name"] + "'"。哪怕 Request["name"] 看起来只是个用户名,它也可能是 ' OR '1'='1 或 admin'--。
- 检查所有
.aspx.cs、.asmx、.ashx文件中调用ExecuteNonQuery()、ExecuteReader()、ExecuteScalar()的位置 - 只要
CommandText出现+、string.Format、StringBuilder.Append拼接用户变量,就必须重构 - 数字型参数(如
id=Request["id"])同样危险,"WHERE id = " + id会被1 OR 1=1绕过
SqlParameter 必须显式添加到 Parameters 集合且类型明确
只写 new SqlParameter("@uid", value) 不够,漏掉挂载或名字不匹配,等于没防。SQL 里写 @uid,代码里就得用 cmd.Parameters.Add("@uid", ...),写成 @userid 或漏掉 @ 符号,参数不会绑定,查询静默失败。
- 类型必须显式指定:
SqlDbType.Int、SqlDbType.NVarChar,避免AddWithValue自动推断出VarChar导致索引失效 - 字符串长度要合理:
SqlDbType.NVarChar, 50,别无脑设4000,否则可能掩盖截断或拖慢执行 -
null值必须显式转为DBNull.Value:Value = (object)input ?? DBNull.Value,否则抛NullReferenceException
表名、列名、ORDER BY 字段不能用 SqlParameter
SqlParameter 只能绑定值(value),不能绑定标识符(identifier)。所以 "SELECT * FROM " + tableName 或 "ORDER BY " + sortField 这类写法,参数化救不了。
- 这类动态部分必须走白名单校验:比如
sortField只允许"name"、"created"、"status"等预设值 - 若必须动态拼接,SQL Server 下可用
QUOTENAME(sortField)包裹,但仅限可信上下文(如后台管理功能且字段来源可控) - 绝对禁止在存储过程中二次拼接:
EXEC(@sql)或sp_executesql N'SELECT * FROM ' + @table同样高危,应改用sp_executesql+ 参数化值 + 白名单表名
存储过程调用也得走参数化,不能拼 EXEC
很多老项目以为“用了存储过程就安全了”,结果在 C# 层还这么写:cmd.CommandText = "EXEC sp_GetUser @id = " + id。这和拼接普通 SQL 没区别,照样被注入。
- 正确做法是:
cmd.CommandText = "sp_GetUser"+cmd.CommandType = CommandType.StoredProcedure - 然后用
cmd.Parameters.Add("@id", SqlDbType.Int).Value = Convert.ToInt32(id) - 注意:存储过程内部若仍用
EXEC(@sql)拼接,那只是把注入点从 C# 移到了 SQL Server,一样要修
最易被忽略的一点:修复不是改完一个登录框就结束。所有用户输入入口——搜索框、下拉框回传值、URL 查询参数、AJAX 请求体、甚至隐藏字段的 value——都得一视同仁走 SqlParameter。漏掉任意一处,整套防护就形同虚设。

















