Django admin error CSRF cookie not set, always admin page not rendering - https

I got issue at rendering in django admin panel at anything, either for POST form or not , when I changed my project from HTTP to HTTPs , the issue only in the admin panel , it shows me CSRF cookie not set.
settings.py
# HTTPS settings & Security
CSRF_COOKIE_DOMAIN = '.example.com'
CSRF_TRUSTED_ORIGINS = ['https://example.com']
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
views.py
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
#csrf_exempt
def login(request):
pass
Error
Forbidden (403)
CSRF verification failed. Request aborted.
You are seeing this message because this site requires a CSRF cookie when submitting
forms. This cookie is required for security reasons, to ensure that your browser is not
being hijacked by third parties.
If you have configured your browser to disable cookies, please re-enable them, at least
for this site, or for “same-origin” requests.
Help
Reason given for failure:
CSRF cookie not set.

Related

What is the point of X-CSRF-TOKEN or X-XSRF-TOKEN, why not just use a strict same site cookie?

Frameworks such as laravel and others require you place the csrf token in your HTML forms.
However at the same time laravel comes by default with the VerifyCsrfToken middleware that automatically creates a X-XSRF-TOKEN cookie with the csrf token on every response. This cookie is used for ajax requests and is automatically added to the header for axios for example.
I am wondering why is it required to add the csrf token to every HTML form. Why could you not just use the already existing X-XSRF-TOKEN cookie to validate the csrf token. I understand there is the issue of same site cookies, and if your csrf cookie is set to lax or none the cookie would be sent from an external site if they would POST to my site. However this issue can be solved by setting the same site to strict then there would be no need to set the csrf token on every form which is kind of annoying to do and remember.
Is there some security concern I am missing on why we just cant use a strict cookie for validating the csrf token?
An X-CSRF-Token protects users against unwanted execution of modifying requests, which are of interest for their side effects (the changes which they make to the server, or the database), not for their response, which the attacker cannot read anyway, by virtue of the CORS protocol.
A same site cookie would protect even against execution of navigation requests, which do not change anything on the server, but only read data (including X-CSRF-Tokens for subsequent modifying requests), which is then displayed in an HTML page. For example, if stackoverflow.com had same site session cookies, you would not be able to navigate from your webmail site via a mailed link to this StackOverflow question and immediately click to upvote it, because the session cookie would not be included in the navigation request, therefore you would not be logged on at first.
SameSite cookies do indeed provide significant protection against CSRF attacks.
But it's always better to put an explicit counter-measure in place - that is provided by anti-CSRF tokens.
For one thing, SameSite uses a notion of "registerable domain" so it does not protect you against subdomain hijacking
Finally, for these topics I very much recommend an excellent book Api Security in Action - they discuss CSRF and related topics in Chapter 4.
there would be no point in validating csrf token through cookies. That's the problem we are trying to solve. If csrf token was sent and validated as a cookie, it also could be sent, and is sent in cross site request. But when doing cross site request, as far as I know, attacker can't read that cookie with js and put it inside the form, only we can access that cookie with js. That's because when we set a cookie we specify domain attribute, and that cookie can be read with js, only on that particular domain. That's the reason why that cookie is not http only, and why we include it inside forms.

Does Laravel CSRF protection provide 100% safety?

Laravel documentation says:
Laravel automatically generates a CSRF "token" for each active user session managed by the application. This token is used to verify that the authenticated user is the person actually making the requests to the application. Since this token is stored in the user's session and changes each time the session is regenerated, a malicious application is unable to access it.
Laravel config session.php file guarantees session cookie lifetime is 120 minutes by default:
'lifetime' => env('SESSION_LIFETIME', 120)
So let's imagine, for example, I authenticate into the Laravel app and receive session cookies. What will happen if within 120 minutes after authentication I will go to a malicious website and get exposed to CSRF attack? Of course, considering the fact cors.php config is set to allow accept any (*) origin ('allowed_origins' => ['*']).
In my current understanding within these 120 minutes after authentication browser has the session cookie, so if I go to a malicious website and get exposed to CSRF attack, the attack will be successful.
Please correct me if my current understanding is wrong?
So the problem with my understanding was that I was not aware of the fact you can't access a cookie of the origin that differs from website you are trying to access it. So in case of csrf, origin of malicious website differs from the origin of CSRF-TOKEN cookie which is provided by Laravel server, therefore attacks fails. So yeah laravel csrf protection works.
Full explanation of CSRF protection to the beginners as myself:
What is csrf attack?
Imagine, you authenticated into website with domain A, and received the session cookie from server that serves the site A. Annother malicious website with domain B contains a script which produce a request to the server which serves domain A as you enter the site B. As long your browser contains the session cookie of website A, the script attempting to attack from website B will be successful.
So how does csrf token help to cover this vulnerability?
Now laravel server sends you response with XSRF-TOKEN cookie, when you try to send axios request with script from domain A, axios automatically places value of XSRF-TOKEN to X-XSRF-TOKEN header in case of same-origin request (when website A has domain of the same origin as the server). In case of malicious website with non same-origin request, script can't access the XSRF-TOKEN cookie because it is not possible to accesss a cookie of another origin. So axios can't place a value of XSRF-TOKEN to a requst header or a request parameter. Server examines incoming request for X-XSRF-TOKEN or csrf token parameter, server is not able to find it, and so therefore server doesn't validate the request.

