web-dev-qa-db-ja.com

ホットリンクを防ぐhtaccessコードが意図したとおりに機能しない

私はこのトピックをよく検索し、このコード(またはわずかなバリエーション)を多くの場所で見つけました。このWebサイトにはいくつかの回答が含まれています。これはコードです:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www\.)mydomain.com/.*$ [NC]
RewriteRule \.(gif|jpg)$ - [F]
</IfModule>

私はウェブマスターではありませんが、コードの機能を理解できます。もちろん私のコードでは、mydomain.comを実際のドメインに置き換えています。

問題は、このコードをルートフォルダーに配置すると、500エラーでWebサイト全体が停止することです。保護したいサブディレクトリにそれを置くと、サブディレクトリ内のすべてのファイルが誰も利用できなくなり、ウェブサイトで壊れた画像プレースホルダーが表示されます。どうしたんだ?

このコードが必要な理由は、サブスクライバーのみがファイルにアクセスできる必要があるためです。リンクを共有して、誰でも利用できるようにしたくない。これはテストコードであり、保護する実際のファイルの拡張子はgifやjpgとは異なりますが、原則は同じです。

1
Ikagil

購読者があなたのウェブサイトにログインしなければならない場合、画像を提供する前にこれをチェックするのと同じ方法で、画像を提供する前に画像フォルダでPHPスクリプトを使用してログインしていることを確認できますサブスクライバー専用Webページ。

。htaccessファイルには次のものがあります。

# Protect subscriber-only assets from hot-linking (*.gif)
RewriteCond %{REQUEST_URI} (.*)\.gif
RewriteRule ^(.*\.)(gif)$ protected-asset.php?f=$1$2 [L]

# Protect subscriber-only assets from hot-linking (*.jpg)
RewriteCond %{REQUEST_URI} (.*)\.jpg
RewriteRule ^(.*\.)(jpg)$ protected-asset.php?f=$1$2 [L]

そして、あなたのprotected-asset.phpファイルは次のようになります:

<?php
$sWebrootPath = '/var/www/www.mydomain.com/';
$sAsset = isset( $_GET['f'] ) ? $sWebrootPath . $_GET['f'] : '';
$aHeaders = array();

/* HTTP 404 if asset does not exist */
if( !file_exists( $sAsset )) {
  header( 'HTTP/1.0 404 Not Found' );
  exit;
}

/* Check if request comes from authenticated subscriber */
require_once( 'classes/session.php' );
if( Session::UserId() == 0 ) {
  header( 'HTTP/1.0 401 Unauthorized' );
  exit;
}

/* Declare MIME-Type */
$sFileExt = strtolower( substr( $sAsset, 1+stripos( $sAsset, '.' )));
switch( $sFileExt ) {
  case 'gif': $sMimeType = 'image/gif'; break;
  case 'jpg': $sMimeType = 'image/jpg'; break;
  default: $sMimeType = 'text/plain'; break;
}
$aHeaders[] = 'Content-Type: ' . $sMimeType . '; charset: UTF-8';

/* Cache for 1 hour (you could extend this as required) */
$iCacheSecs = 60 * 60;
$dtNow = time();
$dtExpires = strtotime( sprintf( '+%s seconds', $iCacheSecs ));
$aHeaders[] = 'Expires: ' . date( 'r', $dtExpires );
$aHeaders[] = 'Last-Modified: ' . date( 'r', $dtNow );
$aHeaders[] = 'Cache-Control: public, must-revalidate, ' .
  sprintf( 'max-age=%s', $iCacheSecs );

/* Generate unique ETag */
$sData = @file_get_contents( $sAsset );
$sETag = md5( $sData );
$aHeaders[] = sprintf( 'ETag: %s', $sETag );

/* HTTP 304 if asset has matching ETag to browser cached copy */
$sSuppliedETag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? 
  $_SERVER['HTTP_IF_NONE_MATCH'] : '';
if( $sSuppliedETag == $sETag ) {
  header( 'HTTP/1.1 304 Not Modified' );
  header( 'Content-Length: 0' );
  exit;
}

/* GZip Compression */
if( !ob_start( 'ob_gzhandler' )) ob_start();

/* Respond with Asset headers and data */
foreach( $aHeaders as $sHeader ) header( $sHeader );
echo( $sData );

上記のコードですぐに機能するわけではなく、独自のコードで置き換える必要がある2つのスニペットは、require_once( 'classes/session.php' );Session::UserId()です。ここでの前提は、require_onceが誰かがログインしているかどうかを識別するために必要な前提条件ファイルをロードし、私のUserId()関数が現在のセッションに割り当てられた認証済みユーザーがいないことを示す0を返していたことです。セッションなどを管理するためにおそらくCookieを使用する必要がありますが、すでにWebサイトのセットアップと動作が完了しているサブスクライバーログイン部分が既にあるように聞こえるので、これらの2つの部分を置き換えるためにコードをコピーするだけで問題ありません.

原則として私はすべてベストプラクティスに取り組んでいますが、一般的に<IfModule>ステートメントを。htaccessファイルに入れないのは、通常、私の経験で解決するよりも多くの問題を引き起こす可能性があるためです私のソリューションを試す前に、これらなしで既存のコードを試す価値があります!

2
richhallstoke