Apache: 認証されたユーザーのみに画像の提供を制限する

Apache: 認証されたユーザーのみに画像の提供を制限する

Apache 設定でメディア フォルダへのアクセスを制限する方法を見つけようとしています。このフォルダは Django サイトからのアップロードを受け取り、画像/PDF のアップロードは認証されたユーザーのサイトに表示されます。問題は、認証されていない誰でも に移動できることですmysite.com/media/images/pic1.jpg。これはあり得ないはずです。この動作を制限するためにいくつかのことを試しましたが、1 つまたは 2 つのヒントが必要だと思います。

最初の試み: XSendfile

Xsendfile は動作しているように見えましたが、(名前が示すように) ファイルをダウンロード用に送信するため、画像を表示するはずのページが読み込まれません。したがって、これは私のユースケースに必要なものではないようです。

2回目の試行: ルールの書き換え

Apache 設定にいくつかの書き換えルールを追加しました:

RewriteCond "%{HTTP_REFERER}" "!^$"
RewriteCond "%{HTTP_REFERER}" "!mysite.com/priv/" [NC]
RewriteRule "\.(gif|jpg|png|pdf)$"    "-"   [F,NC]

認証を必要とするサイトのすべての部分は/priv/パスの背後にあるため、これが機能する場合は、ナビゲートが書き換えられると考えました/media/images/pic1.jpg。しかし、これも機能せず、mysite.com/media/images/pic1.jpg画像が表示されたままです。

3回目の試み:環境

仮想ホスト内の環境で同様のことを試してみました:

<VirtualHost *:80>
    ...
    SetEnvIf Referer "mysite\.com\/priv" localreferer
    SetEnvIf Referer ^$ localreferer
    <FilesMatch "\.(jpg|png|gif|pdf)$">
        Require env localreferer
    </FilesMatch>
    ...
</VirtualHost>

しかし、これも機能しませんでした。画像に直接移動することはできます。

4回目の試行: 有効なユーザーが必要

v-host に追加しましたRequire valid-userが、Django ユーザー モデルに対して確認する方法がわかりません。この変更後、画像を表示するページを読み込むたびにログインを求めるプロンプトが表示されます (ただし、htaccess などがない場合、認証するものはなく、サイトには画像が表示されません)。

そこで、ここで説明されていることを実装してみました(apache-auth は、以下の URL からダウンロードできます。WSGIHandler) ですが、私の Django プロジェクトでは(デフォルトの とは対照的に)が使用できませんget_wsgi_application()。エラーが発生します。これが最も合理的なアプローチのように思えますが、を機能させる方法や、 で を機能させる方法がraise AppRegistryNotReady("Apps aren't loaded yet.")わかりません。WSGIHandlerget_wsgi_application()

ファイルに推測しにくい uuid のような名前を付けることもできることはわかっていますが、これは中途半端な解決策のように思えます。では、これらの画像がユーザーが認証されているサイトの部分内でのみリンクされるように、メディア フォルダーへのアクセスを制限するための最善の戦略は何でしょうか。

Ubuntu 20.04、Apache 2.4

| いくつかのアドバイスに従って編集します |

認証

def check_password(environ, username, password):
    print("---->>>---->>>---->>>---->>>---->>> check_password() has been called  <<<----<<<----<<<----<<<----<<<----")

    return True

#from django.contrib.auth.handlers.modwsgi import check_password

Apache ログには、このスクリプトがロードされたことが示されていますが、print ステートメントがログに表示されないため、関数は実行されていないようです。この戦略がログに記録されるように、このファイルと wsgi.py ファイルに余分な print ステートメントを配置しましたが、wsgi.py ファイルにあったものだけがログに記録されました。

仮想ホスト:

