IIS7 の機能を拡張してみる-Hot Link (直リン) 禁止モジュール

IIS7 はパイプラインの変更により、.NET を使用してその機能を拡張することができます。

.NET を使用した IIS 7 の機能拡張については、詳しいセッションを過去に何度か行ったことがあるのですが、この機能に関しての記事をあまり見かけないので書いておきます。

.NET を使用した IIS 7 の拡張方法は大別すると、以下の方向性に大別されます。

  • リクエスト、レスポンスの応答処理
  • IIS 7 に対する操作
  • IIS 管理ツールの拡張

今回は リクエスト、レスポンスの応答処理 の中の、リクエストを処理を使用して Hot Link を禁止する方法をご紹介しましょう。

ちなみに Hot Link とは、Web サイトで公開しているコンテンツを、他の Web サイトが直接リンクして使用する行為です。"直リン" などと短縮して呼ばれることもあります。

"コンテンツへの直接のリンク" ということで、"Web に公開しているものに直接リンクしてなにが悪い?" と、疑問に思われる方もいらっしゃることてしょう。

たとえば、自分の Web サイトで、お宝画像、もとい、ユーザーから需要のある画像等を公開し、そのページに貼ったバナーのアフィリエイトで収益を上げているとしましょう。

ところが、他の Web サイトが、自分のサイトで公開している画像を <img> タグなどを使用して、さもその Web サイトが提供しているように公開していたらどうでしょう?

"非常に腹立たしい"、のはもちろんなのですが、なんといっても他所のサイトの訪問者は、バナーの貼ってある自分のサイトのページにはアクセスしませんので、アフィリエイトが機能せず、経済的な損失も生じることになります。

そういった場合には、Hot Link をしている Web サイトに対して注意、警告する必要が出てくるわけですが、運営者が気弱だったり、相手が道徳心に欠けるような ならず者 だったりすると、たいがいの場合、泣き寝入りすることになります。

ご存じのとおり、泣き寝入り という行為は、忍耐力の育成方法として考えてもあまり健全なものではありません。

では、このような問題を解決するにはどうしたらよいでしょう?

はたして何らかの解決策があったとして、"すべてもとどおり" というふうになるでしょうか?

じつは、問題というものは、解決策が必要になった時点で、ある意味すでに手遅れなのです。

"問題の解決策" より、先んじて考えなければならないのは、"問題を発生させない方法" なのです。

そもそも問題が発生しなければ、解決策は必要ありません。

つまり、この Hot Link 問題について、もっとも良い解決策というのは、あらかじめ Hot Link されないようにしておくということです。

ご存じのとおり、問題というものは常に、あらかじめ最初から "存在"している" ものなのです。それは "発生していない" だけのことなのです。

そんな、他の Web サイトからの腹立たしい Hot Link や、ダウンロードツールからの無遠慮なコンテンツの略奪行為をクールに回避するための秘密兵器が今回のサンプルコードになります、はい。(え、胡散臭いですか?)

 

IIS7 へのリクエストのハンドリング

IIS 7 の応答処理を拡張実現するオブジェクトとして、モジュールハンドラというものが用意されています。

この二つのオブジェクトの違いを簡単に説明すると、モジュールは、クライアントと Web サーバーの応答の間で処理を行うフィルタのようなものであり、ハンドラは、クリエストを受けてなにがしかの処理を実行するエンジンのようなものです。たとえば ASP.NET や、PHP 等を実行する FastCGI はハンドラです。

ふたつのオブジェクトの違いと詳細については以下のドキュメントを参照してください。

『HTTP ハンドラと HTTP モジュールの概要』
https://msdn.microsoft.com/ja-jp/library/bb398986.aspx

今回は、クライアントのリクエストが、コンテンツに到達する前に、リクエストの内容を調べ、リファラの内容が想定外であった場合はエラーページにリダイレクトする、という処理を行いますので、モジュールとして実装します。

"モジュールとして実装" と書くと難しく聞こえるかもしれませんが、System.Web.IHttpModule を継承したクラスを作成して、そのイベントハンドラ内で、引数として渡された URL を操作するだけなので、ASP.NET の経験者であれば簡単にプログラムを書くことが可能です。

具体的には、Visual Studio で、クラスライブラリでプロジェクトを作成し、コードを書いていきます。

