Django remote user authentication and security - python

I am using Django remote user authentication in a project. What I am actually using is just django.contrib.auth.RemoteUserBackend without the middleware, and manually calling authenticate after having checked with the backend that the user is legitimate.
Reading the source of the middleware, it seems that it just takes the username from a header in the request and then authenticates the user against the backend passing this username. The remote user backend, in turn, just merrily logs the user in with whatever username was passed. The user has then access to every area that requires a valid login.
Isn't this just a huge security flaw? How is this meant to be used?
In my case I should be safe, since the only call to authenticate comes after a successful remote identity verification, but I am wondering the reason why the middleware was introduced.

Let me turn this around on you: if you think this is a security flaw, then try writing an exploit that sets the REMOTE_USER header in a request to your app and see what happens.
REMOTE_USER dates back to the early days of the web when CGI pages were executed locally as the user you were hitting the web page with. REMOTE_USER is actually the name of a unix environment variable that denotes the active user. As security models for web servers changed, this scheme was preserved for compatibility. Now even IIS supports it to transparently handle Active Directory logins.
All user-passed headers begin with HTTP_. Otherwise, you couldn't trust on any header information, like SERVER_NAME, which would be an enormous mess.

Django 'merrily logs the user in' because your webserver has checked that the visitor has valid credentials for that username, and set the header accordingly.
If you trust your webserver (e.g. Apache) to set the REMOTE_USER (or other) header correctly, then it's not a security flaw.

You can see the documentation here. A user can't send a request with customer header for REMOTE_USER.
Warning
Be very careful if using a RemoteUserMiddleware subclass with a custom HTTP header. You must be sure that your front-end web server always sets or strips that header based on the appropriate authentication checks, never permitting an end-user to submit a fake (or “spoofed”) header value. Since the HTTP headers X-Auth-User and X-Auth_User (for example) both normalize to the HTTP_X_AUTH_USER key in request.META, you must also check that your web server doesn’t allow a spoofed header using underscores in place of dashes.
This warning doesn’t apply to RemoteUserMiddleware in its default configuration with header = 'REMOTE_USER', since a key that doesn’t start with HTTP_ in request.META can only be set by your WSGI server, not directly from an HTTP request header.

Related

How do I access the Salesforce API when single-sign on is enabled?

I'm attempting to make SOQL queries to the Salesforce API using the Python salesforce_api and simple-salesforce modules. I had been making these requests with a client object:
client = Salesforce(username='MY_USERNAME',
password='MY_PASSWORD',
security_token='MY_SALESFORCE_SECURITY_TOKEN')
a = client.query("SELECT something FROM some_object_table WHERE some_condition")
However, my company recently restricted Salesforce sign-in through SSO only (you used to be able to login directly to Salesforce without SSO), and the funciton is throwing either:
simple_salesforce.exceptions.SalesforceAuthenticationFailed: INVALID_SSO_GATEWAY_URL: the single sign on gateway url for the org is invalid
Or:
salesforce_api.exceptions.AuthenticationMissingTokenError: Missing or invalid security-token provided.
depending on which module I use. I suspect this is because of the SSO implementation.
I've seen the docs about creating a new app through Okta, but I need to authenticate and access the API of an existing app. What is the best way to access this API with Okta IdP enabled? It there a way to have a get request to Okta return an access token for Salesforce?
Uh. It's doable but it's an art. I'll try to write it up but you should have a look at "Identity and Access Management" Salesforce certification, study guides etc. Try also asking at salesforce.stackexchange.com, might get better answers and Okta specialists.
I don't know if there's pure server-side access to Okta where you'd provide OAuth2 client, secret, username and password and it'd be silently passed to login.
If your app is a proper web application that needs human to operate - you can still make it work with SSO. You'd have to read about OAuth2 in general (you saw it on the web, all the "login with Google/Facebook/LinkedIn/Twitter/..." buttons) and then implement something like this or this. Human starts in your app, gets redirected to SF to enter username and password (you don't see password and you don't care whether he encountered normal SF login page or some SSO), on success he/she is redirected back and you receive info that'll let you obtain session id (sometimes called access token). Once you have access token you can make queries etc, it's just a matter of passing it as HTPP Authorization Bearer header (simple-salesforce docs mention session id at top of the examples).
Look, I know what I've written doesn't make much sense. Download Data Loader and try to use it. You might have to make it use custom domain on login but there is a way for it to still work, even though you have SSO enforced. Your goal would be to build similar app to how Data Loader does it. This might help a bit: https://stackoverflow.com/a/61820476/313628
If you need a true backend integration without human involved... tricky. That might be a management problem though. They should not enforce SSO on everybody. When Okta's down you're locked out of the org, no way to disable SSO. You should have a backup plan, some service account(s) that don't have SSO enforced. They might have crazy password requirements, maybe login only from office IP address, whatever. It's not a good idea to enforce SSO on everybody.
https://help.salesforce.com/articleView?id=sso_tips.htm
We recommend that you don’t enable SSO for Salesforce admins. If your
Salesforce admins are SSO users and your SSO server has an outage,
they have no way to log in to Salesforce. Make sure that Salesforce
admins can log in to Salesforce so that they can disable SSO if
problems occur.
(If you have a web app and it's embedded as Canvas in SF - there's another clean way to have the session id passed to you. Again - this works only if you have a human rather than backend integration)
If you check the profiles in SFDC and uncheck the box that requires SSO.
"is single sign-on Enabled [] Delegate username and password authentication to a corporate database instead of the salesforce.com user database. "

GAE redirect the user to oauth2 without loosing the information from the initial request?

