При разработке одного сайте (после запуска не забыть вставить ссылку) возникла задача предоставить пользователю возможность поделиться открытой информацией о своём заказе в социальных сетях. Была создана страница для публичного отображения заказа, на которую были выставлены кнопки социальных сетей. Всё отлично работало.
Но был один подводный камень. Ссылки выглядели как /order/public/N/
, где N
- это первичный ключ заказа. То есть, зная эту ссылку и подменяя первичный ключ, появлялась возможность просмотреть содержимое всех заказов сервисов, что обычно не хочется позволять.
В голову полез бред с создании моделей для хранения временных ссылок и прочая ерунда. Но бред был вовремя остановлен воспоминанием о модуле django-registration, с которым мне недавно пришлось плотно поработать. В данном модуле применяется проверка активации аккаунта через HMAC ссылку. Смысл идеи в следующем: у модели есть уникальные данные, неизвестные посторонним. Такими данными может послужить точное время регистрации модели в БД. Достаточно вычислить хэш от строки с представлением времени регистрации и добавить его к ссылке на публичную страницу заказа и проверять совпадение хэша при показе страницы. Всё, перебор заказов не возможен!
Вооружившись этой идеей и куском кода из Википедии, я сделал модуль digester.py
:
import hashlib
import hmac
from django.conf import settings
def make_hash(instance, field_name='registered', format='%Y%m%d%H%M%S'):
u"""Возвращает хэш от содержимого поля, преобразованного по указанному формату."""
secret_key = getattr(settings, 'SECRET_KEY')
value = getattr(instance, field_name).strftime(format)
return hmac.new(key=secret_key, msg=value, digestmod=hashlib.md5).hexdigest()
Доработал метод get_absolute_url
у модели заказа, чтобы он использовал сгенерированный хэш:
def get_absolute_url(self):
return reverse('frontend:order_public', args=[str(self.id), digester.make_hash(self)])
Для URL публичной страницы добавляем параметр secret
:
urls.url(r'^order/public/(?P<pk>\d+)/(?P<secret>[0-9a-f]{32})/$', views.order_public_view, name='order_public'),
Ну и добавляем проверку в представление, которое отображает страницу:
def order_public_view(request, pk, secret):
u"""Представление для публичного отображения заказа."""
order = shortcuts.get_object_or_404(Order, pk=pk)
if secret != digester.make_hash(order):
raise http.Http404()
...
Получилось дёшево и сердито, пользуйтесь :)