隐藏

Asp.NET 通过实现 IHttpModule 来修改 Http POST 请求的请求体

发布:2021/8/26 16:03:48作者:管理员 来源:本站 浏览次数:748

需求起因

在 Web 开发中,通常需要对前端(front-end)传到后端(back-end)的数据进行过滤和校验,以防范诸如 SQL 注入、XSS 注入之类的攻击。
在 Java 中,通过继承 HttpServletRequestWrapper 类可重新包装当前请求(Http Request)并将其替换,它的实现如下:


public class RequestWrapper extends HttpServletRequestWrapper {

   private String AMP = "&";

   private String queryString;

   public RequestWrapper(HttpServletRequest request, String queryString)

   {

       super(request);

       this.queryString = queryString;

   }

   public String getQueryString()

   {

       String query = null;

       if (super.getQueryString() != null) {

           query = super.getQueryString() + AMP + this.queryString;

       } else {

           query = this.queryString;

       }

       return query;

   }

}

public class RequestHandler{

      public void changeRequest{

               RequestWrapper reqWrapper = new RequestWrapper(httpRequest, newQueryString() );

               FilterChain.doFilter(reqWrapper, servletResponse); // this FilterChain guy helps to replace the old request to  

                                                                                         // new request to runtime

      }

}

然而,Asp.NET 中并没有类似的包装器实现,通常的做法是在每一个请求通过(ProcessRequest)后再进行合法性校验,它导致的问题是每个请求 Action 都需要重复调用过滤方法来过滤请求参数。这显然冗余且麻烦得不太合理。

Asp.NET 中的一种可行实现思路


在 IIS 7.0+ 中,可以通过 HttpModule 来实现。

前置知识


下面列举了一些可能需要提前了解的知识点


   HttpRequest.Filter 微软给的简单示例

   IIS 7.0的ASP.NET应用程序生命周期概述

   为了有效地设置请求过滤器并应用它,应该在 BeginRequest 事件中进行设置,这是请求验证和 url 映射之后的第一个事件。


步骤


下面以在 .Net MVC 中配置示例

1. 配置


<?xml version="1.0" encoding="utf-8"?>

<configuration>

 // ignore other configuration

 <system.webServer>

   <validation validateIntegratedModeConfiguration="false" />

   <modules runAllManagedModulesForAllRequests="true">

      <add name="httpModule" type="HttpRequestFilterModule" />

       </modules>

   </system.webServer>

</configuration>


2. 实现 HttpRequestFilterModule


using MvcApplication1.Filter;

using System.Web;


public class HttpRequestFilterModule : IHttpModule

{

   public void Dispose()

   {

       throw new NotImplementedException();

   }


   public void Init(HttpApplication context)

   {

       context.BeginRequest += new EventHandler(Application_BeginRequest);

   }


   public void Application_BeginRequest(object sender, EventArgs args)

   {

       HttpContext context = HttpContext.Current;

       HttpRequest request = context.Request;


       // 仅过滤 POST 请求

       if (context.Request.HttpMethod != "POST")

       {

           return;

       }


       var requestCallback = new Func<string, string>(content => {

           // may modify request content here

           return "The request content is modified.";

       });

       context.Request.Filter = new RequestFilter(context.Request.Filter, context.Request.ContentEncoding, requestCallback);

   }

}


RequestFilter.cs


using System;

using System.IO;

using System.Text;


namespace MvcApplication1.Filter

{

   public class RequestFilter : Stream

   {

       //临时缓冲区用于优化加载

       private MemoryStream ms;


       //处理原始输出流

       private Stream _stream;


       //响应的编码方式

       private Encoding _encoding;


       //回调delegate

       private Func<string, string> _callback;


       public RequestFilter(Stream stream, Encoding encoding, Func<string, string> callback)

       {

           _stream = stream;

           _encoding = encoding;

           _callback = callback;

       }


       public override void Flush()

       {

           ms.Flush();

       }


       public override long Seek(long offset, SeekOrigin origin)

       {

           return ms.Seek(offset, origin);

       }


       public override void SetLength(long value)

       {

           ms.SetLength(value);

       }


       public override int Read(byte[] buffer, int offset, int count)

       {

           if (ms == null)

           {

               var sr = new StreamReader(_stream, _encoding);

               string content = sr.ReadToEnd();


               //回调执行

               content = _callback(content);


               byte[] bytes = _encoding.GetBytes(content);

               ms = new MemoryStream();

               ms.Write(bytes, 0, bytes.Length);

               ms.Seek(0, SeekOrigin.Begin);

           }


           return ms.Read(buffer, offset, count);

       }


       public override void Write(byte[] buffer, int offset, int count)

       {

           throw new NotSupportedException();

       }


       public override bool CanRead

       {

           get { return true; }

       }


       public override bool CanSeek

       {

           get { return false; }

       }


       public override bool CanWrite

       {

           get { return false; }

       }


       public override long Length

       {

           get { return ms.Length; }

       }


       public override long Position

       {

           get { return ms.Position; }

           set { throw new NotSupportedException(); }

       }

   }

}


3. 验证结果


   如果正常工作,访问下面 Get Action, 将得到 The request content is modified. 的输出结果。


public class HomeController : Controller

{

   [HttpPost]

   public ActionResult Get()

   {


       string requestBody = string.Empty;


       using (StreamReader reader = new StreamReader(Request.InputStream))

       {

           requestBody = reader.ReadToEnd();

       }


       return Content(requestBody);

   }

}