-
Notifications
You must be signed in to change notification settings - Fork 663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow httpOnly cookie storage #71
Comments
This is super important for me going forward... and it should be for everyone who uses this project. It SEEMS to be rather trivial to set the cookie. Unsure what other side effects this may have. |
@derek-adair I pick the cookie out in nginx and put it in the authorization header. This is a workaround, but it works for me. |
I'll hopefully be submitting a PR for this in the near future! |
Cool, @derek-adair . Looking forward to seeing that! |
I could give it a try too, but I'm not sure how the refresh tokens come into play with cookies.. I think they don't make sense with cookies,
So.. I guess that in order to touch the less amount of code possible, the best solution would be to cookie both tokens, but yeah, the refresh tokens doesn't add any security here |
@mrodal - yes it does, when its an httpOnly cookie. This may only add an extra step, but exposing your tokens to native, insecure browser api's is not good. https://blog.codinghorror.com/protecting-your-cookies-httponly/ |
I'm pretty sure the scenario described is the way to go. If anyone else has a more thorough understanding of how to secure these tokens i'd love to learn about it. While modern browsers support and prevent reading/writing via httpOnly. there are a fair amount of legacy browsers that allow for reading/writing of these headers. So this will really only slow down script kiddies, but its a pretty low effort feature that will add security. It's my understanding that JWT is inherently insecure and requires certain steps to harden that are likely beyond the scope of this project. I'm certainly no expert on this subject so I welcome any corrections or direction on how to implement this project, or JWT in general... securely. |
what do you mean by "it does"? What I meant is that as far as i know, refresh tokens provide an extra security layer because they are less exposed (they are only used when the access token expires) But if they are stored in cookies, both travel in each request, so I don't see how refresh tokens make it more secure |
@mrodal makes a good point. Refresh tokens are supposed to be sent over the wire less frequently (only when obtaining a new access token or access/refresh pair). It's a bit awkward in the context of web browsers, but JWTs can potentially be used by non-browser clients as well. The access/refresh dichotomy makes more sense in that broader scope. |
IIRC, another option would be that the refresh token cookie is only set for the refresh view's path. Then, it would only be sent over the wire for refresh requests in particular. |
@mrodal it's my understanding that httpOnly + same site cookie is more secure than storing it in memory or localStorage (XSS vulnerability). If the refresh token is not persisted in a cookie where would you suggest storing it? Honestly this shit is soooo obtuse... this is the healthiest dialog i've participated around this subject. I'm almost convinced to NOT implement JWT for web browsers. However, i'm not sure the alternative for a single page app.
Could you elaborate? Didn't know it was possible to do this. |
Does django mitigate XSS? As someone who possesses JUST enough knowledge about security to fuck it up... i'm quite confused as to how to implement ANY jwt setup securely (in a web browser). |
I would actually not use it at all when using cookies.. Since it requires extra logic in the front-end and, as I said before, I don't see the benefit from using it. This is what Im doing in a mobile app that's also going to be a spa in the future. Take into account that storing it in the cookie makes you possibly vulnerable to CSRF attacks if you don't use CSRF tokens, since the auth token is automatically added to each request...
If you are using django templates, you are protected by default when printing data from the server side. But you could still be vulnerable if you generate content on the front-end, although last time I checked, browsers also help quite a lot to prevent these attacks. |
So just make them re-auth when it expires and/or store the actual token for longer?
Now that you mention it... man i wish i would have came to that conclusion before I implemented a replay request thing in my redux app.... ha!
Right, it's clear this is where the miscommunication.
What about if you are using CSRF tokens? Should be good is my understanding. |
Jeeeeez. So the refresh token seems to be useless in a browser... however.... httpOnly cookie still seems essential for the access-token. |
Make the expiration time longer.. assuming https is being used, the risk of it being captured in the middle is negligible, and since its an httpOnly cookie, from js it cant be accessed either.. the only possibility I see is an attacker gaining physical access to the device and copying the cookies, but that I believe is always present anyways
Yeah, if you are using both CSRF and httpOnly cookies for the access token you are pretty much covered Please someone correct me if I'm missing something |
@derek-adair This is what I was talking about with the cookie path. If the refresh token is set for the refresh view path, the browser will only include a What @mrodal is saying about CSRF also applies. Since cookies are automatically sent with any request that matches them, they don't care about what initiated that request or from where it originated. So you can't really tell if the user triggered the request intentionally. You need to include the CSRF token with the request so that Django can verify the request came from an "authorized" source e.g. a redux app hosted on a trusted server. All this stuff is bothersome but necessary. Since JWTs are still relatively new tech, they require that you think more about the nuts and bolts of auth system and why they work the way they do. As XSS goes, Django does provide out-of-the-box protection against it, but it's not perfect. There are still certain cases in which it can be disabled whether intentionally or unintentionally. You still need to think about how it could happen and verify that the possibility doesn't apply to you. |
An HttpOnly Cookie access token combined with a Refresh on a certain url (refresh view) looks nice to me. As for SPA-clients, you can protect against CSRF by setting the x-requested-with header and checking that one on the server (see https://markitzeroday.com/x-requested-with/cors/2017/06/29/csrf-mitigation-for-ajax-requests.html). @davesque How would you cover both web and mobile scenario's? I have the same django rest backend for web and mobile. As for mobile, it's ok to send both tokens and store them on the device (secure storage it is). When handling a web-request I want to use the cookie tokens? |
I am very... confused as to the purpose of this? Why would you use JWT authentication for web when you have session cookies already? So assuming this is for the web application / the website, here's my opinion: If anything, you're going to be implementing a lot more JS which means a new point of attack (as previously mentioned in the XSS and CSRF token misplacements). Seems more like a headache to me; providing this would be a disservice. DRF should really only be used for mobile application and occasionally be used with AJAX on an insecure endpoint. Sure, you can tack on some IP rate limit, but, in reality, you should be using a user rate limit and sending 403 forbidden back to unauthenticated users. But with a user rate limit, you're sending refresh tokens via http which can easily be eavesdropped on... so again: What's the point of this? Feels more like a security vulnerability to users than a practical feature. Also a reminder: I could also be interpreting this issue incorrectly :) lemme know if I did. |
@Andrew-Chen-Wang I just think we need both :-) I have one API for both web and mobile, would like to use cookies for the websession part and jwt for API auth. If we can't differentiate the method (cookie vs jwt) then the jwt-token should be stored in a httponly cookie, not readable by js. In this case a few more measures need to be taken to further secure the token. https://blog.logrocket.com/jwt-authentication-best-practices/ |
Shouldn't we store the refresh token rather than the access token inside the http only cookie. JWT access tokens are best stored in memory with a countdown to refresh using the refresh endpoint. If you store the access token in the http cookie, like this patch/plugin suggests, where do I store the refresh token. |
I liked @davesque's idea of storing both in HttpOnly cookies but setting the refresh token cookie for the refresh path only. This way there's no need to store tokens in localStorage but the refresh token isn't sent with every request. |
What's the progress on this issue? |
I've made a fork using a pull request allowing httponly and merged it with the current master. |
Definitely false. |
@Andrew-Chen-Wang This is a really heated and opinionated subject to the point where some have sworn JWT is insecure by design. You can read up on why you would want to store your tokens here. Honsetly not sure of the quality of that article I just really am sick to death of this subject. EDIT:
You set and read cookies. It's really not any different than any other storage method. The ideal solution would be transparent to the client and have no effect if you do not want to use secure cookies. |
Hi, I have been reading through the whole issue without being able to answer this question : I do not blame anyone, the answer is certainly here, but I am not able find it. EDIT: All right I think I found the answer. For other lost people like me : NO, it is not currently possible. Although @ahmedosama5200 shared a very useful piece of code. EDIT 2 : Ok that piece of code works great and now I am wondering why not using this method ? I can probably make a PR if you do not have time ! EDIT 3 : Well actually it does not work as expected in all cases and things are too complicated for me to understand. |
@leogout I think I've come to the conclusion of what people want: currently Django's session middleware is implementing stateful sessions. What people want here is stateless sessions, meaning it's backed by nothing. For both, we encrypt and hit the database to make sure the user is there (ref: https://code.djangoproject.com/ticket/23011). But the statefulness of sessions middleware is we back it by a cache/database. If we want stateless sessions, then the only move to do is have a single token. Not necessarily a refresh or access; it's just the amount of time before a generated token is invalid. So to answer your question directly, yes but why bother? |
@Andrew-Chen-Wang My thought exactly. I just went with a simple LocalStorage + Authorization header solution. |
Hm, local storage is still not safe. Lemme elaborate throughout my learning process here: To me, stateless authentication is still not safe in the hands of the developers since we're all so bad at following rules and standards and protocols etc. Luckily, the default in this repository is the secret key for your Django application, but if you use a weak algorithm, then you secret key can be leaked. I've said it before but giving too much flexibility to the developer when it comes to security is not good. For example, since you mentioned localStorage, you're at risk for losing all user's confidence in a XSS scripting attack. httpOnly cookies avoid XSS for authorization details. Even if you've lost to a XSS attack with the cookies, at least they can't deal damage to ALL users' confidentiality/details, only some certain details. Twitter gets a XSS attack a bunch, but people's usernames aren't changed. I think the localStorage option has been the default for every tutorial. I want to merge one of the two PRs for httpOnly cookies soon just to experiment and watch out for CVEs. |
I'm really confused. Should I use session or jwt for react app? |
Sure jwt
…On Sun, 6 Dec 2020, 8:20 am RizeXor, ***@***.***> wrote:
I'm really confused. Should I use session or jwt for react app?
Also i might make a mobile app in the future, so what should i use then?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#71 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AELSCEYGVWUWVLLBGTAQXPLSTMPBZANCNFSM4GSRP6LA>
.
|
@RizeXor You should use JWT for mobile apps for security reasons (not secure connections, no csrf token, etc.). You don't have cookies to set in transit for mobile apps, so you use JWT. When it comes to a ReactJS app, the current implementation method would be JWT. There's already a repository set up for you that you can download as a template so that you don't need to set it up yourself. Ref #263 |
To implement this for now as a workaround, you can do the following. Add this to views.py: # views.py
from rest_framework_simplejwt.views import TokenRefreshView, TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenRefreshSerializer
from rest_framework_simplejwt.exceptions import InvalidToken
class CookieTokenRefreshSerializer(TokenRefreshSerializer):
refresh = None
def validate(self, attrs):
attrs['refresh'] = self.context['request'].COOKIES.get('refresh_token')
if attrs['refresh']:
return super().validate(attrs)
else:
raise InvalidToken('No valid token found in cookie \'refresh_token\'')
class CookieTokenObtainPairView(TokenObtainPairView):
def finalize_response(self, request, response, *args, **kwargs):
if response.data.get('refresh'):
cookie_max_age = 3600 * 24 * 14 # 14 days
response.set_cookie('refresh_token', response.data['refresh'], max_age=cookie_max_age, httponly=True )
del response.data['refresh']
return super().finalize_response(request, response, *args, **kwargs)
class CookieTokenRefreshView(TokenRefreshView):
def finalize_response(self, request, response, *args, **kwargs):
if response.data.get('refresh'):
cookie_max_age = 3600 * 24 * 14 # 14 days
response.set_cookie('refresh_token', response.data['refresh'], max_age=cookie_max_age, httponly=True )
del response.data['refresh']
return super().finalize_response(request, response, *args, **kwargs)
serializer_class = CookieTokenRefreshSerializer Change the urls in url.py to use those views for token obtaining and refreshing: # url.py
from .views import CookieTokenRefreshView, CookieTokenObtainPairView # Import the above views
# [...]
urlpatterns = [
path('auth/token/', CookieTokenObtainPairView.as_view(), name='token_obtain_pair'),
path('auth/token/refresh/', CookieTokenRefreshView.as_view(), name='token_refresh'),
# [...]
] Check your CORS settings if it doesn't work as expected: maybe you have to set sameSite and secure in set_cookie Workflow - obtain token pair using credentials
Workflow - obtain access (and optional refresh) token using refresh token
|
Please ref #360 and a specific comment made by stunaz ;) as I'm planning on shutting these ideas, issues, and PRs down. |
I think you should override |
This looks well and good but is the CSRF token not necessary for methods like POST, PUT? |
Hey, I was wondering if you could share how you modified these views and the frontend to implement CSRF as well. |
@davesque Besides the refresh-token via cookie being more secure there is another important point. Jwt usecase is as follows: an authorization service + regular services. The former issues tokens, the latter use access tokens. What if there is multiple regular services and they belong to the same organization? And that organization doesn't want to ask the user to log-in multiple times? So you have to pass the refresh token to every client (for every "regular service" that you have). And require the user to log-in multiple times. Which could be solved otherwise by setting a cookie for the auth service. |
This issue has been open for going on four years.
This is a key feature for any server-side JWT library. I can’t imagine using JWT in the browser using anything but httpOnly cookies. |
any update on this ? |
any update on this? thanks. |
Are you kidding me?! After about four years, this critical feature has not been added?! |
Refer to this |
I commented about this last August, waited a month and then implemented my own JWT app. After reviewing drf-sjwt's code I came to the conclusion that it shouldn't be used in production. Even when I was looking for code to copy and paste as a starting point for a fork I couldn't find anything. It's been a few months so I can't point to all the questionable code, but, look for yourself. Here's a "for example" tho: drf-sjwt's config code uses a non-public DRF code that DRF explicitly flags as internal: Here's the note from DRF and here's where it's used in drf-sjwt. It looked like it was working but I was able to break it during testing. My advice, roll you own. It took me about three months to create/test configurable auth/RBAC/ABAC+registration apps based on simplejwt. It wasn't bad. |
It's been years. Any update? |
@MohammadSalek After reading the source and taking into account issues like this one, I came to the conclusion that See my comment above for more details. |
any update on this? thanks. |
Similar to #23 but with a different motivation.
To protect against XSS, I would like the option to store the JWT in an HttpOnly cookie.
django-rest-framework-jwt
has this feature as an optional setting but that project I believe is abandoned and also has a vulnerability due to preventing the usage of django's CSRF token (see: jpadilla/django-rest-framework-jwt#434). Combining an HttpOnly cookie with CSRF token would be a pretty rock solid solution.References:
https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage
https://stackoverflow.com/questions/44133536/is-it-safe-to-store-a-jwt-in-localstorage-with-reactjs
The text was updated successfully, but these errors were encountered: