0

I am by no means a security engineer , and I have barely started my journey as a web developer. I'm utilizing a Python package known as Django for my backend and react.js for my front end. Recently I have incorporated django-channels, which is a package that gives me the ability to use websockets in my project. Since I have decoupled my front and backends , the basis of authentication I'm using is via tokens (will look into using JWT).

The issue is that with JavaScript , it is not possible to send authentication headers via websocket connection (or so I'm told) , therefore a lot of people are using cookies to send this authentication token instead. Here\s an example snippet of how I am sending the token from my front end:

 const path = wsStart + 'localhost:8000'+ loc.pathname
    document.cookie = 'authorization=' + token + ';' 
    this.socketRef = new WebSocket(path)

Doing this allows me to then extract out the token information through utilizing a customized middleware on my backend:

import re
from channels.db import database_sync_to_async
from django.db import close_old_connections

@database_sync_to_async
def get_user(token_key):
    try:
        return Token.objects.get(key=token_key).user
    except Token.DoesNotExist:
        return AnonymousUser()


class TokenAuthMiddleware:
    """
    Token authorization middleware for Django Channels 2
    see:
    https://channels.readthedocs.io/en/latest/topics/authentication.html#custom-authentication
    """

    def __init__(self, inner):
        self.inner = inner

    def __call__(self, scope):
        return TokenAuthMiddlewareInstance(scope, self)


class TokenAuthMiddlewareInstance:
    def __init__(self, scope, middleware):
        self.middleware = middleware
        self.scope = dict(scope)
        self.inner = self.middleware.inner

    async def __call__(self, receive, send):
        close_old_connections()
        headers = dict(self.scope["headers"])
        print(headers[b"cookie"])
        if b"authorization" in headers[b"cookie"]:
            print('still good here')
            cookies = headers[b"cookie"].decode()
            token_key = re.search("authorization=(.*)(; )?", cookies).group(1)
            if token_key:
                self.scope["user"] = await get_user(token_key)

        inner = self.inner(self.scope)
        return await inner(receive, send) 


TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))

However this has raised some form of security red flags (or so I'm told) .

Therefore I wish to extend this questions to the security veterans out there :

  1. Is this methodology of sending token authentication information via cookie headers safe?
  2. Is my implementation of this method safe?
  3. Is there a way to secure this even further?
1

As you probably know already WebSockets does not implement any authentication or authorization methods by design, so the only possible way is to implement that on your own or use methods provided by frameworks like Django. Implementing secure authentication is hard.

From the technical point of view, you just have no other option but to send the authentication information with Cookies header. The only security requirement that comes in my mind for WebSockets you have to remember is to use secure connection, so "wss://" is a must.

Another thing is how you use the value send by cookie header on the server-side. The values included in cookie headers are considered as not safe as those are under the control of a user (sender). You should validate incoming values if it is not providing any malicious payload (SQL Injection, XSS), that could harm your backend. In your example, you are sending token value to a database so you should be aware of SQL injection (but not only) and provide correct mitigations into DB queries.

For input validation please refer to this OWASP Cheat sheet For SQL injection mitigation refer to this SQL Injection Cheat Sheet

The next thing is more general, how you use the token. You are passing the token into the database and on behalf of this, you are returning the user. It looks like you are using the token like user ID (I cannot be sure). Using the JWT as an authentication info you should consider a couple of things:

  • data stored in the token are not changeable, if some data about the user will change, you have to generate a new token
    • you have to validate the JWT token on every call according to the standard
    • I would highly recommend to use encrypted JWT tokens, which is more secure but also adds additional overhead on cryptography operations.

You have to take those points into consideration and analyze the pros and cons regards to your use case from the perspective of usability, security, and possibly performance.

In regards JWT please refer to these links: JWT Authentication Best Practices - please check also the links at the bottom of this article and articles comments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.