必须用SqlParameter参数化查询替代所有字符串拼接,旧版ASP.NET完全支持;禁用Replace/正则过滤等无效防护,严查ExecuteNonQuery/ExecuteReader调用处,存储过程避免EXEC动态拼接,连接字符串加Charset=utf8,异常信息不可泄露。

直接上参数化查询,别碰任何字符串拼接逻辑——旧版 ASP.NET(.NET Framework 2.0–4.0)完全支持 SqlCommand + SqlParameter,这是唯一可靠、可落地、不改架构的修复路径。
确认你用的是 SqlCommand 而不是字符串拼接
很多老项目里藏着类似这样的代码:
string sql = "SELECT * FROM users WHERE username = '" + txtUser.Text + "' AND pwd = '" + txtPass.Text + "'";
这种写法必须全部替换。不要试图加 Replace() 或正则过滤单引号——绕过方式太多(比如 %df%27 宽字节、1' OR '1'='1 等),且会漏掉数字型注入(如 id=1 OR 1=1)。
检查所有 .aspx.cs 或 .asmx 中调用 ExecuteNonQuery() / ExecuteReader() 的地方,确保 SQL 字符串里没有 + 拼接用户输入变量。
用 SqlParameter 替换所有用户输入占位符
把原来拼接的 SQL 改成带 @ 占位符的写法,并显式添加参数。注意三点:
-
SqlParameter构造时必须传入类型和长度(尤其SqlDbType.NVarChar配合txtUser.Text.Length),否则可能触发隐式转换导致索引失效或截断 - 数字型参数(如
id)也必须走SqlParameter,不能只对字符串做处理;否则id=1 OR 1=1仍能穿透 - 不要用
string.Format()或$"..."(后者在旧框架不可用)生成 SQL —— 占位符只认@name,不认{0}或$插值
示例修复前后对比:
// ❌ 旧写法(危险)
string sql = "SELECT * FROM orders WHERE uid = " + Request["uid"];
<p>// ✅ 新写法(安全)
string sql = "SELECT * FROM orders WHERE uid = @uid";
using (var cmd = new SqlCommand(sql, conn)) {
cmd.Parameters.Add(new SqlParameter("@uid", SqlDbType.Int) { Value = Convert.ToInt32(Request["uid"]) });
// ... 执行
}避免在存储过程中“二次拼接”
有些项目把 SQL 逻辑下推到数据库,但存储过程里又用了 EXEC(@sql) 或 sp_executesql 拼接参数——这等于把注入点从 C# 移到了 SQL Server 里。
检查所有含 EXEC、sp_executesql、QUOTENAME() 的存储过程,若必须动态构造 SQL,务必用 sp_executesql 配合参数化(不是字符串拼接),例如:
-- ❌ 错误:拼接进 EXEC SET @sql = 'SELECT * FROM logs WHERE level = ''' + @level + ''''; EXEC(@sql); <p>-- ✅ 正确:用 sp_executesql + 参数 SET @sql = 'SELECT * FROM logs WHERE level = @level'; EXEC sp_executesql @sql, N'@level NVARCHAR(10)', @level = @level;
如果存储过程本身只做简单查询(如 WHERE id = @id),就根本不需要动态 SQL,直接写死语句更安全、更快。
字符集与错误处理别漏掉
旧项目常忽略两件事:一是数据库连接字符串没设 Charset=utf8(虽不如 PHP 宽字节敏感,但中文排序/比较异常时可能暴露逻辑漏洞);二是异常直接 Response.Write(ex.Message),泄露表名、字段甚至数据库版本。
必须做到:
- 连接字符串末尾加上
;Charset=utf8(SQL Server 可省略,但 MySQL 必须加) - 所有数据库异常统一跳转到友好的 500 页面,禁止输出
SqlException.Number或Message - 开发环境可开详细错误,生产环境
web.config中<customErrors mode="On" defaultRedirect="error.htm" />必须生效
最易被忽略的其实是日志:很多老系统把原始 SQL 和参数一起记进文本日志,如果日志文件可被 Web 访问(如放在 /log/ 下),攻击者能直接下载看到未脱敏的参数——这类路径要立即移出 Web 根目录或加 IIS/Nginx 访问限制。

















