I would like to give users access to delete a model instance that they added to the db. In the django docs it says allowing someone to delete from the template is not a good practice. Is there a secure way to let a user click a "delete this" link from the template and remove that model instance? How should I go about doing that?
views:
148answers:
2What is the best secure way to allow a user to delete a model instance that they added to the db?
Check out this question for discussion related to what you are asking about.
Essentially, when you normally click on a link on the page the browser makes a GET
request to the server to get the next page's contents. Just like there is a lot of pushing towards semantically relevant CSS layouts, it is also important that your page requests are semantically relevant. The problem with using links to remove items is that it is making a GET
request to DELETE
something in the database. From this comes the problem that some search engines might index your links and accidentally erase content. There also comes the problem of cross-site request forgeries which can make an unsuspecting user make a command to a website without being aware. So the proper way to handle this is by following the rule that any request that modifies state in the server should be processed via POST. As such, instead of doing this:
<a href="{% url remove_item item.id %}">Delete Item</a>
It is better to do this:
<form action='{% url remove_item %}' method='POST' id='form'>
<input type='hidden' name='action' value='delete'>
<input type='hidden' name='id' value='{{ item.id }}'>
<input type="submit" value="Delete Item">
</form>
If you would like to keep your links while maintaining the POST
, you'd have to resort to Javascript:
<a href="#" onclick="document.getElementById('form').submit(); return false;">Delete Item</a>
Unsightly, yes, but it's for the best. Your Django view would then do something like this:
def remove_item(request):
if request.method == 'POST':
## remove item
Furthermore, as Scott mentions, Django has some built in stuff to help you avoid the cross-site request forgeries I mentioned above, since it is still possible to do it even if you are doing a POST (just slightly harder). The way to avoid this is to have some kind of token tied to the form that needs to be validated server side before allowing the action to be taken. Check out the CsrfMiddleware class for more details on that. It will essentially automate some of that work out of it for you.
Additional Reading
Have the user submit a POST request to delete that model instance. These kinds of changes should never be possible via GET requests, so that people can't link each other to unwittingly performing changes on the site.
In your view, check that request.user is the same as the author of that particular model instance. You could also check that the HTTP_REFERRER is not set to another site if you were really worried.
Your security issue here is Cross Site Request Forgery. Django provides CsrfMiddleware which will actually add security to your forms to prevent this kind of attack. But it only works as long as you're not allowing permanent changes to take place via GET requests.