user3599803
user3599803

Reputation: 6994

django rest framework - session auth vs token auth, csrf

I have DRF set with the default settings. My ajax clients works fine with the session authentication. I want another remote server to consume the same API as the javascript clients.

My login code is simple:

class Login(APIView):
    def post(self, request, *args, **kwargs):

        user = authenticate(username=username, password=password)

        if user is None:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

        login(request, user)
        # ...

The issue is when I use a client from another host, like python requests, I get a CSRF error. According to DRF docs, I think I should use a token authentication instead.

Questions:

  1. Why do I need token authentication? The sessionid cookie is already a token, why I can't use it both for ajax clients and software clients? Thus avoid another separate db table for the tokens.

  2. Since I do want to use only session authentication, how to enforce CSRF only for ajax clients?

Upvotes: 3

Views: 4987

Answers (2)

Ken4scholars
Ken4scholars

Reputation: 6296

  1. It's not really compulsory to use Token Authentication, just that Session Authentication is liable to CSRF attacks. You can try to prevent this using CORS mecahnisms and CSRF tokens but it is still not entirely safe. To be honest, Token Authentication doesn't entirely work well with browsers either as the token can be easily retrieved using the browser's developer tools if you don't use a very complex and sophiscated mechanism for handling it. It's just simpler to use it for third-party apps.

  2. Though CSRF attacks are only applicable to browsers(Ajax clients), you shouldn't try to exlude them because the method of checking if the request is from an ajax client request.is_ajax() depends on whether the client has set the X-Requested-With header. It may be possible for an attacker to remove this header. Again I would advise that you also add CORS verification which is the method used by browsers to safeguard against CSRF attacks in addition to Django's CSRF tokens. This is typically done using Django-cors-headers package

And why token authentication isn't subject to csrf attacks? It does not seem more secure to me than the session. As I see it, both of them use HTTP headers to pass a token (in token authentication is in the Authorization header, and session is a cookie which is also a header)

Tokens are sent using the Authorization header(you could also decide to use a custom header but this is the standard for interoperability) while session auth uses cookies which are automatically sent by the browser and this is why they're susceptible to CSRF attacks. For tokens, the client has to explicitly set the header so it has to know the token, while the attacker will not even have to know what is stored in the cookies as the browser just automatically sends whatever is in its cookie store for that site.

Upvotes: 6

Kirill Bubochkin
Kirill Bubochkin

Reputation: 6343

You shouldn't enable CSRF protection for ajax clients only – it doesn't make any sense. How can you differentiate between "ajax" client and "normal" client? If it will be done e.g. by some query param, then an attacker can just use this "normal" link to do bad things.

When you're using token-based authentication, an attacker cannot just use common URL to make your request be authenticated transparently. That's why only session-based authentication requires a valid CSRF token to be included into request.

So for security reasons there are 2 options:

  • either use session-based authentication, but then you need to send auth cookie and CSRF token with every request;
  • or use token-based authentication, which is simpler since you only need to provide auth token e.g. as a query param.

Can I use token authentication that gets the token from the standard django_session table? just use that as token?

In theory you can achieve that by writing some custom authentication middleware that will use token from query param and match it with session table, but that's generally bad idea.

First, there's no such a big overhead in using one more table, but without it you're making the system harder to read and maintain.

Second, it will make the system more fragile as well. Since sessions and tokens are 2 completely different entities, they can have e.g. different lifetime. Sessions can be flushed, their TTL can be shorter/longer than token TTL. For example, default django session TTL is 2 weeks. Do you want to complicate remote server logic to get new token every 2 weeks? Or imagine the situation when token is compromised. Do you want to force ajax client to log out as well?

Upvotes: 2

Related Questions