I'm working on a Django project and I can make a CSRF-attack from an external url or file. How I can block it?
The attack consist:
I create a file with this content:
<html>
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://XXXXXX.com/YYYYY/AAAAAA/LLLLLL">
<input type="submit" value="Submit request" />
</form>
</body>
</html>
I login on my page
I open the file in the same browser
Submit the button
The request is accepted and the action is executed.
Thanks for everything :)
Solved
django.middleware.csrf.CsrfViewMiddleware does not provide csrf protection if the request is GET
# Assume that anything not defined as 'safe' by RFC7231 needs protection
if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if getattr(request, '_dont_enforce_csrf_checks', False):
# Mechanism to turn off CSRF checks for test suite.
# It comes after the creation of CSRF cookies, so that
# everything else continues to work exactly the same
# (e.g. cookies are sent, etc.), but before any
# branches that call reject().
return self._accept(request)
Change your method to post and add csrf token
<html>
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://XXXXXX.com/YYYYY/AAAAAA/LLLLLL" method="post">
{% csrf_token %}
<input type="submit" value="Submit request" />
</form>
</body>
</html>
and handle your view inside :
if request.method == 'POST':
# your logic here
Make sure that 'django.middleware.csrf.CsrfViewMiddleware' should come before any view middleware that assume that CSRF attacks have been dealt with.
Related
I made a custom password_reset_confirm.html template. But when a user enters a new password and hits submit, the browser does not redirect to the admin view password_reset_complete.
Here's the form I made in the custom password_reset_confirm.html template:
<div ng-app="app" ng-controller="Ctrl">
<form id="reset-pw-confirm-form" name="newPWForm" method="post" action="">
{% csrf_token %}
<input
id="id_new_password1"
type="[[[ newPW.showPW ? 'text' : 'password' ]]]"
name="new_password1"
ng-model="newPW.pw"
ng-minlength="8"
ng-maxlength="32"
required>
<button class="btn btn-primary" type="submit" ng-disabled="!newPW.pw">Submit</button>
<input
id="id_new_password2"
type="hidden"
value="[[[ newPW ]]]"
name="new_password2"
ng-model="newPW"
ng-minlength="8"
ng-maxlength="32"
required>
</form>
</div>
When I fill out the password and hit submit, the browser sends a POST request to the same URL it landed on, but the page seems to just refresh with nothing changed. The user's password remains unchanged. It seems Django's auth/views.py did not execute properly.
In that view, there's this code:
if post_reset_redirect is None:
post_reset_redirect = reverse('password_reset_complete')
else:
post_reset_redirect = resolve_url(post_reset_redirect)
When I have the view print post_reset_redirect, it prints None. Could this be the issue?
How can I make my custom template compatible with Django's password_reset_confirm view?
When you specifies "action" attribute for the form it will be used as your link for data sending so probably your logic isn't handled. Try to remove it and check you js files that the data is sent to the the specified link.
Also please check all required parameters for the password_reset_confirm
https://docs.djangoproject.com/en/1.8/_modules/django/contrib/auth/views/
My hidden input's value and ng-model attributes needed to be set to newPW.pw.
How to disable the intermediate signout page from django allauth. When the user clicks on the signout link on my site I want him to logout right away, I want to remove this intermediate page
Set ACCOUNT_LOGOUT_ON_GET to True in your settings.
Also see the documentation
Using a GET request is probably a bad idea due to browsers prefetching urls from the URL bar. Chrome (as of right now) is pretty bad for this; it'll send a GET request to pages it think you'll hit enter on when typing in your URL bar.
Plus, people can add a link such as <img src="https://example.com/account/logout/"> and you'll be logged out. That's not a security risk since it's logging you out, but it is certainly annoying for your users.
Instead, you should consider using a POST request using a form with CSRF. Django Allauth already comes with this. Here's the <form> from the intermediate signout page:
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}
<button class="STYLE_ME" type="submit">Logout</button>
</form>
In my case, I just added this to the site header and made the submit <button> look like every other link using CSS so it feels the same to them, but the form will use a POST request.
But if that's not a solution you can implement for any reason, open your settings.py file (or your main settings file) and set:
ACCOUNT_LOGOUT_ON_GET = True
^ The above setting will do what you need. For further Django Allauth settings, check out their configuration page.
Here's another shortcut for preserving the POST request, if you don't want to mess with styling the form button with something like this:
Hide the form:
<form style='display: none;' method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
<input type="hidden" name="next" value="/redirect_target/"/>
<button id="signOutBtn" type="submit">Logout</button>
</form>
Submit with a click event attached to whatever element you've already styled:
$(document).on('click', '#signOutLink', function() {
$('#signOutBtn').click()
});
I am trying to create a login page for my Django website. At first I tried all the built in login functions that Django ships with, however I ran into a problem. My problem is, for the time being the login page has been trying to redirect to the home page. (I have also tried another page, didnt work) Once the user logs in, the redirect page is rendered as an html, however the url stays as /accounts/login/ when it is supposed to be /mobile/ or / and because of this none of the links work, and static files aren't loaded.
Below is my login view code:
def login(request):
if request.method == "POST":
redirect_to = request.POST['next']
print 'method is post'
form = AuthenticationForm(data=request.POST)
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username, password=password)
if form.is_valid() and user is not None and user.is_active:
print 'form is valid'
netloc = urlparse.urlparse(redirect_to)[1]
print 'original redirect_to is: ',redirect_to
# Use default setting if redirect_to is empty
if not redirect_to:
print 'changing redict to default: ', redirect_to
redirect_to = settings.LOGIN_REDIRECT_URL
print 'redirect to is: ',redirect_to
# Heavier security check -- don't allow redirection to a different
# host.
#elif netloc and netloc != request.get_host():
# redirect_to = settings.LOGIN_REDIRECT_URL
# Okay, security checks complete. Log the user in.
print form.get_user()
auth.login(request, user)
print 'user logged in'
return redirect(reverse('builds.views.mobile_view'))
else:
redirect_to = request.REQUEST.get('next')
form = AuthenticationForm(request)
c = {
'next':redirect_to,
'form': form,
}
print 'returning render to response login'
return TemplateResponse(request,'registration/login.html', context = c)
Since the built in login view didn't seem to be working, I figured if i wrote my own it may work, however the problem persisted.
Here is my login html code:
<!-- Start of first page: START -->
<div data-role="page" class="type-interior" id="LOGIN" data-theme="a">
{% if form.errors %}
<div data-role="header" data-theme="a" data-content-theme="a">
<h1>Your username and password didn't match. Please try again.</h1>
</div><!-- /header -->
{% else %}
<div data-role="header" data-theme="a" data-content-theme="a">
<h1>Please Enter Your Username and Password</h1>
</div><!-- /header -->
{% endif %}
<form method="post" action="/accounts/login/" id="login_form">
<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="Login" id="button"/>
<input type="hidden" name="next" value="{{ next|escape }}" />
</form>
<div data-role="footer" data-theme="a">
<h4></h4>
</div>
</div>
Please help me! I feel like I have tried everything. Also please tell me if I should include any other code.
EDIT:
OKAY! I have narrowed down the problem to the java script content I am using in my Header of the login page (I am using jquery). The header is:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<title>Install Builds</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1 maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="{{ STATIC_URL }}blablahblachname.css" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile.structure-1.1.0.min.css" />
<!--<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>-->
<link rel="apple-touch-icon-precomposed" href="{{ STATIC_URL }}static/homepage_icon.png" />
<link rel="icon" href="{{ STATIC_URL }}homepage_icon.png" type="image/vnd.microsoft.icon" />
</head>
Is there a work around for this? Or do I have to completely exclude the javascript and thus have a bland looking login page :(
I think this issue is related to jQuery mobile. You may be able to fix it while keeping the other features of jQuery mobile by adding data-ajax="false" to your form element, as described on jQuery Mobile's form sample. You may need to read up a bit on jQuery Mobile to get AJAX login working, if that's what you need.
Have you tried using the django.contrib.auth.views.login with the next_page parameter? I've used it in my sites and it works pretty well. Please go through this carefully.
I have to pass a parameter in url. I can't send it in as usual GET request the variable and value is shown in the address bar with the request. So, the end user can change the value for this variable value and send request which will be processed.
href="url=/admin/usermanagement/?flag=2
I want to send this hiding flag=2
now this goes as a GET request and it is seen in the address bar. Please give your suggestion if you have any idea on changing this to POST to hide and send the value.
You can still use a html form but "hide" it from the user:
<!DOCTYPE html>
<html>
<body>
<form id="myform" method="post" action="/">
{% csrf_token %}
<input type="hidden" name="flag" value="2" />
Let's go!
</form>
</body>
</html>
And the view:
def index(request):
if request.method == 'POST':
try:
flag = request.POST['flag']
# TODO use flag
except KeyError:
print 'Where is my flag?'
return render_to_response('index.html', {},
context_instance=RequestContext(request))
You can use AJAX to get rid of forms entirely.
Just add this to your JavaScript:
function postTo(url, query) {
var request = (XMLHttpRequest?new XMLHttpRequest():new ActiveXObject());
request.open('POST', url, true);
request.send(query);
}
Then call with something like this:
postTo('/admin/usermanagement/','flag=2');
Note that this will NOT reload the page. If you want to reload the page, use Borges' answer.
while reading up djangobook chapter ,I came across the section which mentions a csrf exploit where a logout link was put in a hidden of malicious site.
In a web app I created using django,I had used a similar logout link
base.html:
<a href="{% url my_logout %}" > Logout </a>
where the my_logout url points to django.contrib.auth.views.logout_then_login
urlpatterns=patterns('django.contrib.auth.views',
url(r'^logout/$', 'logout_then_login', {}, name = 'my_logout'),
)
Now,after reading about csrf attack,I fear that a malicious site can cause trouble for me too.So,I would like to use a form to do the logging out.
I thought I could do like this
base.html:
...
<form method="post" action=".">{% csrf_token %}
<input type="hidden" name="next" value="{{next}}" />
<input type="hidden" name="confirm" value="true" />
<input type="submit" value="Logout" />
</form>
...
Now,how should I write the view for processing this form?If I am to process the hidden variables(confirm to check whether logout should be done and next to go to the previous view) ,will I still be able to use the django.contrib.auth.views.logout_then_login method?
can someone please tell me if I am doing this the right way?
thanks in advance
You could wrap it like
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_POST
#csrf_protect
#require_POST
#never_cache
def safer_logout(request):
# 'confirm' is useless here, POST implies 'do it'
return logout_then_login(request, request.POST.get('next'))
Also, consider using SESSION_COOKIE_HTTPONLY