Django Admin - Re-authentication? - django

I'm in a bit of a dilemma at the moment regarding Django's admin backend. The default authentication system allows already logged-in users that have staff privileges to access the admin site, however it just lets them straight in.
This doesn't feel “right” to me, and I'm wondering if it would be difficult to at least require a re-authentication of that same session in order to get into the backend.
Preferably though, it'd be good if the frontend sessions could be separated from the backend ones (though still using the same user objects), this would allow a clean separation of both parts of the site. Would this perhaps require two separate authentication backends? Would something like this be difficult to achieve?

Here's an idea: run the admin app on a different domain to the frontend. The cookies won't be valid in the other domain, so the user will have to log in again. All you'd need would be a separate Apache vhost and a basic settings.py that just has contrib.admin in INSTALLED_APPS.

You could probably implement a middleware that asks for authentication when accessing the admin site from a referer not in the admin site. It could log the person out and make them log back in, but even that wouldn't be necessary. Just require another password entry, and redirect them if it fails. It might involve setting a session variable, is_admin_authenticated or something.

Related

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

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.
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 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.

Users for multiple sites in Django

I am trying to get multiple sites to use the same database and code but in a way which forces each user to have their own login to each site.
I have seen a few suggestions as to how to make this work but I'm not sure which way to go.
I am using the Sites Framework, using the subdomain to identify the current site so I'm not using SITE_ID at all.
Use the sites framework - This answer (https://stackoverflow.com/a/1405902/1180442) suggests using the sites framework to do it, but I'm having trouble with the get_user() method, as it doesn't have access to the request and that's where my site info is stored.
Use separate databases for users - I'm really not sure about this one but I think it might cause bigger problems down the line.
Change to using SITE_ID - I want to try and avoid this if possible as it will mean having to run many different instances of my app, one for each site, which uses it's own settings.py. This will quickly turn into a nightmare, I think.
Permissions - I'm wondering if this should be something that I get the permissions framework to use? So one set of users for all sites but each user can have permissions to see each site, as long as they've registered with that site?
Can anyone help with this?
I quite like the idea of number 1 but I just need to get the request in the get_user() method so I can do this
def get_user(self, user_id):
try:
# I can't do this because there is no request available here
return User.objects.get(pk=user_id, site=request.site)
except User.DoesNotExist:
return None
to prevent people logged in to one site being able to log into another using the same session.
How I actually do it, not for users but for common databases, Is to design a main, hidden app with a REST API architecture. My other apps, naturally have their own DB and exchange their data via batch or stream process depending on the need. I use django-rest-framework.
For your case what I would do is that whenever a user makes a Log In request I would send it via HTTPS to my main database and get it authenticated in my main app. Whenever I would need to validate the user status I would simply make a get request to the main app.
This architecture is not that different from the one that many mobile apps have.
I hope it helps.

how can i check that a admin user is logged into django website or not into NGINX

I want to see that admin user is logged in or not into nginx.conf file .
I need this for my specific requirement.
My Try :
I tried to fetch COOKIES into nginx.conf file.
but when admin is logged out so cookies are changed so i am not able figure out that if a admin is logged in or not.
As far as I know this is not possible.
Django uses encrypted cookies by default and stores all data in the session table in the DB (also encrypted). You could check if the user has a cookie set in nginx but you won't be able to verify if that cookie value actually means "Admin" or "random other cookie value".
Also unless you're using custom nginx modules to check the contents of the cookie you run the risk of someone managing to trick nginx into thinking the user is an admin when that is not the case.
I'm not sure what the use case is here but you could try doing something with the django middleware or looking for third party plugins instead of using nginx.
If you want to limit file access to a specific file, e.g. admin-only images/javascript/data files or such you could try the HttpAccessKeyModule for Nginx and just generate a custom 'url' for your admin to access them.
You can also try looking into: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html and see about delegating the check to some part of Django which just returns Yes or No to nginx.
There might be another nginx plugin somewhere which you can add/enable for an admin user from within django. But this requires you to think the other way around. With django telling nginx the user is an admin. Instead of nginx finding it out itself.
Hope this helps you in some way.

Django + Wordpress: Integrating user login

I would like to have one users system (preferrably Django's) to rule both Django and Wordpress.
The use case is that Django is an application embedded inside a wordpress installation (via iframe or something similar). In order to use the Django, users must be authenticated, authentication in WordPress is not mandatory, but recommended (for posting comments and stuff like that).
In order to ease the usage of the site, I would like the same sign-up to apply for both the Django app and the WordPress installation. Sign-up might occur either via OAuth / FB authentication (lots of Django solutions for this), or via dedicated site users. While the signup process is most important, it would be nice if certain user fields would remain synced between the two worlds.
My thoughts on the matter:
Maybe there's an out-of-the-box solution (couldn't find any)
Create a full-fledged django app for syncing the two user models (wp_users and django's users) via one of the following options:
A master(django)-slave(wp) solution, where each change in Django changes info in the wp_users and other related tables and vice-versa (via Django periodically checking the DB or creating a WP plugin). Can be implemented either by putting both django and wp on the same (MySQL) db, using XML-RPC or some other anomination I didn't think of.
Same as above, but let WP be the master instead of Django (seems harder).
An external system to rule both models - maybe make OAuth / some other external authentication mandatory, and somehow sync the two models using this?
Has anyone encountered this situation before?
Any suggestions will be appreciated.
You should really try to work out some approach, revising your work when encountering problems afterwards.
Nevertheless imo Wordpress is kind of bordered. I wouldn't recommend making changes to both frameworks, but fixate on Django.
For example you could create a WordpressUser(User) and create a python script a crobjob, celery task or whatever you'll need to syncronize your databases. Meaning to say you should keep things strictly separated unless you have very good reasons not to (I can't think of any).

How to authenticate against Django from Drupal?

I have a medium sized Drupal 6 site running (around 5 million page views per month and more than 30K registered users) and I need to integrate OSQA, a Django application, with it. I already have many users, roles and permissions in my Drupal database and I'd like to point the Django app to use the sign up and login pages I already have in Drupal to give my users a single point on entrance.
I want to keep the Django authentication part because I think OSQA would work better. I also have performance reasons in mind, the Drupal site already gets a lot of traffic and has a very busy database and I think that using a separate database for Django would help.
After some research I think I could make the Drupal sign up and login pages call Django in the background to sign up or login to the Django app. I plan to do this writing a couple of views in Django, one for sign up and another for login, and Drupal would post the username and password to those views. Of course I'd need to disable CSRF in Django for those views and probably also post some secret key that only my Drupal and Django applications know about to avoid external sites trying to use this "unprotected" Django views.
I know that my Django application may need some user data from Drupal at some points and I'm planning on using the Drupal services module for that.
Would this be a good approach? Any suggestions?
Thanks a lot!
Are there any plugins for OSQA to expose an authentication service that Drupal can talk to? (OpenID or similar).
Alternatively, check out Drupal's ldap_integration module for an example of a module that uses an external authentication service. Consider that you will need to create Drupal user accounts for each login.
Finally, why not just build the essential parts of OSQA's functionality with Drupal? Seems like the key functionality could be replicated quite easily using Taxonomy, Vote Up and Userpoints/User Badges... potentially easier to do than shared authentication, especially on a large site.
I once created a very simple [sql_authentication][1] module, which you can probably simply re-create for a more recent version of Drupal.
The idea is simple: provide Drupal with an alternative authentication callback.
In that callback-function, just check against the Django database, and return TRUE if you think the user is correct.
You could look at how openid.module (in core) extends the user-authentication for a simple example.
If you can post to the Django form, you may be able to use drupal_http_request to handle the call to Django. After using the ldap_integration module for a while, I worked on a custom authentication module that calls a Java-based REST authentication API using drupal_http_request. If you're interested in the code, let me know.