ちなみにこれから紹介するサンプルコードについては、コンパイルする必要もありませんので、"Visual Studio を持っていない"、"プログラムを書けない" という方も、ぜひ設置してその効果をお試しください。

実際のサンプルコードと、IIS 7  への設置方法は以下の通りです。

  1. テキストエディタに以下のコードを貼り付けて、HotLinkBlocker_std.cs という名前で保存してください。

    using System; using System.Web; using System.Configuration;

    namespace MyIisExtentionModure {     public class HotLinkBlocker_std : IHttpModule     {         public void Dispose() {}

            public void Init(HttpApplication context)         {             //リクエスト処理前のイベントハンドラを定義             context.PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute);         }

            public void OnPreRequestHandlerExecute(Object source, EventArgs e)         {             HttpApplication app = (HttpApplication)source;             HttpRequest request = app.Context.Request;             HttpResponse response = app.Context.Response; 

                //リダイレクト用の URL を設定             string redirectURL = ConfigurationSettings.AppSettings["RedirectURL"];

                //リファラを取得             string requestReferer = request.Headers["Referer"];

                //リファラが null の場合はホットリンクと判断し、リダイレクト             if (requestReferer == null) response.Redirect(redirectURL);

                //リファラの正誤判断用の URL の前半部分を設定             string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];

                //リファラの内容が意図しないものの場合、リダイレクト             if (isInvalidReferer(validReferer)) response.Redirect(redirectURL);         }

            //リファラをチェック         private bool isInvalidReferer(string requestReferer)         {             string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];             return (validReferer != requestReferer.Substring(0, validReferer.Length));         }     } }

  2. Default Web Site 下の、ASP.NET が動作するように設定してある 仮想ディレクトリの物理フォルダ内に App_Code というフォルダを作成します。

  3. 作成した App_Code フォルダに HotLinkBlocker_std.cs ファイルを配置します。

  4. 同仮想ディレクトリ内の Web.config の <configuration> 内に以下の設定を追加します。

    <appSettings>     <add key="ValidReferer" value="https://localhost/" />     <add key="RedirectURL" value=https://localhost/iisstart.htm /> </appSettings>
  5. IIS 管理ツールを起動します。

  6. 画面左のツリービューから目的の Web サイト、あるい仮想ディレクトリを選択します。

  7. [機能 ビュー] から [モジュール] アイコンをダブルクリックします。

  8. [モジュール] リストが表示されるので、画面右の [操作パネル] から [マネージモジュールの追加] リンクをクリックします。

  9. [マネージモジュールの追加] ダイアログボックスが表示されるので、[名前] テキストボックスに HotLinkBlocker と入力します。

  10. 同ダイアログボックスの [種類] ドロップダウンリストボックスから、"MyIisExtentionModure.HotLinkBlocker_std" を選択します。

  11. [OK] ボタンをクリックしてダイアログボックスを閉じます。

  12. Defauft Web Site の直下に、モジュールを設置した仮想ディレクトリ内のコンテンツへのリンクを記述した html ファイルを配置します。

以上で配置は完了です。

Default Web Site 直下の HTML 内のリンクから、Hot Link 防止用のモジュールを配置した仮想ディレクトリ内にあるコンテンツにアクセスし、問題なくブラウザに内容が表示されることを確認してください。

次に、先ほどブラウザに表示したコンテンツの URL を、新しく起動したブラウザのアドレスバーに入力し、リクエストが iisstart.htm にリダイレクトされることを確認してください。

 

モジュールの配置について

IIS 7 の拡張に使用される ASP.NET 2.0 では、実行時にコンパイルが行われるため、ソースコードをそのまま配置しても実行することができます。

その場合は、ASP.NET が実行可能となっている仮想ディレクトリのルートに App_Code という名前でフォルダを作成し、そこにソースファイルを配置します。

もちろん、アセンブリとしてコンパイルした dll を使用することも可能です。

その場合は、ASP.NET が実行可能となっている仮想ディレクトリのルートに bin という名前でフォルダを作成し、そこにアセンブリファイル (dll) を配置します。

 

もう少し融通の利いた Hot Link 防止モジュール

前出の Hot Link 防止モジュールは、ソースを見やすくするためコード量を削っているので、動作的に融通が利きません。

たとえば、仮想ディレクトリ内すべてのコンテンツに Hot Link のチェックがかかるため、同ディレクトリ内のコンテンツから閲覧を開始することができません。(すべてリダイレクトされます)

以下に紹介するソースは、Web.config に定義した拡張子リストに合致するファイルのみ、Hot Link 防止機能が働きます。

ソースは以下のとおりです。

using System; using System.Web; using System.Configuration;

namespace MyIisExtentionModure {     public class HotLinkBlocker : IHttpModule     {

        public void Dispose() { }

        public void Init(HttpApplication context)         {             //リクエスト処理前のイベントハンドラを定義             context.PreRequestHandlerExecute += new EventHandler(OnPreRequestHandlerExecute);         }

        public void OnPreRequestHandlerExecute(Object source, EventArgs e)         {             HttpApplication app = (HttpApplication)source;             HttpRequest request = app.Context.Request;             HttpResponse response = app.Context.Response; 

            //リクエストされたファイルの拡張子を取得             string fileExtn = getFileExtension(request.FilePath);

            //拡張子がなければなにも処理しない             if (fileExtn == "") return;

            //リクエストされた拡張子がチェック対象でなければ処理しない             if (!isTargetExtension(fileExtn)) return;

            //リダイレクト用の URL を設定             string redirectURL = ConfigurationSettings.AppSettings["RedirectURL"];

            //リファラを取得             string requestReferer = request.Headers["Referer"];

            //リファラが null の場合はホットリンクと判断し、リダイレクト             if (requestReferer == null) response.Redirect(redirectURL);

            //リファラの正誤判断用の URL の前半部分を設定             string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];

            //リファラの内容が意図しないものの場合、リダイレクト             if (isInvalidReferer(validReferer)) response.Redirect(redirectURL);         }

        //リファラをチェック         private bool isInvalidReferer(string requestReferer)         {             string validReferer = ConfigurationSettings.AppSettings["ValidReferer"];             return (validReferer != requestReferer.Substring(0, validReferer.Length));         }

        //引数に指定された拡張子がチェックリストにあるか確認         private bool isTargetExtension(string fileExtn)         {             string[] fileExtnArray;

            //カンマ区切りのチェック用拡張子リストを取得             string targetFileType = ConfigurationSettings.AppSettings["TargetFileType"];

            //拡張子リストに "*" のみが指定されている場合はすべてのファイルをチェック             if (targetFileType == "*") return true;

            //拡張子リストが空の場合は処理を行わない             else if (targetFileType == "") return false;

            //拡張子リストを配列に確認             fileExtnArray = targetFileType.Split(',');

            //リクエストされたファイルの拡張子がチェック対象であるか確認             foreach (string targetExtn in fileExtnArray)             {                 if (fileExtn == targetExtn) return true;             }             return false;         }

        //リクエストされたファイルの拡張子を取得         private string getFileExtension(string filePath)         {             string[] fileExtnArray = filePath.Split('.');             if (fileExtnArray.Length >= 2)             {                 return fileExtnArray[fileExtnArray.Length - 1];             }             else             {                 return "";             }         }     } }

使用するには、テキストエディタに上記のソースコードを貼り付けて、HotLinkBlocker.cs という名前で保存して、仮想ディレクトリ内の App_Code フォルダにコピーしてください。

また、Web.config の <configuration> 内には、前出のサンプルより 1 行増えた、以下の設定を追加します。

<appSettings>     <add key="ValidReferer" value="https://localhost/" />     <add key="RedirectURL" value="https://localhost/iisstart.htm" />     <add key="TargetFileType" value="jpg,jpeg,gif,png" /> </appSettings>

追加された TargetFileType には、Hot Link を防止したいファイルの拡張子を "," (カンマ) 区切りで指定します。

また、"*" (アスタリスク) を指定した場合には、すべてのファイルに Hot Link 防止機能が適用されます。""(空文字) を指定した場合は、Hot Link のチェックは行われません。

 

コンパイルしたい人向けの Visual Studio プロジェクト

今回紹介したモジュールの Visual Studio プロジェクトファイルを以下の SkyDrive にアップしましたので、アセンブリで配置したい人は使ってみてください。

アセンブリで使用したいけれど、Visual Studio を持っていない、という方は、以下から Visual Studio 2008 Express Editions (無償) を入手することができますので、こちらもお試しください。

『Visual Studio 2008 Express Editions』
https://www.microsoft.com/japan/msdn/vstudio/express/

Real Time Analytics

Clicky