<VirtualHost *:80>
    ServerName mysite.com
    ServerAlias mysite.com
    DocumentRoot /path/to/docroot/
    
    Alias /static/ /path/to/docroot/static/

    # Not sure if I need this
    Alias /media/ /path/to/docroot/media/

    <Directory /path/to/docroot/static/>
        Require all granted
    </Directory>

    <Directory /path/to/docroot/media/>
        Require all granted
    </Directory>

    # this is my restricted access directory
    <Directory /path/to/docroot/media/priv/>
        AuthType Basic
        AuthName "Top Secret"
        AuthBasicProvider wsgi
        WSGIAuthUserScript /path/to/docroot/mysite/auth.py
        Require valid-user
    </Directory>

    <Directory /path/to/docroot/mysite/>
        <Files "wsgi.py">
            Require all granted
        </Files>
    </Directory>

    WSGIDaemonProcess open-ancestry-web python-home=/path/to/ENV/ python-path=/path/to/docroot/ processes=10 threads=10
    WSGIProcessGroup mysite-pgroup
    WSGIScriptAlias / /path/to/docroot/mysite/wsgi.py

    LogLevel trace8
    ErrorLog "|/bin/rotatelogs -l /path/to/logs/%Y%m%d-%H%M%S_443errors.log 30"
    CustomLog "|/bin/rotatelogs -l /path/to/logs/%Y%m%d-%H%M%S_443access.log 30" combined
</VirtualHost>

|別の編集|

すべてが機能するようになったので、回答を受け入れました。多くの可動部分があり、それが回答の最初の問題を引き起こしました。(1) テストの check_password 関数が Apache ログに表示されませんでした.../var/log/apache2/error.logセットアップされたカスタム ログの代わりに表示されていました。理由はわかりませんが、わかりました...

(2) 私のvenvは適切にアクティブ化されていませんでしたが、djangoがシステムPythonにもインストールされているため、実際にはこれに気づきませんでした。私はactivate_this.pyスクリプトをvirtualenvからコピーしてvenvに追加し、wsgiファイルに次のようなものを追加しました。

activate_this = '/path/to/ENV/bin/activate_this.py'
with open(activate_this) as f:
    exec(f.read(), {'__file__': activate_this})

これらの問題を修正すると、wsgi.py ファイルから呼び出されたときに check_password 関数が機能します。ここでの「機能する」とは、認証されていないユーザーがアクセスできないフォルダーへのアクセスを制限することを意味します。ユーザーは、通常の Django ビューで 1 回、ブラウザー プロンプトで 1 回、合計 2 回、資格情報を提供する必要があります。これはイライラしますが、実際、私の質問はアクセス制限に関するものだったので、別の日に残しておきます。

auth.py から check_password を呼び出すという回答の提案は、私のプロジェクトでは機能しません。wsgi.py の前に呼び出されることを示唆するエラーが表示されます。check_password が呼び出された時点で venv が読み込まれていないか、設定が読み込まれていないようです。

答え1

get_wsgi_application が実行しているのは次のことです:

def get_wsgi_application():
    django.setup(set_prefix=False)     # this will lead to "apps_ready=true"
    return WSGIHandler()

ハンドラを返す前に Django 環境をセットアップしています。

wsgi.py で次のようにすればうまくいくはずです:

application = get_wsgi_application()
from django.contrib.auth.handlers.modwsgi import check_password
# the sequence is important!!

実際、問題は modwsgi.py の最初の行にあります。

UserModel = auth.get_user_model()

get_user_model() は apps_ready をチェックし、Python がファイルのインポートを実行した瞬間にすべてが完了するからです。

より良い方法は、別の auth.py を作成し、最初に Apache の error.log に出力される単純な print を使用して、それが実際に Apache によって呼び出されているかどうかを確認することです。

def check_password(environ, username, password):
    print("***********   check_password() has been called  ********")
    return True

これが実行されたら、import ステートメントに置き換えて、djangos check_password() を使用できます。

from django.contrib.auth.handlers.modwsgi import check_password

次に、httpd-vhosts.conf に次の内容を追加します。

<VirtualHost *:80>

   ....

   <Directory path_to_server_root/secret>
        AuthType Basic
        AuthName "Top Secret"
        AuthBasicProvider wsgi
        WSGIAuthUserScript path_to_wsgi/wsgi.py
        Require valid-user
   </Directory>

</VirtualHost>

関連情報