開發工具:Visual Studio 2017 C 版本:C 7.1 最有效的防止SQL註入的方式是調用資料庫時使用參數化查詢。 但是如果是接手一個舊的WebApi項目,不想改繁多的資料庫訪問層的代碼,應該如何做。 我的解決方案是加一個過濾器。 先寫過濾方法,上代碼 然後是過濾器,先上代碼 思路是, ...
開發工具:Visual Studio 2017
C#版本:C#7.1
最有效的防止SQL註入的方式是調用資料庫時使用參數化查詢。
但是如果是接手一個舊的WebApi項目,不想改繁多的資料庫訪問層的代碼,應該如何做。
我的解決方案是加一個過濾器。
先寫過濾方法,上代碼
using System;
using System.Collections.Generic;
using System.Web;
namespace Test
{
/// <summary>
/// 防止SQL註入
/// </summary>
public class AntiSqlInject
{
public static AntiSqlInject Instance = new AntiSqlInject();
/// <summary>
/// 初始化過濾方法
/// </summary>
static AntiSqlInject()
{
SqlKeywordsArray.AddRange(SqlSeparatKeywords.Split('|'));
SqlKeywordsArray.AddRange(Array.ConvertAll(SqlCommandKeywords.Split('|'), h => h + " "));
SqlKeywordsArray.AddRange(Array.ConvertAll(SqlCommandKeywords.Split('|'), h => " " + h));
}
private const string SqlCommandKeywords = "and|exec|execute|insert|select|delete|update|count|chr|mid|master|" +
"char|declare|sitename|net user|xp_cmdshell|or|create|drop|table|from|grant|use|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|select|delete|update|orderhaving|having|by|count|*|truncate|like";
private const string SqlSeparatKeywords = "'|;|--|\'|\"|/*|%|#";
private static readonly List<string> SqlKeywordsArray = new List<string>();
/// <summary>
/// 是否安全
/// </summary>
/// <param name="input">輸入</param>
/// <returns>返回</returns>
public bool IsSafetySql(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
return true;
}
input = HttpUtility.UrlDecode(input).ToLower();
foreach (var sqlKeyword in SqlKeywordsArray)
{
if (input.IndexOf(sqlKeyword, StringComparison.Ordinal) >= 0)
{
return false;
}
}
return true;
}
/// <summary>
/// 返回安全字元串
/// </summary>
/// <param name="input">輸入</param>
/// <returns>返回</returns>
public string GetSafetySql(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
if (IsSafetySql(input)) { return input; }
input = HttpUtility.UrlDecode(input).ToLower();
foreach (var sqlKeyword in SqlKeywordsArray)
{
if (input.IndexOf(sqlKeyword, StringComparison.Ordinal) >= 0)
{
input = input.Replace(sqlKeyword, string.Empty);
}
}
return input;
}
}
}
然後是過濾器,先上代碼
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Test
{
/// <inheritdoc>
/// <cref></cref>
/// </inheritdoc>
/// <summary>
/// SQL註入過濾器
/// </summary>
public class AntiSqlInjectFilter : ActionFilterAttribute
{
/// <inheritdoc />
/// <summary>
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(HttpActionContext filterContext)
{
base.OnActionExecuting(filterContext);
var actionParameters = filterContext.ActionDescriptor.GetParameters();
var actionArguments = filterContext.ActionArguments;
foreach (var p in actionParameters)
{
var value = filterContext.ActionArguments[p.ParameterName];
var pType = p.ParameterType;
if (value == null)
{
continue;
}
//如果不是值類型或介面,不需要過濾
if (!pType.IsClass) continue;
if (value is string)
{
//對string類型過濾
filterContext.ActionArguments[p.ParameterName] = AntiSqlInject.Instance.GetSafetySql(value.ToString());
}
else
{
//是一個class,對class的屬性中,string類型的屬性進行過濾
var properties = pType.GetProperties();
foreach (var pp in properties)
{
var temp = pp.GetValue(value);
if (temp == null)
{
continue;
}
pp.SetValue(value, temp is string ? AntiSqlInject.Instance.GetSafetySql(temp.ToString()) : temp);
}
}
}
}
}
}
思路是,加過濾器繼承ActionFilterAttribute,重寫OnActionExecuting方法,獲取入參,對入參中的string類型的所有數據進行過濾。兩種情況,一是參數是string類型,二是類的屬性。過濾器搞定。
過濾器有兩種使用方式,一種是在具體的方法上添加
[HttpPut,Route("api/editSomething")]
[AntiSqlInjectFilter]
public async Task<bool> EditSomeThingAsync([FromBody]SomeThingmodel)
{
var response = await SomeThingBusiness.Editsync(model);
return response;
}
一種是全局配置,在WebApiConfig.cs文件中的Register方法中加上過濾器
using System.Web.Http;
namespace Test
{
/// <summary>
/// WebApi配置
/// </summary>
public static class WebApiConfig
{
/// <summary>
/// 註冊配置服務
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//全局配置防止SQL註入過濾
config.Filters.Add(new AntiSqlInjectFilter());
}
}
}
測試有效。