IIS 收到的每个请求是否会多次调用 ReleaseRequestState?

发布于 2024-11-08 14:06:46 字数 3302 浏览 2 评论 0原文

我正在 VS2010/ASP.NET 4.0 中编写一个 HttpModule,以便与 IIS 7 一起使用。该模块将通过加密查询字符串来强制查询字符串安全。

我希望该模块完全独立于网站并对网站透明,以便网站不知道正在使用查询字符串加密这一事实。这首先将确保页面/控件不必关心这个问题。其次,它将为生产环境启用查询字符串加密,并为非生产环境禁用它(通过从 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 的多次调用的响应内容,自己解决了这个问题。在每次调用时,我都会检查 标记是否存在,并写出修改后缓冲到该点的内容(在我的情况下,加密 < ;a> 标签)。

因此:

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

有人想对这种方法发表评论吗?

I am writing an HttpModule in VS2010/ASP.NET 4.0 for use with IIS 7. The module is going to enforce query string security by encrypting query strings.

I would like this module to be completely independent of as well as transparent to the website, so that the website has no knowledge of the fact that query string encryption is being employed. This will ensure firstly that pages/controls don't have to care about this issue. Secondly, it would enable query string encryption for Production environments and disable it for non-Production ones (by removing the HTTP module from the Web.config).

I have designed the HttpModule to get plugged into IIS via the Web.config by:

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

The module itself looks like this:

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

There is a class QueryStringSecurityStream which is used to fiddle with the HTML output in the Response, and secure all tags by replacing the query strings therein with encrypted ones.

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

The magic happens, or is supposed to happen, in the ReplaceHRefsWithSecureHRefs() extension method.

This method expects the entire HTML. It will go through it with a fine-toothed comb (i.e., using a Regex), find all the anchor tags, take out their href attributes, replace any query strings in the href value with encrypted versions and return the HTML. This HTML will then be written out to the Response stream.

So far so good. All of this falls over because I suspect that ReleaseRequestState is raised multiple times for individual requests. That is to say, that there are multiple calls to ReleaseRequestState as a result of a single call to BeginRequest.

What I am looking for is:

  1. Confirmation that my hunch is correct. I have asked Mr. Google and Mr. MSDN but haven't found anything definitive. I seem to remember hitting something similar while debugging WSDL from an ASMX web service running in IIS 6. In that case I solved the issue by caching the incoming byte stream till I have valid XML and then writing it all out after modifying it.

  2. The right way to handle this sort of scenario. You can take this to either mean specifically the single BeginRequest/multiple ReleaseRequestState calls issue or query string encryption generally.

Ladies and Gentlemen. Start your engines. Let the answers roll in.

Update:

I read this article on MSDN on request life-cycle

I have solved this issue for myself by creating a buffer to store response content across multiple calls to ReleaseRequestState. On every call, I check for the existence of a </html> tag and writes out the content buffered up to that point after modification (in my case encrypting the query strings in the <a> tags).

So:

  1. Declare a StringBuilder as a private field member in the QueryStringSecurityModule class (in my case a StringBuilder to serve as a buffer for response content).
  2. Initialize the field at BeginRequest (in my case, allocate a StringBuilder).
  3. Finalize the field at EndRequest (in my case set it to null, though I have read that EndRequest doesn't always fire)
  4. Buffer bytes being sent to Write in the custom filter till we find a closing html tag, at which point we modify buffer contents and write them out to the output stream.

Would anybody like to comment on this approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文