1

我正在 VS2010/ASP.NET 4.0 中编写一个用于 IIS 7 的 HttpModule。该模块将通过加密查询字符串来强制执行查询字符串的安全性。

我希望这个模块完全独立于网站并对网站透明,以便网站不知道正在使用查询字符串加密的事实。这将首先确保页面/控件不必关心这个问题。其次,它将为生产环境启用查询字符串加密,并为非生产环境禁用它(通过从 Web.config 中删除 HTTP 模块)。

我将 HttpModule 设计为通过 Web.config 通过以下方式插入 IIS:

<configuration>
    <system.web>
        <httpModules>
            <add name="QueryStringSecurityModule" type="MyHttpModules.QueryStringSecurityModule"/>
        </httpModules>
    </system.web>
</configuration>

模块本身如下所示:

public class QueryStringSecurityModule : IHttpModule
{
    public virtual void Init(HttpApplication application)
    {
        application.BeginRequest += HandleBeginRequest;
        application.EndRequest += HandleEndRequest;
        application.ReleaseRequestState += HandleReleaseRequestState;
    }

    public virtual void Dispose()
    {
    }

    private void HandleBeginRequest(object sender, EventArgs e)
    {
        // TODO : Decrypt the query string here and pass it on to the application
    }

    private void HandleEndRequest(object sender, EventArgs e)
    {
        // TODO : Twiddle thumbs
    }

    private void HandleReleaseRequestState(object sender, EventArgs e)
    {
        var response = HttpContext.Current.Response;

        if (response.ContentType == "text/html")
        {
            response.Filter = new QueryStringSecurityStream(response.Filter);
        }
    }
}

有一个类 QueryStringSecurityStream 用于处理响应中的 HTML 输出,并通过将其中的查询字符串替换为加密的字符串来保护所有标签。

public QueryStringSecurityStream : Stream
{
    public QueryStringSecurityStream(Stream stream)
        : base()
    {
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        var html = Encoding.Default.GetString(buffer, offset, count).ReplaceHRefsWithSecureHRefs();
        var bytes = Encoding.Default.GetBytes(html);

        this.stream.Write(bytes, 0, bytes.Length);
    }
}

魔术发生或应该发生在 ReplaceHRefsWithSecureHRefs() 扩展方法中。

此方法需要整个HTML。它将使用细齿梳(即使用正则表达式)进行检查,找到所有锚标记,取出它们的 href 属性,将 href 值中的任何查询字符串替换为加密版本并返回 HTML。然后,此 HTML 将被写入响应流。

到目前为止,一切都很好。所有这一切都失败了,因为我怀疑 ReleaseRequestState 会针对单个请求多次引发。也就是说,对于一次 BeginRequest 的调用,会导致多次调用 ReleaseRequestState。

我正在寻找的是:

  1. 确认我的预感是正确的。我问过 Google 先生和 MSDN 先生,但没有找到任何确定的答案。我似乎记得在从 IIS 6 中运行的 ASMX Web 服务调试 WSDL 时遇到了类似的问题。在这种情况下,我通过缓存传入的字节流直到我拥有有效的 XML 并在修改后将其全部写出来解决了这个问题。

  2. 处理这种情况的正确方法。您可以将此具体表示单个 BeginRequest/多个 ReleaseRequestState 调用问题或查询字符串加密。

女士们先生们。启动你的引擎。让答案滚滚而来。

更新:

在 MSDN 上阅读了关于请求生命周期的这篇文章

通过创建一个缓冲区来存储对 ReleaseRequestState 的多次调用的响应内容,我已经为自己解决了这个问题。在每次调用时,我都会检查标签是否存在,</html>并在修改后写出缓冲到该点的内容(在我的情况下,加密<a>标签中的查询字符串)。

所以:

  1. 将 StringBuilder 声明为 QueryStringSecurityModule 类中的私有字段成员(在我的例子中, StringBuilder 用作响应内容的缓冲区)。
  2. 在 BeginRequest 处初始化字段(在我的例子中,分配一个 StringBuilder)。
  3. 最终确定 EndRequest 的字段(在我的情况下将其设置为 null,尽管我已经读过 EndRequest 并不总是触发)
  4. 缓冲区字节被发送到自定义过滤器中的写入,直到我们找到一个关闭的 html 标记,此时我们修改缓冲区内容并将它们写入输出流。

有人想评论这种方法吗?

4

0 回答 0