Проблемы с безопасностью с помощью страницы прокрутки промежуточного программного обеспечения

В то время как мой запуск в темном режиме, я хочу получить доступ, кроме доступа к странице /, чтобы перейти на страницу поиска, где пользователи вводят пароль, предоставленный им представителем. Для выполнения задачи я придумал следующее промежуточное программное обеспечение. Чтобы быть ясным, это должно гарантировать, что пользователи согласятся держать сайт в уверенности, прежде чем им разрешат просматривать, а не использовать в качестве системы безопасности или клонирования .htaccess. Однако я бы хотел, чтобы они не видели какие-либо общедоступные страницы (то есть те, которые не были украшены @login_required), не зная пароль screener. Функция 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)

И вид выглядит следующим образом:

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)

РЕДАКТИРОВАТЬ 1. Удалены регулярные выражения, как было предложено Tobu

Ответ 1

Вот мое решение. Он использует не COOKIES, а пользовательский Auth Backend.

Шаг 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

settings.py:

INSTALLED_APPS = (
    # ...
    'key_auth',
)

Шаг 2

Нам нужна Модель для хранения ваших токенов. models.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)

Примечание. В моем простом решении вам потребуется создать и синхронизировать новых пользователей и их SecurityKeys вручную. Но вы можете улучшить это в будущем.

Шаг 3

Нам нужно специальное промежуточное программное обеспечение, которое потребует аутентификации у всех пользователей на всех страницах (за исключением нескольких специальных страниц). Вот middleware.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)

Это промежуточное программное обеспечение перенаправляет неавторизованных пользователей на страницу "key_auth_login" с формой auth.

Шаг 4

Вот urls.py, который отображает "key_auth_login":

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

urlpatterns = patterns('key_auth.views',
    url(r'^$', 'login_view', name='key_auth_login'),
)

И глобальный проект 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'),
)

Как вы можете видеть - Admin Site включен (так что вы также можете войти в систему как admin).

Шаг 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()

Я не буду показывать '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

Как мы видим, authenticate() используется здесь. Этот метод попытается аутентифицировать пользователя с существующими бэкэндами.

Шаг 7

Пользовательский сервер аутентификации. backend.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

Это очень просто! Просто проверьте ключ (токен). Вот установка (settings.py):

AUTHENTICATION_BACKENDS = (
    'key_auth.backends.KeyAuthBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Вторым остается оставить работу администратора сайта.

Резюме

Что это!

  • Любой запрос проходит через KeyAuthMiddleware
  • KeyAuthMiddleware пропускает логин, выход из системы и URL-адреса администратора...
  • ... и перенаправляет всех неавторизованных пользователей на страницу входа в токен-авторизацию (с формой входа в токен)
  • Эта форма проверяет "ключ" через метод django.contrib.auth.authenticate()...
  • ... который пытается аутентифицировать пользователя через пользовательский бэкэнд аутентификации KeyAuthBackend
  • Если auth является успешным и форма действительна, тогда пользовательский KeyAuthLoginView делает django.contrib.auth.login(пользователь) и перенаправляет на запрошенную страницу

Вы также можете использовать Admin Site и логин/выход из режима без key_auth ckeck.