Django Rest Framework app security preparation for production (Auth and Admin Panel)

Viewed 271

Intro

I've built a web application with multiple services:

  • frontend (react)
  • backend (API and admin panel) (Django Rest Framework + simple jwt auth)
  • Redis, DB, Nginx and etc
  • Kubernetes cluster

The app isn't small like 60k+ lines of code. It's a startup. I mentioned it to let you know that probably I wouldn't have that much attention from hackers or traffic at all. Hence I have a space to improve gradually.

The auth is done with DRF simple jwt library. Expiring Access + Refresh token.

Problem statement

I did a security audit and found imperfections from the security architecture perspective. I don't know how those issues are crucial, how should I fix them, or what issues can be fixed later. So I'm seeking solutions and advice. I would prefer an optimal proportion between the speed and quality rather than quality only (If I miss about that let me know) hence if something is "nice to have" rather than "important" I would put it to the backlog of next releases.

The actual list of issues

Let's refer by its number if you want to.

#1 Authentication methods

My current setup:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
    .....

}

As you see I have 3 methods. JWT is ok but BasicAuthentication and SessionAuthentication seem to be not ok. What I want to achieve is to have the real JWT auth and as the only way of auth for the API views (and I did really believe that I had it until I find out the opposite).

As I understood (might be wrong) I don't need SessionAuthentication and BasicAuthentication in the production setting but I do for the dev because it allows me to log in to the DRF API UI with the login form which is cool for testing. Am I right about that?

#2 Sessions

When I got to the Chrome Dev Tools and inspected the cookies I was discouraged. At this moment I deprecated the SessionAuthentication and BasicAuthentication as for the test.

enter image description here

As I understood I have the session id cookie because of the SessionMiddleware. And it's ok to have it because it is used only for the admin panel authentication and ignored by the DRF API views so the only way to auth is JWT but is it? Maybe it can have more impact and exploits. Hence, should I completely drop the SessionMiddleware, especially in order to achieve the goal to have JWT auth as the only auth type?

*I understand that it would abandon the ability to use the Admin Panel feature and I will address this point later.

#3 I store Access and Refresh tokens in the local storage

Yeah, seems to be I wrong for that. I admit it. It was the lack of experience in the beginning. The frontend app and tests (I use Cypress) heavily depend on the tokens to be in the local storage but it's feasible to migrate. On another hand, I am just afraid of the new bugs that can appear afterward. Also, I suspect expect migration can be a little painful. The question is how crucial is that and hence should I migrate the token store to the cookies now or I can do it later?

#4.1 Separation of Admin Panel from the API

Django Admin panel is awesome we all know but it is tightly coupled to the app. But. Issue #2 has brought me to the idea to separate API and Admin. So since I use Kubernetes the idea is to run the two services. One is API, I imagine it as the same codebase but the settings are different (disabled SessionMiddlware and admin panel). And another service where the admin panel feature is fully enabled. Does it make sense?

*I feel that it smells a little bit of over-engineering to me. So please stop if I am wrong about this.

** Seems to heavily depend on the #2 because if there are no issues and exploits with SessionMiddleware hence there are no strong reasons to do so.

#4.2 Admin Panel production setup

I just wonder what are the best security practices for setting up Admin Panel access in prod. I have absolutely bare setup. No captcha. No VPN. Fully vanilla. And the question is what is the most feasible but efficient access setup? I feel that it should be secured somehow. At least /admin to be not a public endpoint (VPN?) but I have no clue how to achieve this. I am on Google Cloud Platform so maybe I can use one of its solutions?

Outro

What else security checks do you do before going into production as an engineer? I mean of course, the best way is to hire the security team but I can't do that.

What I've done:

  • made sure that there are no data leaks possibilities with (tests and propper backend and frontend app architecture)
  • did an extensive acceptance and e2e testing on the auth
  • ran through the serializer and made sure that there are no exploits (at least visible) (of course a lot of unit and e2e tests)

Thanks, Artem

1 Answers

#1 Yes, remove both other auth methods and only leave the jwt auth, this is only for the REST framework, so should not be a problem for the /admin.

#2 First points will already solve this problem. But remember you can also change cookies paths from django. So you can set that the /admin in your django app uses other cookies. This will allow you to complete separate the /admin from other urls of your app. You will find this interesting:

SESSION_COOKIE_PATH = '/admin'
CSRF_COOKIE_PATH = '/admin'
LANGUAGE_COOKIE_PATH = '/admin'
SESSION_COOKIE_NAME = 'backend-sessionid'
CSRF_COOKIE_NAME = 'backend-csrftoken'

#3 As far as I know... that's the only way we can save tokens on the frontend app. So the security part there is done by having the "expiration time" of the tokens, in case someone get to obtain tokens from a client, he will only have access for a short period of time. This depends a lot on the logic of the business and how you want to manage this expirations

#4.1 I will not recommend to run your app in two instances, you should be able to setup the app correctly and avoid this kind of solutions.

#4.2 As you mentioned, /admin should have a restricted access. I have done that by whitelisting IPs (can be done on nginx), but you will have to know the IP's beforehand. You can also do it by the http auth of nginx, so you can have a user and password (that you can share without having to know IPs) asked before even connecting to the django app.

Recommendations:

  • Run an scan on your app, like: https://observatory.mozilla.org/ This will let you know lot of other security things you will need in your app. With this you should be ready to feel safe. And also, there are sometime you cannot avoid to fail some of the tests, just try to do the better to mitigate the problems.
Related