Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django is there a benefit of checking every view with `method=='POST':`

Is there a benefit to starting every one of my view functions with if request.method=='POST': or if request.method=='GET':? Or would I just be adding unnecessary lines of code?

I've followed a few examples where views for Ajax are all checking if the HTTP is made with GET.

Could it, for example, prevent DDOS from latching on to a POST method and hammering it with GETs? Or, more practically, prevent API consumers from incorrectly PATCHing when they should PUT or POST?

def employee_delete(request, uid):
    if request.method == 'DELETE':

def employee_detail(request, uid):
    if request.method == 'GET':

def employee_create(request):
    if request.method == 'POST':

def employee_update(request, uid):
    if request.method == 'PUT':
like image 224
Kalanos Avatar asked Sep 22 '18 20:09

Kalanos


People also ask

Which of the following is requires that a view only accepts the GET method?

Decorator to require that a view only accepts the GET and HEAD methods. These methods are commonly considered “safe” because they should not have the significance of taking an action other than retrieving the requested resource.

How to use decorators in django class based views?

To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the dispatch() method of the class. The decorators will process a request in the order they are passed to the decorator.

What decorator is used to require that a view accepts only the GET and HEAD methods?

__doc__ = "Decorator to require that a view only accepts safe methods: GET and HEAD."

What does post mean in Django?

The code is usually used in conditional statements, to distinguish between code for processing a submitted form, and code for displaying an unbound form: if request. method == "POST": # HTTP Method POST. That means the form was submitted by a user # and we can find her filled out answers using the request.


2 Answers

Is there a benefit to starting every one of my view functions with if request.method=='POST':

Yes, even if you only support one method, it is better to guard this. The HTTP protocol specifies that GET requests, should not have side-effects (well effects in the sense of counting visitors are probably no problem, but not something that changes the "entities" of your business logic is strictly speaking not acceptable).

Now "web crawlers" (for example used by search engines or scrapers) typically detect links on a page, and make GET requests on these links (since they aim to "discover" new pages). If there is a view behind this URL that, for example, deletes an employee, it can happen that accidentally a "web crawler" will edit your database.

Other methods like GET, HEAD, PUT and DELETE should be idempotent (that means that making the same request twice, should have the same side-effects, as making the request only once).

So by not "protecting" your views, you lose a layer of "protection" against accidental misuse of your webserver.

A hacker could also aim to make a request with another method, and see how the server responds in a search to find exploits: for example, look if a server makes certain assumptions on the method that fail when performing a DELETE request. For example, a rather generic view implementation that handles all methods, could - if not guarded - unintentionally allow the deletion of files (for example you write a generic view to create and edit content can be "misused" by a hacker by using a DELETE request that the parent view implemented, but should not be supported for that specific entity).

In the early days, some HTTP webservers for example did not check authentication when a HEAD request was used. As a result, a hacker could, by trying several HEAD requests "scan" the id space, and thus obtain knowledge what id's were filled in in the database. Of course that in itself does not leak much data, but it is a vulnerability that can be used as a first step in hacking data.

Note that although Django has some protection against this when using, for example, class-based views, a person can just use any string for the request. So a person can write as method FOOBAR. If the view for example specifies if request.method == 'POST', and an else: statement, it can thus be used, to enter the else statement with a non-GET method.

But regardless of the use-case, "better be safe than sorry", and guarding the HTTP methods, is just one of the aspects to check.

That being said, if only a subset of methods are allowed, you can use the @require_http_methods [Django-doc] decorator:

from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def my_view(request):
    # I can assume now that only GET or POST requests make it this far
    # ...
    pass

This decorator thus makes it more elegant to guard that the proper method is used.

like image 123
Willem Van Onsem Avatar answered Sep 19 '22 16:09

Willem Van Onsem


To offer a different perspective, I think your question illustrates why you should consider using class-based views, which make life so much simpler when dealing with such problems.

For example the generic CreateView already comes with all the logic built in to restrict the type of HTTP request. It will let you perform a GET request to initialise a form, but require a POST request to process data. Thus you can't accidentally trigger data to be saved through a GET request.

It also provides the framework for proper form data validation, error handling etc - which you would have to implement yourself in a procedural view.

Same goes for the range of other views that Django provides - UpdateView, DetailView etc.

All Django class-based views come with a http_method_names attribute that you can use to control which methods are allowed on your views, e.g.,

from django.views.generic import View

class MyView(View):

    # only GET and POST allowed. Anything else will get a 405 Method Not Allowed response.
    http_method_names = ['get', 'post']

    def get(self, request, *args, **kwargs):
        # Logic for GET requests goes here.

    def post(self, request, *args, **kwargs):
        # Logic for POST requests goes here. No risk of it getting mixed up with GET.

This in addition to providing a lot of other helpers for things like form handling, template loading etc. Procedural views may feel simpler initially, but you will quickly realise that you end up having to write a lot more code to get them to do what you need.

like image 44
solarissmoke Avatar answered Sep 20 '22 16:09

solarissmoke