string.Replace不能防SQL注入,因其仅做文本替换,无法解决语义污染问题,且易漏Unicode变体、破坏正常数据、挡不住堆叠注入等;正确做法是全程使用参数化查询。
为什么
不能防 SQL 注入
因为替换只是文本处理,而 SQL 注入的本质是语义污染——攻击者靠拼接出合法但恶意的 SQL 语法来绕过检查。
对
或
的简单替换,既漏掉 Unicode 变体(如全角单引号)、又破坏正常数据(比如用户真名叫
),还挡不住联合查询、堆叠注入等高级手法。
常见错误现象:
看似“转义”了,但遇到
依然执行第二条命令。
永远别在拼接前做“关键词过滤”或“字符替换”,这属于防御性幻觉
所有用户输入进入 SQL 语句前,必须走参数化路径,不区分字段类型、不看输入内容
ORM 如 Entity Framework 的
默认走参数化,但手写
就立刻退化回风险区
如何正确用
绑定值
核心就一条:值进 SQL,只通过
或
,且绝不拼进
字符串里。
使用场景:增删改查所有含用户输入的语句,包括
子句、
后的字段名(注意:字段名不能参数化,需白名单校验)。
参数差异:
自动推断类型,但对
或空字符串可能误判为
引发隐式转换;生产环境建议用
显式指定。
和
也要防注入吗
要。只要 SQL 字符串里混入了未消毒的用户输入,不管用哪个执行方法,漏洞都存在。区别只在于攻击后果不同:
可能被用来偷单个字段(如密码哈希),
更容易拖库。
容易踩的坑:
以为“只读查询就安全”,结果写成
在存储过程中拼接动态 SQL(如
),此时需用
+ 参数,而非
把参数化当成“万能胶”,却在同一条语句里混用:参数化一部分、拼接另一部分(如表名拼接 + 字段值参数化)
为什么 ORM 不等于自动免疫
Entity Framework、Dapper 这类工具默认对
、
等接口做参数化,但它们不拦截你主动写的原始 SQL。
典型翻车点:
Dapper 的
—— 没有
符号,就是裸拼
EF Core 的
——
不处理参数,
才安全
用 EF 动态构建表达式树时,误把用户输入塞进
当作字段名,实际应走白名单映射
最常被忽略的复杂点:多层封装后,SQL 构建逻辑可能散落在服务层、仓储层甚至自定义扩展方法里,人眼很难追踪哪一行悄悄拼了字符串。只要没看到
占位符和显式
添加,就得怀疑。
string.Replacestring.Replace'--O'Reillycmd.CommandText = "SELECT * FROM users WHERE name = '" + input.Replace("'", "''") + "'";; DROP TABLE users--.Where(x => x.Name == input).FromSqlRawSqlCommand.ParametersParameters.AddParameters.AddWithValueCommandTextINORDER BYAddWithValueNULLvarchar(1)Add("@name", SqlDbType.NVarChar).Value = inputvar cmd = new SqlCommand("SELECT * FROM products WHERE category = @cat AND price < @max", conn);
cmd.Parameters.Add("@cat", SqlDbType.NVarChar).Value = userInputCategory;
cmd.Parameters.Add("@max", SqlDbType.Money).Value = decimal.Parse(userInputPrice);ExecuteScalarExecuteReaderExecuteScalarExecuteReader"SELECT * FROM logs WHERE ip = '" + Request.UserHostAddress + "'"EXEC('SELECT * FROM ' + @table)sp_executesqlEXEC.Where().Query() connection.Query("SELECT * FROM users WHERE id = " + id)@context.Users.FromSqlRaw("SELECT * FROM users WHERE name LIKE '%" + keyword + "%'")FromSqlRawFromSqlInterpolatedExpression.Constant@Parameters