В части 5 нашего Django урока по созданию опроса мы заставили работать нашу форму голосования. В админке можно создавать опросы, и они будут отображаться. В этой части мы обсудим вопросы безопасности созданного приложения.
1. Попробуйте перейти на детальную страницу опроса, снятого с публикации. Система спокойно позволяет вам это. Давайте сделаем так, что при попытке обращения пользователя по url к опросу, снятого с публикации, он видел сообщение: "Извините, опрос снят с публикации".
Для этого отредактируем файл шаблона: polls/templates/polls/detail.html:
2. На странице просмотра опроса пользователь может указать id несуществующего ответа, что породит ошибку DoesNotExist at /polls/1/vote/ Answer matching query does not exist.
Чтобы ее избежать, отредактируем polls/views.py:
Теперь, если хакер в форме укажет id ответа другого опроса и отправит форму, наше приложение не увеличит счетчик голосов в другом опросе и вернет ошибку: "Указан недопустимый ответ".
3. Если хакер в форме голосования вместо числового id ответа укажет русские символы, например:
то это вызовет ошибку:
UnicodeEncodeError at /polls/2/vote/ 'decimal' codec can't encode characters in position 0-24: invalid decimal Unicode string
Если пользователь укажет вместо числового id знак минуса "-", то это породит ошибку:
ValueError at /polls/2/vote/ invalid literal for int() with base 10: '-'
Теперь, зная типы возникающих ошибок, добавим их в блок except в файле polls/views.py:
Таким образом, мы подавили ошибки при передаче некорректных данных.
Примечание: По идее, мы не должны заботиться о хороших сообщениях для хакеров, усложняя тем самым логику нашего приложения. Для хакера сойдет сообщение Server Error (500), которое будет если в firstsite/settings.py установить:
4. Тем не менее мы все равно имеем шанс проголосовать, к примеру за опрос id = 2, находясь на странице опроса с id = 1. Для этого нам достаточно изменить у формы атрибут action и изменить id ответа. Пример:
<form method="post" action="/polls/3/vote/">
<input type="radio" value="7" id="answer1" name="answer">
<label for="answer1">Картошка</label><br>
<!-- Другие поля формы... -->
</form>
Да, пусть хакер это делает, т. к. это равносильно тому, что он просто откроет открытый опубликованный опрос и там проголосует. Но нехорошо будет если хакер сможет таким образом увеличивать число голосов по уже закрытым опросам. Чтобы этого не произошло, отредактируем polls/views.py:
Таким образом, если кто-то попробует увеличить счетчик снятого с публикации опроса, он получит сообщение "Опрос снят с публикации".
В следующей части урока по Django по созданию опроса мы защитим наши опросы от повторного голосования.
Продолжение - Опрос (ч.7)
1. Попробуйте перейти на детальную страницу опроса, снятого с публикации. Система спокойно позволяет вам это. Давайте сделаем так, что при попытке обращения пользователя по url к опросу, снятого с публикации, он видел сообщение: "Извините, опрос снят с публикации".
Для этого отредактируем файл шаблона: polls/templates/polls/detail.html:
{% if question.is_active %} <h1>{{ question.title }}</h1> {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %} <form action="{% url 'polls:vote' question.id %}" method="post"> {% csrf_token %} {% for answer in question.answer_set.all %} <input type="radio" name="answer" id="answer{{ forloop.counter }}" value="{{ answer.id }}" /> <label for="answer{{ forloop.counter }}">{{ answer.answer }}</label><br /> {% endfor %} <input type="submit" value="Голосовать" /> </form> {% else %} <p>Извините, опрос снят с публикации.</p> {% endif %}
2. На странице просмотра опроса пользователь может указать id несуществующего ответа, что породит ошибку DoesNotExist at /polls/1/vote/ Answer matching query does not exist.
Чтобы ее избежать, отредактируем polls/views.py:
def vote(request, poll_id): question = get_object_or_404(Question, pk=poll_id) if request.POST.get('answer'): try: selected_answer = question.answer_set.get(pk=request.POST['answer']) except(Answer.DoesNotExist): return render(request, 'polls/detail.html', { 'question': question, 'error_message': "Указан недопустимый ответ", }) selected_answer.votes += 1 selected_answer.save() return HttpResponseRedirect(reverse('polls:results', args=(question.id,))) else: return render(request, 'polls/detail.html', { 'question': question, 'error_message': "Вы не выбрали ответ.", })
Теперь, если хакер в форме укажет id ответа другого опроса и отправит форму, наше приложение не увеличит счетчик голосов в другом опросе и вернет ошибку: "Указан недопустимый ответ".
3. Если хакер в форме голосования вместо числового id ответа укажет русские символы, например:
<input type="radio" value="ЭТО_ХАКЕР" id="answer1" name="answer">
то это вызовет ошибку:
UnicodeEncodeError at /polls/2/vote/ 'decimal' codec can't encode characters in position 0-24: invalid decimal Unicode string
Если пользователь укажет вместо числового id знак минуса "-", то это породит ошибку:
ValueError at /polls/2/vote/ invalid literal for int() with base 10: '-'
Теперь, зная типы возникающих ошибок, добавим их в блок except в файле polls/views.py:
def vote(request, poll_id): question = get_object_or_404(Question, pk=poll_id) if request.POST.get('answer'): try: selected_answer = question.answer_set.get(pk=request.POST['answer']) except(Answer.DoesNotExist, UnicodeEncodeError, ValueError): # ...
Таким образом, мы подавили ошибки при передаче некорректных данных.
Примечание: По идее, мы не должны заботиться о хороших сообщениях для хакеров, усложняя тем самым логику нашего приложения. Для хакера сойдет сообщение Server Error (500), которое будет если в firstsite/settings.py установить:
DEBUG = False TEMPLATE_DEBUG = False ALLOWED_HOSTS = ['127.0.0.1']
4. Тем не менее мы все равно имеем шанс проголосовать, к примеру за опрос id = 2, находясь на странице опроса с id = 1. Для этого нам достаточно изменить у формы атрибут action и изменить id ответа. Пример:
<form method="post" action="/polls/3/vote/">
<input type="radio" value="7" id="answer1" name="answer">
<label for="answer1">Картошка</label><br>
<!-- Другие поля формы... -->
</form>
Да, пусть хакер это делает, т. к. это равносильно тому, что он просто откроет открытый опубликованный опрос и там проголосует. Но нехорошо будет если хакер сможет таким образом увеличивать число голосов по уже закрытым опросам. Чтобы этого не произошло, отредактируем polls/views.py:
def vote(request, poll_id): question = get_object_or_404(Question, pk=poll_id) if not question.is_active: return HttpResponse('Опрос снят с публикации') # ...
Таким образом, если кто-то попробует увеличить счетчик снятого с публикации опроса, он получит сообщение "Опрос снят с публикации".
В следующей части урока по Django по созданию опроса мы защитим наши опросы от повторного голосования.
Продолжение - Опрос (ч.7)
Комментариев нет:
Отправить комментарий