SQL注入攻击是C#开发中对接数据库时最常见的安全威胁之一,攻击者通过在输入参数中拼接恶意SQL语句,绕过正常的验证逻辑,非法获取、修改甚至删除数据库中的数据。掌握有效的防范方法,同时完善数据库交互的安全策略,是每一个C#开发者的必备技能。

C#防范SQL注入的核心方法
1. 使用参数化查询
参数化查询是防范SQL注入最有效的方法,它将SQL语句和参数值分开处理,数据库会把参数值当作纯数据处理,不会将其解析为SQL指令的一部分。以下是使用SqlCommand实现参数化查询的示例:
using System.Data.SqlClient;
public class UserDao
{
private string connectionString = "Server=127.0.0.1;Database=TestDB;User Id=sa;Password=123456;";
// 根据用户名查询用户信息,使用参数化查询防止注入
public void GetUserByName(string userName)
{
string sql = "SELECT Id, UserName, Age FROM Users WHERE UserName = @UserName";
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
// 添加参数,指定参数类型和值
cmd.Parameters.Add(new SqlParameter("@UserName", System.Data.SqlDbType.NVarChar, 50) { Value = userName });
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"用户ID:{reader["Id"]},用户名:{reader["UserName"]},年龄:{reader["Age"]}");
}
}
}
}
}
}
如果使用字符串拼接的方式构造SQL语句,比如string sql = "SELECT * FROM Users WHERE UserName = '" + userName + "'";,当用户输入' OR '1'='1时,最终执行的SQL会变成SELECT * FROM Users WHERE UserName = '' OR '1'='1',直接绕过验证查询出所有用户数据,存在严重安全风险。
2. 使用存储过程
存储过程本身支持参数传递,只要调用时正确传入参数,同样可以避免SQL注入问题。以下是调用存储过程的示例:
using System.Data.SqlClient;
public class OrderDao
{
private string connectionString = "Server=127.0.0.1;Database=TestDB;User Id=sa;Password=123456;";
// 调用存储过程查询订单信息
public void GetOrderById(int orderId)
{
string procName = "GetOrderById";
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
// 添加存储过程参数
cmd.Parameters.Add(new SqlParameter("@OrderId", System.Data.SqlDbType.Int) { Value = orderId });
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine($"订单ID:{reader["OrderId"]},订单金额:{reader["Amount"]}");
}
}
}
}
}
}
3. 输入内容校验
对用户输入的内容做合法性校验,过滤掉可能的恶意字符,比如单引号、分号、注释符号等。可以结合正则校验,限制输入内容的格式,比如用户名只允许字母和数字,手机号必须符合手机号格式等。以下是简单的输入校验示例:
using System.Text.RegularExpressions;
public class InputValidator
{
// 校验用户名,只允许字母、数字、下划线,长度3-20位
public static bool ValidateUserName(string userName)
{
if (string.IsNullOrEmpty(userName)) return false;
string pattern = @"^[a-zA-Z0-9_]{3,20}$";
return Regex.IsMatch(userName, pattern);
}
// 校验手机号格式
public static bool ValidatePhoneNumber(string phone)
{
if (string.IsNullOrEmpty(phone)) return false;
string pattern = @"^1[3-9]d{9}$";
return Regex.IsMatch(phone, pattern);
}
}
C#提升数据库安全的其他措施
1. 最小权限原则配置数据库账号
不要使用数据库管理员账号(如sa)对接应用,为应用单独创建数据库账号,只授予该账号必要的操作权限,比如查询账号只给SELECT权限,写入账号只给INSERT、UPDATE权限,避免账号被攻击后造成全库数据泄露。
2. 加密敏感配置信息
数据库连接字符串中包含账号密码等敏感信息,不要直接明文写在代码或配置文件中,可以使用.NET提供的配置加密功能,或者将敏感信息存储在专门的密钥管理服务中,运行时动态获取,避免配置泄露导致数据库被非法访问。
3. 避免返回过多敏感数据
查询数据时只返回必要的字段,不要使用SELECT *查询所有字段,尤其是密码、身份证号等敏感字段,非必要场景不要返回。如果必须返回敏感字段,要做脱敏处理,比如手机号显示前3位和后4位,中间用星号代替。
4. 定期更新依赖和框架版本
及时更新.NET框架版本和数据库驱动的版本,修复已知的安全漏洞,同时定期做代码安全审计,排查潜在的SQL注入和其他安全风险,确保应用长期运行的安全性。
注意:防范SQL注入的核心是不让用户输入的内容被解析为SQL指令,参数化查询是最优先选择的方案,尽量不要使用字符串拼接构造SQL语句,哪怕做了输入校验,也可能存在校验遗漏的风险。