Sonar扫描提示C#代码存在SQL注入安全热点,该如何处理?
处理SQL注入安全热点的方案
问题根源
这段代码用string.Format直接拼接表名和主键列名到SQL语句中,尽管这两个值来自实体类型的元数据,但Sonar无法确认GetTableName和GetPrimaryKeyName是否会返回不可信内容,因此标记为安全热点。
解决方法
1. 严格验证并转义标识符
给动态获取的表名、列名添加合法性校验,同时按照数据库规则转义标识符,避免恶意内容注入:
Type entryEntityType = entry.Entity.GetType(); string tableName = GetTableName(entryEntityType); string primaryKeyName = GetPrimaryKeyName(entryEntityType); // 验证标识符格式:只允许字母、数字、下划线 if (!IsValidDbIdentifier(tableName) || !IsValidDbIdentifier(primaryKeyName)) { throw new ArgumentException("非法的表名或主键列名"); } // 转义标识符(SQL Server用[]包裹,MySQL用`,Oracle用"",根据数据库调整) string escapedTableName = $"[{tableName}]"; string escapedPrimaryKey = $"[{primaryKeyName}]"; string deleteQuery = $"UPDATE {escapedTableName} SET IsDeleted = 1 WHERE {escapedPrimaryKey} = @id"; Database.ExecuteSqlCommand(deleteQuery, new SqlParameter("@id", entry.OriginalValues[primaryKeyName])); // 辅助校验方法 private bool IsValidDbIdentifier(string name) { return !string.IsNullOrEmpty(name) && System.Text.RegularExpressions.Regex.IsMatch(name, @"^[a-zA-Z0-9_]+$"); }
2. 改用ORM内置软删除功能(推荐)
如果使用Entity Framework,可以直接通过ORM机制实现软删除,完全避免手写SQL的风险:
// 定义软删除接口 public interface ISoftDelete { bool IsDeleted { get; set; } } // 实体类实现接口 public class YourEntity : ISoftDelete { public int Id { get; set; } public bool IsDeleted { get; set; } // 其他业务属性 } // 在DbContext中配置全局筛选与自动软删除 protected override void OnModelCreating(ModelBuilder modelBuilder) { // 全局过滤已删除数据 foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType)) { modelBuilder.Entity(entityType.ClrType) .HasQueryFilter(e => EF.Property<bool>(e, "IsDeleted") == false); } } } public override int SaveChanges() { // 将删除操作转为软删除 foreach (var entry in ChangeTracker.Entries<ISoftDelete>()) { if (entry.State == EntityState.Deleted) { entry.State = EntityState.Modified; entry.Entity.IsDeleted = true; } } return base.SaveChanges(); }
3. 封装为安全的存储过程
如果必须保留动态SQL逻辑,可将逻辑封装到存储过程中,在存储过程内做标识符白名单校验:
CREATE PROCEDURE SoftDeleteEntity @TableName NVARCHAR(128), @PrimaryKeyName NVARCHAR(128), @Id INT AS BEGIN -- 白名单校验:只允许预定义的合法表名和列名 IF @TableName NOT IN ('User', 'Order', 'Product') OR @PrimaryKeyName NOT IN ('Id', 'UserId', 'OrderId') BEGIN THROW 50000, "非法的表名或主键列名", 1; END DECLARE @Sql NVARCHAR(MAX) = N'UPDATE ' + QUOTENAME(@TableName) + N' SET IsDeleted = 1 WHERE ' + QUOTENAME(@PrimaryKeyName) + N' = @Id' EXEC sp_executesql @Sql, N'@Id INT', @Id END
C#调用代码:
Database.ExecuteSqlCommand("EXEC SoftDeleteEntity @TableName, @PrimaryKeyName, @Id", new SqlParameter("@TableName", tableName), new SqlParameter("@PrimaryKeyName", primaryKeyName), new SqlParameter("@Id", entry.OriginalValues[primaryKeyName]));
核心原则
- 绝对禁止直接将用户输入作为表名/列名拼接SQL
- 动态标识符必须经过合法性校验+数据库规则转义
- 优先依赖ORM内置能力,减少手写SQL的安全风险
内容的提问来源于stack exchange,提问作者Radha
相关产品推荐
相关产品推荐

