IIS7で使用するためにVS2010/ASP.NET 4.0でHttpModuleを作成しています。このモジュールは、クエリ文字列を暗号化することでクエリ文字列のセキュリティを強化します。
このモジュールをWebサイトから完全に独立させ、透過的にして、クエリ文字列の暗号化が採用されていることをWebサイトが認識しないようにします。これにより、最初にページ/コントロールがこの問題を気にする必要がなくなります。次に、本番環境ではクエリ文字列の暗号化を有効にし、非本番環境では無効にします(Web.configからHTTPモジュールを削除することにより)。
次の方法で、Web.configを介してIISに接続するようにHttpModuleを設計しました。
<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);
}
}
}
応答のHTML出力をいじり、その中のクエリ文字列を暗号化されたものに置き換えることによってすべてのタグを保護するために使用されるクラスQueryStringSecurityStreamがあります。
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への1回の呼び出しの結果として、ReleaseRequestStateへの複数の呼び出しがあります。
私が探しているのは:
私の勘が正しいことの確認。GoogleさんとMSDNさんに聞いてみましたが、決定的なものは見つかりませんでした。IIS6で実行されているASMXWebサービスからWSDLをデバッグしているときに、同様の問題が発生したことを覚えているようです。その場合、有効なXMLが得られるまで着信バイトストリームをキャッシュし、変更後にすべて書き出すことで問題を解決しました。
この種のシナリオを処理する正しい方法。これは、具体的には単一のBeginRequest /複数のReleaseRequestState呼び出しの問題、またはクエリ文字列の暗号化を一般的に意味するものと見なすことができます。
皆様。エンジンを始動します。答えを出しましょう。
アップデート:
リクエストのライフサイクルについてMSDNでこの記事を読みました
ReleaseRequestStateへの複数の呼び出しにわたって応答コンテンツを格納するためのバッファーを作成することにより、この問題を自分で解決しました。呼び出しのたびに、タグの存在を確認し、変更後(私の場合はタグ</html>
内のクエリ文字列を暗号化)、その時点までにバッファリングされたコンテンツを書き出します。<a>
それで:
- StringBuilderをQueryStringSecurityModuleクラスのプライベートフィールドメンバーとして宣言します(私の場合、応答コンテンツのバッファーとして機能するStringBuilder)。
- BeginRequestでフィールドを初期化します(私の場合、StringBuilderを割り当てます)。
- EndRequestでフィールドを完成させます(EndRequestが常に起動するとは限らないことを読みましたが、私の場合はnullに設定します)
- 終了htmlタグが見つかるまで、カスタムフィルターでWriteに送信されるバッファーバイト。その時点で、バッファーの内容を変更し、出力ストリームに書き込みます。
誰かがこのアプローチについてコメントしたいですか?