PHP の apache_setenv と virtual を利用して,ファイルへの直リンクを防止する

  ファイルへの直リンクを防止する方法としては,例えば Referer ヘッダ参照する方法とかがあると思います.
  Apache クックブック のレシピ 6.5 に載っているのもその方法ですが,Referer ヘッダを送信しないクライアントはどうするかという問題があります.

  そこで,PHP の apache_setenv と virtual を利用する方法を紹介します.

- apache_setenv
  Apache サブプロセスの環境変数を設定する
  http://php.net/apache-setenv

- virtual
  Apache サブリクエストを実行する
  http://php.net/virtual

  ダウンロードするファイルがあるディレクトリを /var/www/html/data とします.
  このディレクトリに .htaccess を置いて,以下のように記述します.

Order Allow,Deny
Allow from env=ACCESS_ALLOW


  この時点で,/var/www/html/data 以下のファイルにアクセスできなくなります.具体的には 403 Forbidden が返されます.
  これは,ACCESS_ALLOW という環境変数があれば,アクセスできますよと指定しているためです.

  次に以下のような PHP スクリプトを記述します.サンプルなのでファイル名は固定にしてあります.
  これを /var/www/html/foo.php として配置し,ブラウザからアクセスしてみてください.

<?php

// コンテンツタイプ
header('Content-type: image/jpeg');

// Apache 環境変数を設定
apache_setenv('ACCESS_ALLOW', '1');

// Apache にサブリクエストを投げる
// このスクリプトからの相対パスを指定する必要がある
virtual('data/foo.jpg');
?>


  この foo.php にアクセスすると,今度は data/foo.jpg が表示されると思います.
  つまり,環境変数 ACCESS_ALLOW を指定してアクセスしているわけです.

  この foo.php のファイル名を時間によって変えるなどすれば,まあ,直リンクを防止することができる……のかな?ちと強引ですが.

  パフォーマンスとか調査していないのですが,このような方法もあるということで紹介しておきます.


  参考までに,上記スクリプトを以下のように改造してあげると,filename で指定されたファイルをダウンロードすることができます.

<?php

define('DATA_DIR', 'data' . DIRECTORY_SEPARATOR);

if (!isset($_GET['filename']) || $_GET['filename'] == '') {
    exit;
}

$filepath = DATA_DIR . basename($_GET['filename']);

if (!is_readable($filepath)) {
    exit;
}

$info = getimagesize($filepath);

// コンテンツタイプ
header('Content-type: ' . $info['mime']);

// Apache 環境変数を設定
apache_setenv('ACCESS_ALLOW', '1');

// Apache にサブリクエストを投げる
virtual($filepath);
?>