中间件筛选器页面的安全问题

Erg*_*sum 10 python django

当我的启动处于黑暗模式时,我希望除访问/之外的所有访问都进入筛选页面,其中用户输入由代表给出的密码.我想出了以下简单的中间件来执行任务.需要明确的是,这是为了确保用户同意在允许网站浏览之前保密网站,而不是用作安全系统或.htaccess克隆.但是我想在不知道screener密码的情况下阻止他们看到任何公共页面(即那些没有用@login_required修饰的页面).password_check函数使用Django Auth生成输入密码的哈希值以检查db值.

你们可以看到的任何想法/规避技巧?我的一个想法是更改登录功能以将LicenceKey推送到新登录的用户会话,而不是为登录用户提供豁免.但是,由于它们只能通过登录创建新会话,并且登录需要同意筛选器,因此它似乎是多余的.

反馈意见.

中间件看起来像这样:

from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
import re

class LicenceScreener(object):
    SCREENER_PATH = reverse("licence")
    INDEX_PATH = reverse("index")
    LICENCE_KEY = "commercial_licence"
    def process_request(self, request):
        """ Redirect any access not to the index page to a commercial access screener page
            When the screener form is submitted, request.session[LICENCE_KEY] is set.
        """
        if not LicenceScreener.LICENCE_KEY in request.session \
            and not request.user.is_authenticated() \
            and LicenceScreener.SCREENER_PATH != request.path\
            and LicenceScreener.INDEX_PATH != request.path:
                return HttpResponseRedirect(self.SCREENER_PATH)
Run Code Online (Sandbox Code Playgroud)

视图看起来像这样:

def licence(request):
    c = RequestContext(request, {}  )
    if request.method == 'POST':
        form = LicenceAgreementForm(request.POST)
        if form.is_valid():
            if password_check(form.cleaned_data["password"]):
                request.session[LicenceScreener.LICENCE_KEY] = True
                return HttpResponseRedirect(reverse("real-index"))
            else:
                form._errors["password"] = form.error_class([_("Sorry that password is incorrect")])    
    else:
        form = LicenceAgreementForm()  
    c["form"] = form        
    return render_to_response('licence.html',c)
Run Code Online (Sandbox Code Playgroud)

编辑1.删除了Tobu建议的正则表达式

Ros*_*ost 1

这是我的解决方案。它使用的不是 COOKIES,而是自定义的身份验证后端。

步骤1

最好有一个Django 应用程序来实现这一点:

key_auth/
    templates/
        key_auth_form.html # very simple form template
    __init__.py
    models.py
    urls.py
    views.py
    forms.py
    middleware.py
    backend.py
Run Code Online (Sandbox Code Playgroud)

设置.py

INSTALLED_APPS = (
    # ...
    'key_auth',
)
Run Code Online (Sandbox Code Playgroud)

第2步

我们需要一个模型来存储您的代币。模型.py

from django.db import models
from django.contrib.auth.models import User

class SecurityKey(models.Model):
    key = models.CharField(max_length=32, unique=True)
    user = models.OneToOneField(User)
Run Code Online (Sandbox Code Playgroud)

笔记:在我的简单解决方案中,您将需要手动创建和同步新用户及其安全密钥。但你将来可以改进这一点。

步骤3

我们需要一个自定义中间件,它需要所有页面上的所有用户进行身份验证(少数特殊页面除外)。这是中间件.py

from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse

class KeyAuthMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):
        login_url = reverse('key_auth_login') # your custom named view

        # Exceptional pages
        login_page = request.path.find(login_url) == 0
        logout_page = request.path.find(reverse('logout')) == 0
        admin_page = request.path.find(reverse('admin:index')) == 0 # I've excluded Admin Site urls

        if not login_page and not logout_page and not admin_page:
            view_func = login_required(view_func, login_url=login_url)

        return view_func(request, *view_args, **view_kwargs)
Run Code Online (Sandbox Code Playgroud)

该中间件会将未经授权的用户重定向到其中包含身份验证表单的“key_auth_login”页面。

步骤4

以下是映射“key_auth_login”视图的urls.py :

from django.conf.urls.defaults import patterns, url

urlpatterns = patterns('key_auth.views',
    url(r'^$', 'login_view', name='key_auth_login'),
)
Run Code Online (Sandbox Code Playgroud)

以及全局项目的urls.py

from django.contrib import admin
from django.conf.urls.defaults import patterns, include, url

admin.autodiscover()

urlpatterns = patterns('',
    url(r'^key_auth/$', include('key_auth.urls')),
    url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': '/'}, name='logout'),
    url(r'^admin/', include(admin.site.urls)),
    url(r'^$', 'views.home_page'),
)
Run Code Online (Sandbox Code Playgroud)

如您所见 - 管理站点已打开(因此您也可以以管理员身份登录)。

步骤5

这是我们的视图(views.py):

from django.contrib.auth import login
from django.views.generic.edit import FormView
from key_auth.forms import KeyAuthenticationForm

class KeyAuthLoginView(FormView):
    form_class = KeyAuthenticationForm
    template_name = 'key_auth_form.html'

    def form_valid(self, form):
        login(self.request, form.user)
        return super(KeyAuthLoginView, self).form_valid(form)

    def get_success_url(self):
        return self.request.REQUEST.get('next', '/')

login_view = KeyAuthLoginView.as_view()
Run Code Online (Sandbox Code Playgroud)

我不会显示“key_auth_form.html”,因为它非常简单的表单模板,没什么特别的。但我会展示形式课程

步骤6

表单类(forms.py):

from django import forms
from django.contrib.auth import authenticate

class KeyAuthenticationForm(forms.Form):
    key = forms.CharField('Key', help_text='Enter your invite/authorization security key')
    user = None

    def clean_key(self):
        key = self.cleaned_data['key']
        self.user = authenticate(key=key)
        if not self.user:
            raise forms.ValidationError('Please, enter valid security key!')
        return key
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,这里使用了authenticate()。此方法将尝试使用现有后端对用户进行身份验证。

步骤7

自定义身份验证后端。后端.py

from django.contrib.auth.models import User
from key_auth.models import SecurityKey

class KeyAuthBackend(object):
    def authenticate(self, key=None):
        try:
            return SecurityKey.objects.get(key=key).user
        except SecurityKey.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None
Run Code Online (Sandbox Code Playgroud)

很简单!只需检查密钥(令牌)即可。这是它的安装(settings.py):

AUTHENTICATION_BACKENDS = (
    'key_auth.backends.KeyAuthBackend',
    'django.contrib.auth.backends.ModelBackend',
)
Run Code Online (Sandbox Code Playgroud)

留下第二个以保持管理站点正常运行。

恢复

就是这样!

  1. 任何请求都通过KeyAuthMiddleware
  2. KeyAuthMiddleware跳过登录、注销和管理 url ...
  3. ...并将所有未经授权的用户重定向到令牌身份验证登录页面(带有令牌登录表单)
  4. 此表单通过django.contrib.auth.authenticate()方法验证“密钥” ...
  5. ...它尝试通过自定义KeyAuthBackend身份验证后端对用户进行身份验证
  6. 如果身份验证成功并且表单有效,则自定义KeyAuthLoginView使django.contrib.auth.login(user)并重定向到请求的页面

您还可以使用管理站点和登录/注销视图,而无需进行 key_auth ckeck。