我正在尝试找出一种方法来限制对我的 apache 配置中的媒体文件夹的访问。该文件夹从 Django 站点进行上传,并且图像/pdf 上传显示在站点中以向经过身份验证的用户显示。问题是,任何未经身份验证的 schmo 都可以导航到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、阿帕奇 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 上。我从 virtualenv 复制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})
修复这些问题后,从 wsgi.py 文件调用 check_password 函数即可工作。这里的“有效”意味着它限制对未经身份验证的用户不应访问的文件夹的访问。用户仍然需要提供两次凭据——一次在常规 django 视图中,一次在浏览器提示中。这很烦人,但实际上我的问题是关于限制访问的,所以我将它留到另一天。
从 auth.py 调用 check_password 的答案建议与我的项目不合作。我收到的错误提示它是在 wsgi.py 之前调用的——似乎在调用 check_password 时未加载 venv 或未加载设置。
这就是 get_wsgi_application 正在做的事情:
它在返回处理程序之前设置 django 环境。
以下应该在您的 wsgi.py 中解决问题:
实际上问题出在 modwsgi.py 的第一行:
因为 get_user_model() 将检查 apps_ready 以及所有在 python 执行文件导入的那一刻完成的事情!
更好的方法是创建一个单独的 auth.py 并首先检查它是否真的被 Apache 调用,并通过简单的打印将转到 Apache 的 error.log:
一旦运行,您可以用 import 语句替换它并使用 djangos check_password()。
然后在 httpd-vhosts.conf 中类似以下内容: