Consider changing default authorization mode (header mode encourages insecure client architecture)
Due to this article: https://dev.to/rdegges/please-stop-using-local-storage-1i04 storing token client-side on browser applications is insecure. Any server credentials shouldn't be accessible for javascript due to potential XSS vulnerability. There's no secure way to store token on web application when LexikJWTAuthenticationBundle requires "Authorization" header to be present. Therefore I'd suggest to change default authorizarion mode to Http-only cookie with "secure" flag (only https).
Agree - I am in process of switching from regular session based auth to stateless JWT and more information I read, more I understand that header based token is less secure since malicious javascript can access it, if stored in LocalStorage. So recommended way is to use httpOnly (and secure) cookies. I understand that header is preferred in cookie-less environments such as mobile apps but isn't the web api clients more popular? What's the argument behind default settings?
Thanks for opening this issue. I agree.
What's the argument behind default settings?
None except backward compatibility, but we can find a smooth way. I will take care of this unless someone else does
I agree with the fact that sensitive data, including tokens, shall not be set in the local storage. But this does not mean the Authorization header is not secured (or less secured).
XSS can be blocked with good CORS, CSP or mode_block directives. CORS mode is now the default mode for the fetch API so the article from 2018 was certainly right at some point but looks outdated to me.
You can safely use tokens that live on client side unless you store them in your browser localStorage. So I am not in favor of such change as it can be mitigated with basic protection means.
I have to say that my quick approval is principally due to the fact that more and more developers are asking for a cookie-based approach by default. The last request I received by email is quite similar:
Hi, I’m contacting you in order to notify that the default behaviour of sending JWT using Bearer token should be replaced using secure cookie. The secure token prevents XSS Attack, I suggest to change the default to the secure one.
Yet it comes with other problems that one needs to anticipate as well. And I definitely agree with @Spomky that using the Authorization header is really not an issue on its own. That's why I didn't change this until now, also because setting up the bundle using secure cookies might be more challenging for some. I'm not fond of changing the default without good reason so I'm quite divided here.
Setting up CSP for larger sites (with content from different external sources) might be a challenge, but it is doable. It seems that multiple articles out there are either outdated or taking things out of context. I guess many would appreciate section in the LexikJWTAuthenticationBundle readme on how to properly secure either header or cookie based token, even if it is not, strictly speaking, job of the bundle :) If I understand correctly - cookies are prone to CSRF attacks, which can be mitigated by either setting custom header, such as X-Requested-With or using Samesite cookie attribute?
@Spomky could you please elaborate on "You can safely use tokens that live on client side unless you store them in your browser localStorage.". What are the other options? If we store token in application state (browser memory) then token would be lost after each refresh or tab reopen, which is bad in most real life scenarios. Thanks!
Hi @acirulis,
Indeed the token lives in the browser memory and disappears when you close it. If you want to permanently store it in a safe way, you can replicate what native applications do e.g. with the Android KeyStore or Apple Secure Enclave.
Note that this undoubtedly need a user interaction (e.g. ask PIN code). Therefore, in your use case (API and client on the same server and under the same domain), the cookie seems more appropriate.
Now that the Web Crypto API is widely available, you can leverage on the CryptoKey to encrypt and store the result e.g. in IndexedDB or localStorage.
Example in this JSFiddle project. As the encrypted data is a binary string, it should be encoded (e.g. base64) if stored in the localStorage.
Note for me: it should be possible to generate a more complex encryption key using the hmac-secretextension from Webauthn. In this case the user interaction will be limited thanks to iOS FaceID/TouchID, Windows Hello or Android.