よくWordPressサイトなんかでは is_user_logged_in() を使用してログインユーザーのみしかページにアクセスできないようにしているが、多くの場合ログインしてないユーザーでもそのページの画像ファイルに直接アクセスすると取得できてしまうような実装になっていることが多い。
これをログインユーザーのみ画像にアクセスできるようにする方法を書いてみる。
まず画像ファイルをブラウザからアクセスできない領域(公開ディレクトリが「/var/www/public_html/」であれば、「/var/www/secret-image」など)に配置するか、.htaccessで直接アクセスできないよう制限をかける。
# .htaccess
# カレントディレクトリのファイルすべての直接のアクセスを禁止
Deny from all
# 一部のサイトのみにする場合
SetEnvIf Referer "^https://hoge\.com" mysite
SetEnvIf Referer "^https://www\.hoge\.com" mysite
SetEnvIf Referer "^http://hoge\.com" mysite
SetEnvIf Referer "^http://www\.hoge\.com" mysite
# SetEnvIf User-Agent "^Googlebot.*$" mysite # ユーザーエージェントを指定する場合
Order Deny,Allow
Deny from all
Allow from env=mysite
その後、サーバーサイドプログラム(例ではPHP、WP)を使って画像を表示させる(Instagramとかの画像のURLに拡張子がないのは、このように画像をコントロールしているから)。
// img.php
require_once( dirname( __FILE__ ) . 'wp-load.php' );
// ログインユーザーのみ画像を表示する場合
if ( is_user_logged_in() ) {
$file = '../secret-image/sample.png';
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($img_file);
header('Content-Type: '.$mimeType);
readfile($file);
}
// ページIDが100のページのパスワード保護が突破されているときのみ画像を表示する場合
if ( post_password_required( $id = 100 ) ) { }
// 特定のリファラのみ画像を表示する場合(ログインユーザーでも直接アクセスするのを禁止する場合)
if ( $_SERVER['HTTP_REFERER'] === 'https://hoge.com/foo/bar/' ) { }
その後、HTMLで設定する。
<!-- index.php -->
<img src="img.php" alt="" />
Webに公開する以上、あらゆる手を駆使しても画像などのリソースファイルにアクセスする=取得できることになるので、そのリソースファイルを完全には取得できないようにすることは難しい。
たとえば画像ファイルなんかでは、pointer-events:none;とか右クリックを禁止したところで、パスが分かっていれば直接アクセスしたり、スクショを取ったりできるのである程度知識のある方には通用しない。
最近だと、JSで頻繁にイベントを発火して、ブラウザタブがアクティブなときにしか画面を表示しないようにしたりしてスクショできないようにしているが、画面収録をすることで回避できたりするし、スマホなんかはどうしようもない。
顧客にそれを説明し可能な実装方法を提示していないシステム会社はごまんといるため、なんらかのアクセス制限が必要なサイトを依頼する際は、注意深く質問して聞いてみると良い。