laravel 419 expired error for get method or gateway link (a tag) when callback

By default, error 419 is displayed in Laravel when enter or callback from bank gateway by using the post or get method or link (a tag). The problem is solved when I make changes to the session file as follows:
SESSION_SECURE_COOKIE = true;
same_site = none;
but some users still have a 419 error when callback from the bank gateway.
All caches has been cleared.
force redirect to https has been set;
set all callback urls in VerifyCsrfToken.php
and
SESSION_DOMAIN = null
SESSION_LIFETIME = 525000
This only happens to some users.
please help me. thank you
If you are handling sessions as cookies, then some browser caches may be holding on to cookies without the new headers.
This would explain why it occurs for some users and not others (especially if your cookies have long expiry).
If possible, get one of your end users to clear their cookie for you site using the browser development tools. For Chrome it should be something like:
F12 -> Application -> Cookies -> "Clear All"

python-keycloak package with KeycloakOpenID : logout does not work

I have a keycloak docker container (pulled image jboss/keycloak ) and a Django 2.2 web container. For integration of django with keycloak social-auth-app-django was used. Login works fine. Now trying to implement logout using python-keycloak following instructions described here:
https://github.com/marcospereirampj/python-keycloak :
from keycloak import KeycloakOpenID
keycloak_openid = KeycloakOpenID(server_url="http://<my IP>:8080/auth/",
client_id="<client_id>",
realm_name="<my realm name>",
client_secret_key="<my secret>",
verify=True)
config_well_know = keycloak_openid.well_know()
token = keycloak_openid.token("<username>", "<password>")
print(token) # all tokens returned ok
userinfo = keycloak_openid.userinfo(token['access_token'])
print ("userinfo:", userinfo) # userinfo returned ok
keycloak_openid.logout(token['refresh_token'])
in the container log:
Some clients have been not been logged out for user <username> in <my realm name> realm: <client_id>
No logout happens, still can browse the site.
What's missing? Thanks
UPDATE
Maybe I understood the problem. The token I get from keycloak_openid.token() call is not the token that was generated for me at the moment of login. The only token that can be fed to keycloak_openid.logout() call for it to work is that original token ('refresh_token' key value of the token dict, to be specific). Calling keycloak_openid.refresh_token() also issues a new token which is rejected as logout credential. But the originally issued refresh_token does not seem to be stored anywhere - sessions, cookies or keycloak db. (Note: I did find access_token, it's in the django DB in social_auth_usersocialauth table, but I need refresh_token). However, it's dumped to the console output at the moment of login, so if I copy it and call keycloak_openid.logout() with it, it does logout from keycoak. The question is where can I find that original refresh_token?
I used to experience the same issue. What helped was
Going to admin page and location your user in the realm
open your browser's developer console and monitor the networks
Go to sessions tab on keycloak and click log out
Observe which end point is being called and mimic that in your python backend, with proper header in the request.
Hope this helps!
I understand that this question is outdated, but I managed to logout by this:
Add the following variables to settings.py:
SOCIAL_AUTH_KEYCLOAK_LOGOUT_URL = 'https://your-keycloak/auth/realms/your-realm/openid-connect/logout'
SOCIAL_AUTH_KEYCLOAK_EXTRA_DATA=[("refresh_token","refresh_token")]
Now it will save the refresh token in extra_data.
Add into urlpatterns list in urls.py:
url(r'^logout/$', views.logout, name='logout'),
Add the logout view with communication code to views.py:
from django.contrib.auth import logout as auth_logout
import requests
def logout(request):
if request.user.is_authenticated:
user = request.user
if user.social_auth.filter(provider='keycloak'):
social = user.social_auth.get(provider='keycloak')
access_token=social.extra_data['access_token']
refresh_token=social.extra_data['refresh_token']
#logger.debug(access_token) # you can view the tokens
#logger.debug(refresh_token)
logout_request_data={"client_id": settings.SOCIAL_AUTH_KEYCLOAK_KEY, "refresh_token": refresh_token, "client_secret": settings.SOCIAL_AUTH_KEYCLOAK_SECRET}
headers={"Authorization" : "Bearer "+access_token,"Content-Type" : "application/x-www-form-urlencoded"}
result=requests.post(settings.SOCIAL_AUTH_KEYCLOAK_LOGOUT_URL,data=logout_request_data,headers=headers)
auth_logout(request)
return redirect('/')
result code will be 204 on success.

Devise scrubbing Sessions on Post?

I have setup devise and omniauth based authentication and it has been working fine for the most part. After login, I have the current_user set and using before_filter :authenticate_user! It works mostly as expected. The session sticks even when I (manually) go to other sites. When I come back to my site, I am still logged in.
I ran into issues while trying to integrate with a payment gateway provider.
I do a form post to the payment gateway which does its thing and then returns the user back to my site by doing a form post. When the user lands on my site, the cookies are scrubbed and the authentication is lost. This means that the current_user is now nil.
The flow is something like
Order - > Login -> Confirm -> PaymentGateway -> CallBack(MySite)
| ^
| -- loggedin -- |
-----------------
I further investigated using Firefox's inspect and it looks like the cookies are set correctly. For example after I login the cookie is _session_id:f58b52e6c168178711ba66aa3ac9d637, When I return from the Payment Gateway the cookie (according to firefox) is _session_id:f58b52e6c168178711ba66aa3ac9d637 .. This would lead me to believe that I would stay authenticated. However when I print current_user I get nil.
I also print the cookie in my controllers and it is correct :
In The confirm action when I print the cookies I get
Cookies #<ActionDispatch::Cookies::CookieJar:0x000001017c33d0, #set_cookies={}, #delete_cookies={}, #host="localhost", #secure=true, #closed=false, #cookies={"_session_id"=>"f58b52e6c168178711ba66aa3ac9d637"}>
In the CallBack(MySite) page if I print the cookies, I get
Cookies #<ActionDispatch::Cookies::CookieJar:0x0000012b2225f0 #set_cookies={}, #delete_cookies={"remember_user_token"=>{:path=>"/"}}, #host="localhost", #secure=true, #closed=false, #cookies={"_session_id"=>"f58b52e6c168178711ba66aa3ac9d637"}>
What am I missing here. Why is devise deliberately logging me out. And is there a way I can remain logged in ?
Edit:
I also tried rememberable ... That also does not work and scrubs the cookie.
In The confirm action when I print the cookies I get
Cookies #<ActionDispatch::Cookies::CookieJar:0x0000012b286fc8 #secret="4ccae7dce24d26dcf98220fb1e54cacf412efe1bf7dc003e1e5315abf50f5c4ep9g6i62b8a9d83cf3501564c7bd70ca7722db85a46015652c3d7fa4f156d4d0b", #set_cookies={}, #delete_cookies={}, #host="localhost", #secure=true, #closed=false, #cookies={"_session_id"=>"f63c3165f807b15e4a4e7de4a615d012", "remember_user_token"=>"BAhbB1sGaQYiIiQyYSQxMCRxVGJoVzEydjJtQ1Y4dndUeTdJMVp1--099213f62d5e317d9f83e758ac5995395e63ce7f"}>
Note that the remember_user_token is set
In the CallBack(MySite) page if I print the cookies, I get
#<ActionDispatch::Cookies::CookieJar:0x0000012b410100 #secret="4ccae7dce24d26dcf98220fb1e54cacf412efe1bf7dc003e1e5315abf50f5c4ep9g6i62b8a9d83cf3501564c7bd70ca7722db85a46015652c3d7fa4f156d4d0b", #set_cookies={}, #delete_cookies={"remember_user_token"=>{:path=>"/"}}, #host="localhost", #secure=true, #closed=false, #cookies={"_session_id"=>"f63c3165f807b15e4a4e7de4a615d012"}>
So a before filter has deleted the remember_user_token cookie, In firefox's inspect I can confirm that both cookies are sent to the site and to rails.
Either way devise is logging me out when the incoming action is a post from an external site. Is this expected behavior, Can I change it.
You need to disable the request from forgery protection when the external website is posting (or for that specific action), otherwise Rails' protection mechanism against CSRF attacks is going to (correctly) log you out.

Resources