Apache:限制向经过身份验证的用户提供图像

Apache:限制向经过身份验证的用户提供图像

我正在尝试找出一种方法来限制对 apache 配置中媒体文件夹的访问。该文件夹从 Django 站点获取上传内容,并将图像/pdf 上传内容显示在站点中供经过身份验证的用户查看。问题是,任何未经身份验证的人都可以导航到mysite.com/media/images/pic1.jpg。这应该是不可能的;我尝试了一些方法来限制这种行为,但我认为我需要一两个指针。

第一次尝试:XSendfile

Xsendfile 似乎可以工作,但它(顾名思义)发送文件进行下载,然后我原本应该显示图像的页面无法加载。所以这似乎不是我的用例所需要的。

第二次尝试:重写规则

我在 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仍然显示图像。

第三次尝试:环境

我在虚拟主机内部的环境中尝试了类似的操作:

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

但这也不起作用;我仍然可以直接导航到图像。

第四次尝试:要求有效用户

我已将其添加Require valid-user到 v-host,但我不知道如何根据 Django 用户模型对其进行检查。进行此更改后,每次加载显示图像的页面时,我都会收到登录提示(但如果没有 htaccess 等,则无需进行身份验证,网站上也不会显示任何图像)。

然后我尝试实现这里描述的内容(https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/apache-auth/),但我的 django 项目不喜欢WSGIHandler(与默认的相反get_wsgi_application())。我收到raise AppRegistryNotReady("Apps aren't loaded yet.")错误。这似乎是最合理的方法,但我不知道如何让其工作WSGIHandler,或者让该方法与 一起工作get_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 日志显示此脚本已加载,但该函数显然未执行,因为日志中未显示打印语句。我在此文件和 wsgi.py 文件中放置了一个杂散的打印语句,以确保此策略能够进入日志,只有 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从虚拟环境中复制了脚本并将其添加到我的 venv 中,并将类似这样的内容添加到我的 wsgi 文件中

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

修复这些问题后,check_password 函数在从 wsgi.py 文件调用时就可以正常工作。这里的“正常工作”意味着它限制了未经授权的用户无权访问的文件夹的访问权限。用户仍然需要提供两次凭据 - 一次在常规 django 视图中,一次在浏览器提示符中。这很烦人,但实际上我的问题是关于限制访问权限,所以我将这个问题留到另一天再解决。

答案建议从 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 调用,并使用简单的打印将输出到 Apache 的 error.log:

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>

相关内容