Django: предотвращение запросов строк внешних запросов - PullRequest
2 голосов
/ 19 января 2012

Я занимаюсь веб-разработкой уже несколько месяцев и продолжаю испытывать эту ноющую проблему.Для страниц характерно запрашивать контент со строкой запроса, которая обычно содержит значимые данные, такие как идентификатор в базе данных.Примером может служить такая ссылка:

http://www.example.com/posts?id=5

Я пытался придумать хорошую стратегию, чтобы пользователи не могли вручную вводить значение для идентификатора, не обращаясь к нему по ссылке.хотел бы только подтвердить запросы, которые были сделаны по ссылкам, представленным на моем сайте.Кроме того, веб-сайт может не иметь системы аутентификации и позволяет осуществлять анонимный просмотр;При этом информация не особо чувствительна, но все же мне не нравится идея не иметь возможности контролировать доступ к определенной информации.Я полагаю, что одним из вариантов было бы использование HTTP POST-запросов для страниц такого типа - я не верю, что пользователь может смоделировать почтовый запрос, но я могу ошибаться.любое произвольное число для идентификатора и в конечном итоге запрашивает запись, которая не существует в базе данных.Конечно, я мог бы проверить запрошенный идентификатор, но тогда я бы потратил ресурсы на эту проверку.

Есть мысли?Я работаю с django, но общая стратегия для любого языка программирования будет хорошей.Спасибо.

Ответы [ 3 ]

4 голосов
/ 19 января 2012

Во-первых, выбор между GET и POST: пользователь может смоделировать любой вид запроса, поэтому POST вам там не поможет. При выборе между ними лучше всего решить, основываясь на действии, которое пользователь совершает или на том, как они взаимодействуют с вашим контентом. Они получают страницу или отправляют вам данные (очевидным примером является форма)? Для вашего случая получения какого-либо сообщения, GET подходит.

Также стоит отметить, что GET является правильным выбором, если контент подходит для закладок. Обслуживание URL-адреса исключительно на основе ссылки - как вы говорите, «запретить пользователям вручную вводить значение для идентификатора, не обращаясь к нему по ссылке», - ужасная идея. Это вызовет неисчислимые головные боли и, вероятно, не будет приятным для пользователя.

Как общий принцип, избегайте использования первичного ключа записи базы данных . Этот ключ (id = 5 в вашем случае) следует рассматривать исключительно как поле с автоинкрементом для предотвращения конфликтов записей, т. Е. У вас всегда будет уникальное поле для всех записей в таблице. Это поле ID является служебной программой. Не открывайте его своим пользователям и не полагайтесь на него сами.

Если вы не можете использовать ID, что вы используете? Распространенная идиома - использование дата записи, slug или оба. Если вы имеете дело с сообщениями, используйте дату публикации / создания. Затем добавьте текстовое поле, которое будет содержать понятные URL-адреса и описательные слова. Назовите это слагом и прочитайте о моделях Django.SlugField для получения дополнительной информации. Кроме того, посмотрите URL статьи на любом новостном сайте. Ваш окончательный URL будет выглядеть примерно так: http://www.example.com/posts/2012/01/19/this-is-cool/

Теперь ваш URL-адрес дружелюбен, имеет преимущества Google-fu для SEO, позволяет создавать закладки и не поддается предположению. Поскольку вы не полагаетесь на фиксированный произвольный идентификатор внутренней базы данных, у вас есть свобода ... восстановить резервный дамп базы данных, переместить базы данных, изменить идентификатор номера с автоматическим приращением на хэш UUID, что угодно. Заботится только ваша база данных, а не вы как программист и не ваши пользователи.

Да, и не беспокойтесь о пользователе, «запрашивающем несуществующую запись» или «проверяющем запрашиваемый идентификатор» ... вы все равно должны это сделать. Это не потребляет ненужных ресурсов. Так работает сайт, поддерживаемый базой данных. Вы должны подключить запрос к данным. Если запрос неверен, вы 404. Ваш веб-сервер делает это для несуществующих URL-адресов, и вам нужно будет сделать это для несуществующих данных. Оформить заказ Django get_object_or_404 () для идей / реализации.

1 голос
/ 19 января 2012

Есть два способа, которыми я знаю, чтобы сделать это эффективно, поскольку в принципе нет способа помешать кому-либо подделать какой-либо запрос.

Первый - не использовать пустые идентификаторы в параметрах запроса.Вместо этого создайте большое случайное число и сделайте из этого ссылку.Вам нужно будет сохранить таблицу в вашей базе данных, сопоставляя ваши случайные числа с фактическими идентификаторами, которые они представляют, и вам придется в конечном итоге очистить таблицу.Это довольно просто реализовать, но требует некоторого пространства для хранения, а иногда и некоторого управления хранимыми данными.

Второй способ - подписать данные при создании ссылки.Добавляя криптографическую подпись к данным и проверяя подпись при выполнении запроса, вы гарантируете, что только ваша веб-служба могла создать ссылку.Даже если сам запрос «подделан» - возможно, добавлен в закладки, записан, скопирован и вставлен в другой браузер - вы знаете, что ваш сайт уже разрешил этот URL.

Для этого вам нужносоздать код аутентификации сообщений (MAC) с данными, которые вы подписываете (скажем, просто значением 'id', или, возможно, идентификатором и временем, когда вы подписали данные) и с секретным ключом, который вы храните только на своемserver.

Затем, по вашему мнению, вы берете значение id (или id и метку времени, если это то, что вы используете), и вы снова создаете MAC, и смотрите, совпадают ли они.Если есть какая-либо разница, вы отклоняете запрос как подделанный.

Посмотрите документы на python для модуля hmac , а также модуль hashlib длявсе детали.

Вы можете создать ссылку на Python, как это:

settings.py:

hmac_secret_key = '12345'

views.py:

import time, hmac, hashlib
from django.conf import settings

def some_view(request):
    ...
    id = 5
    time = int(time.time())
    mac = hmac.new(
        settings.hmac_secret_key,
        '%d/%d' % (id, time),
        hashlib.sha1)
    url = 'http://www.example.com/posts/id=%d&ts=%d&mac=%s' % (
        id, time, mac.hexdigest())
    # Now return a template with that url in it somewhere

Чтобы проверить это в другом представлении, вы должны использовать код, подобный следующему: (предупреждение, предупреждение, не надежный, много проверки ошибок еще предстоит сделать)

def posts_view(request):
    id = int(request.GET['id'])
    ts = int(request.GET['ts'])
    mac_from_url = request.GET['mac']

    computed_mac = hmac.new(
        settings.hmac_secret_key,
        '%d/%d' % (id, time),
        hashlib.sha1)

    if mac_from_url <> computed_mac:
        raise SomeSecurityException()

    # Now you know that the request is legit. 
    # You can check the timestamp here, too, if you like.
0 голосов
/ 19 января 2012

Я не знаю, является ли это правильным способом, но, возможно, вы можете сохранить URL-адрес, который он будет перенаправлен после запроса GET в сеанс, и написать decorator, чтобы, еслисеанс имеет этот URL, чтобы привести его на эту страницу.иначе выдает ошибку 404 или что-то в этом роде.

...