我在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类,用于调整Response中的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。
我在寻找的是:
确认我的预感是正确的。我问谷歌先生和MSDN先生,但没有找到任何确定的内容。我似乎记得在从IIS 6中运行的ASMX Web服务调试WSDL时遇到类似的问题。在这种情况下,我通过缓存传入的字节流解决了这个问题,直到我有了有效的XML,然后在修改它之后全部写出来。
处理此类情况的正确方法。您可以将此视为具体意味着单个BeginRequest /多个ReleaseRequestState调用问题或查询字符串加密。
女士们,先生们。启动引擎。让答案滚滚而来。
更新
我读了这个article on MSDN on request life-cycle
我已经为自己解决了这个问题,方法是创建一个缓冲区,以便在多次调用ReleaseRequestState时存储响应内容。在每次调用时,我检查是否存在</html>
标记,并在修改后写出缓存到该点的内容(在我的情况下加密<a>
标记中的查询字符串)。
所以:
有人想评论这种做法吗?