I'm using GAE with python. I'm working on an application that opens specific files from drive.
When you try to open a file from drive with your application, you are redirected to a url like this one :
http://my-app.appspot.com/state=%7B%22ids%22:%5B%220B1AXKdjZqM9FZDNIZEhMZEh0YzA%22%5D,%22action%22:%22open%22,%22userId%22:%22102709420614967238115%22%7D
In my program, I need to check if the application is authorized by the user; and to do so, I need it to be redirected to the oauth2 util it's authorized and then come back to the previous url.. or at least I need to efficiently save the information:
state=%7B%22ids%22:%5B%220B1AXKdjZqM9FZDNIZEhMZEh0YzA%22%5D,%22action%22:%22open%22,%22userId%22:%22102709420614967238115%22%7D
How can I redirect the user without loosing the information from the initial request ?
You can use state parameter
state
Any string Provides any state that might be useful to your application
upon receipt of the response. The Google Authorization Server
roundtrips this parameter, so your application receives the same value
it sent. Possible uses include redirecting the user to the correct
resource in your site, nonces, and cross-site-request-forgery
mitigations.

API registration and authentication service

I'm working on an API registration and authentication service application using python. Developers will be able to register their application (domain name of the application) and a random API key will be generated for the registered application.
Next, the registered application will send the API key to the API service with each API request. API server will authenticate the domain of the incoming request with the passed API key to confirm that the request is valid. I'm using Forwarded Host to validated the domain name of the API request, however it doesn't work as in some cases (when the opened page is the first page), Forward Host comes blank.
Are there a better approach to authenticate the request or any changes required in the API registration process to reliably authenticate the request? Some pointers will be helpful.
Using Authorization proxy
Samples are "3scale.net", offering free tier, other commercial solutions exist too.
Open source solution I am aware of is ApiAxle, which is much simpler, but still very useful.
The proxy takes care of managing access keys and forwards request back to real application only in case, it is really to be served.
Using Authorization service
Another solution is to have some internal service evaluating set of client provided keys (providerid, appid, accesskey, ...) are authrized or not. For this purpose, you have to:
set up authorization service
modify your code by adding 2-3 lines at the top of each call calling the authentication service.
Sample code for 3scale is here: https://github.com/3scale/3scale_ws_api_for_python
Conclusions
Authentication proxy makes the application simple and not bothering about who is asking. This can be advantage until your application needs to know who is asking.
Authentication service requires changing your code.

WSGI/Django: pass username back to Apache for access log

My Django app, deployed in mod_wsgi under Apache using Django's standard WSGIHandler, authenticates users via form login on the Django side. So to Apache, the user is anonymous. This makes the Apache access log less useful.
Is there a way to pass the username back through the WSGI wrapper to Apache after handling the request, so that it appears in the Apache access log?
(Versions: Django 1.1.1, mod_wsgi 2.5, Apache 2.2.9)
You can only do it if using embedded mode and only if you use a separate package called apswigpy, which provides a Python binding for original Apache request object. The mod_wsgi package provides an optional mechanism for allowing original Apache request object to be passed as Python CObject reference in WSGI environment. You use that in conjunction with apswigpy something like:
from apache.httpd import request_rec
r = request_rec(environ['apache.request_rec'])
r.user = user
At least I think that will setup the appropriate information which access logging can then use.
You should really take this discussion over to the mod_wsgi mailing list.
You could use mod_auth_tkt. An auth_tkt is a signed cookie with the user id that Apache can understand. Your web application would have to set the cookie when the user logs in and out. Apache can derive a REMOTE_USER from the cookie, pass it to your web app or a non-Django web application running on the same server, include it in logs, whatever.
This probably isn't what you're expecting, but you could use the username in your URL scheme. That way the user will be in the path section of your apache logs.
You'd need to modify your authentication so that auth-required responses are obvious in the apache logs, otherwise when viewing the logs you may attribute unauthenticated requests to authenticated users. E.g. return a temporary redirect to the login page if the request isn't authenticated.
Correct me if I'm wrong, but what's stopping you from creating some custom middleware that sets a cookie equal to the display name of the current user logged in. This middleware will run on every view, so even though technically the user could spoof his username to display whatever he wants it to display, it'll just be reset anyway and it's not like its a security risk because the username itself is just for log purposes, not at all related to the actual user logged in. This seems like a simple enough solution, and then Apache log can access cookies so that gives you easiest access. I know some people wouldn't like the idea of a given user spoofing his own username, but i think this is the most trivial solution that gets the job done. Especially, in my case, when it's an iPhone app and the user doesn't have any direct access to a javascript console or the cookies itself.
for latest (Django 2.x, Apache 2.4) Tested
source https://www.django-rest-framework.org/api-guide/authentication/#apache-mod_wsgi-specific-configuration
you need to add WSGIPassAuthorization on in either server config, virtual host, directory or .htaccess

Subdomains and Logins

If you multiple subdomains e.g.:
sub1.domain_name.com
sub2.domain_name.com
Is there a way to have a user be able to log into both of these without issues and double login issue?
The platform is Python, Django.
Without information regarding what platform you are using, it is difficult to say. If you use cookies to store authentication information, and you are using subdomains as you describe, then you can force the cookie to be issued for the highest level domain, e.g. domain_name.com.
This will be accessable by both sub1 and sub2, and they could each use that for their authentication.
EDIT:
In the settings.py for each application running under the subdomains, you need to put
SESSION_COOKIE_DOMAIN = ".domain_name.com" as per the django docs
Yes. Just set the cookie on the domain ".domain_name.com" and the cookie will be available to sub1.domain_name.com, and sub2.domain_name.com.
As long as you maintain your session information on both domains, you should be fine.
This is a very common practice, and is why you can log into your Google Account at http://www.google.com/ and still be logged in at http://mail.google.com.

Categories