適用於WebApi的SQL注入過濾器

開發工具:Visual Studio 2017
C#版本:C#7.1sql

最有效的防止SQL注入的方式是調用數據庫時使用參數化查詢。
可是若是是接手一箇舊的WebApi項目,不想改繁多的數據庫訪問層的代碼,應該如何作。shell

個人解決方案是加一個過濾器。數據庫

先寫過濾方法,上代碼api

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類型,二是類的屬性。過濾器搞定。async

過濾器有兩種使用方式,一種是在具體的方法上添加ide

[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());
        }
    }
}

測試有效。開發工具

相關文章
相關標籤/搜索