I'm running python flask and have an end point say /api/secure.
Right now anyone can access it using Basic Auth over HTTPS.
In my case customers will use this to get their specific data.
The problem I have is that I don't know that the client that connects to this api is actually the customer we send the username/psw to.
So I have been googling this for awhile now and havn't found a good solution for it.
Since the client that connects will be a batchprogram (so no real user) then I guess oauth and json web tokens is not what I am after?
My idea could be that the client would supply a certificate in the request and that we on our end can compare this certificate with something we have saved when we setup the account for the customer.
I have been looking at mutual authentication like mutual-tls-with-python-flask-and-werkzeug but I'm not sure if this is the right way.
Is there a best practise how to handle this?
Related
I'm trying to secure my API routes with API keys and website URL of the client.
I'm using the tuple (api_key, website_url) to grant the access to my API. In fact, the website URL is sent in the request. Example: using Angular httpClient
this.restPost(this.endpoint,body,options)
the options include: the API key and website URL.
How can I check if the website_url inserted in the options matches the URL of the request sender? I'm using Flask microframework in the backend.
What you are asking for is impossible.
Fundamentally, if requests are coming to you across the public internet, you cannot know the identity of the application sending requests to you.
You can make an educated guess about the remote client. But since the remote client is running on a platform you can't control, nothing prevents an attacker from reverse engineering how it works and then sending you identical requests. You won't be able to tell the difference if the attacker is skilled enough.
There are tools that can help you detect and block malicious clients, but there are also tools for malicious clients to evade detection (just search Stack Overflow for many examples of the reverse problem). It's an arms race and if you want to win you will need to invest more time and money than your counterpart(s) on the other side are.
The normal solution to this problem is to make it your clients' problem. Charge them for a quota of API requests, and bill them if they make more than that. Then if they share the API key with someone else, they also need to pay the bill for them. Then you don't need to care whose API key it is: you're getting paid either way.
If you can't bill them (e.g. if it's a free service) then the next best thing is rate limiting. Don't allow more than, say, 10 requests in a second for a single API key.
If you're serious about this sort of thing, you probably don't want to reinvent the wheel. There are cloud-scale API gateway services out there. Pick one and use it to handle all your API key authentication and client throttling.
I created a Flask-Webservice with Python that runs independently inside a docker container. I then uploaded the docker image to an Azure Container Registry. From there I can create a WebService (for Containers) with some few clicks in the Azure Portal, that runs this container. So far so good. It behaves just as I want it to.
But of course I don't want anyone to access the service. So I need some kind if authentication. Luckily (or so I thought) there is a built-in authentication-mechanism (I think it is based on OAuth ... I am not that well versed in security issues). Its documentation is a bit sparse on what actually happens and also concentrates on solutions in C#.
I first created a project with Google as described here and then configured the WebApp-Authentication with the Client-Id and Secret. I of course gave Google a java script source and callback-url, too.
When I now log off my Google account and try a GET-Request to my Webservice in the Browser (the GET should just return a "hello world"-String), I am greeted with a Login Screen ... just as I expected.
When I now login to Google again, I am redirected to the callback-url in the browser with some kind of information in the parameters.
a token perhaps? It looks something like this:
https://myapp.azurewebsites.net/.auth/login/google/callback?state=redirxxx&code=xxx&authuser=xxx&session_state=xxx&prompt=xxx).
Here something goes wrong, because an error appears.
An error occurred.
Sorry, the page you are looking for is currently unavailable.
Please try again later.
If you are the system administrator of this resource then you should check the error log for details.
Faithfully yours, nginx.
As far as I now, nginx is a server software that hosts my code. I can imagine that it also should handle the authentication process. It obviously lets all requests through to my code when authentication is turned off, but blocks un-authenticated accesses otherwise and redirects to the google login. Google then checks if your account is authorized for the application and redirects you to the callback with the access token along with it. This then returns a cookie which should grant my browser access to the app. (I am just reproducing the documentation here).
So my question is: What goes wrong. Does my Browser not accept the cookie. Did I something wrong when configuring Google+ or the Authentication in the WebApp. Do I have to use a certain development stack to use the authentication. Is it not supported for any of the technologies I use (Python, Flask...).
EDIT
#miknik:
In Microsofts documentation of the authentication/authorization it says
The authentication and authorization module runs in the same sandbox
as your application code. When it's enabled, every incoming HTTP
request passes through it before being handled by your application
code.
...
The module runs separately from your application code and is
configured using app settings. No SDKs, specific languages, or changes
to your application code are required.
So while you are probably right that the information in the callback-redirect is the authorization grant/code and that after that this code should now be used to get an access token from Google, I don't quite understand how this would work in my situation.
As far as I can see it Microsofts WebApp for Container-Resource on Azure should take care of getting the token automatically and return it as part of the response to the callback-request. The documentation states 4 steps:
Sign user in: Redirects client to /.auth/login/.
Post-authentication: Provider redirects client to /.auth/login//callback.
Establish authenticated session: App Service adds authenticated cookie to response.
Serve authenticated content: Client includes authentication cookie in subsequent requests (automatically handled by browser).
It seems to me that step 2 fails and that that would be exactly what you wrote: that the authorization grant is to be used by the server to get the access token but isn't.
But I also don't have any control over that. Perhaps someone could clear things up by correcting me on some other things:
First I can't quite figure out which parts of my problem represent which role in the OAuth-scheme.
I think I am the Owner, and by adding users to the list in the Google+-Project I authorize them to use my service.
Google is obviously the authorization server
my WebService (or better yet my WebApp for Containers) is the resource server
and finally an application or postman that does the requests is the Client
In the descriptions of OAuth I read the problematic step boils down to: the resource server gets the access token from the authorization server and passes it along to the client. And Azures WebApps Resource is prompted (and enabled) to do so by being called with the callback-url. Am I right somewhere in this?
Alas, I agree that I don't quite understand the whole protocol. But I find most descriptions on the net less than helpful because they are not specific to Azure. If anyone knows a good explanation, general or Azure-specific, please make a comment.
I found a way to make it work and I try to explain what went wrong as good as I can. Please correct me if I go wrong or use the wrong words.
As I suspected the problem wasn't so much that I didn't understand OAuth (or at least how Azure manages it) but the inner workings of the Azure WebApp Service (plus some bad programming on my part). Azure runs an own Server and is not using the built-in server of flask. The actual problem was that my flask-program didn't implement a WSGI-Interface. As I could gather this is another standard for python scripts to interact with any server. So while rudimentary calls from the server (I think Azure uses nginx) were possible, more elaborate calls, like the redirect to the callback url went to dev/null.
I build a new app following this tutorial and then secured it by following the authentication/authorization-tutorial and everything worked fine. The code in the tutorial implements WSGI and is probably more conform to what Azure expects. My docker solution was too simple.
My conclusion: read up on this WSGI-standard that flask always warned me about and I didn't listen and implement it in any code that goes beyond fiddeling around in development.
There's a lot out there on how to issue JWT tokens to clients from Django, but I'm looking for a way to store a JWT token that is issued to the Django app for authentication against an external API.
The setup:
Django requests and receives token from external API. It is good for 24 hours.
Every time a client a makes a request, the app must make an authenticated call to the external API. Ideally, if 3 clients make 2 requests each, we should only need to request a single JWT.
24 hours later, a fourth client makes a request. Django sees that the token is invalid and requests a new one.
The problems here:
Requests from multiple clients should not each require their own token.
The token must be able to be changed (this rules out sticking it in the settings)
The token must be stored securely.
Ideas so far:
Stick in the database with a field listing the expiry time. This seems questionable from a security standpoint.
Implement some kind of in memory storage like this https://github.com/waveaccounting/dj-inmemorystorage . This seems like overkill.
Any suggestions as to a better way to do this?
The django cache was the way to go. See the above link for an example.
I'm using Spotipy to get some spotify data from an authorized user but I can't understand how to authorize my account.
Am I correct in thinking that a server is required that hosts something at http://www.myapp.com/callback ? What needs to be returned? Maybe I'm completely missing something here... I'm very confused by the whole required redirect URI thing...
I am trying to make a program, without website, so how should I handle authorization? What exactly should the redirect URI do?
Thanks
Edit:
Using http://localhost:8888/callback as my redirect URI now and that works. I'm not even sure why since nothing is running on that port.
Disclaimer: I know nothing about Spotify's API. But I have worked with similar APIs in the past (or even designed them). What I assume is that they use some kind of OpenID/OAuth authorization mechanism.
The very nature of these APIs is that they work through the browser! The idea is that MyApp doesn't have your actual Spotify credentials, but instead some signed token it can use.
To communicate this token to the MyApp, there are the server-callbacks, outlined in your question. Because all the browser can do is to redirect to a special URL you provide, with some info added.
So there are conceptually two ways to deal with this:
the easy, server-based one: you in fact register a myapp.com. When your app tries to authorize with spotify, it first creates a unique resource (myapp.com/authrequests/HASH-NUMBER), and communicates this as callback. Then it goes through the motions of making spotify authorize it, and once these are finished, there will have been a call to myapp.com/authrequests/HASH-NUMBER/ADDITIONAL-INFO. So while your app is waiting for this to happen, it has to poll (or open a websocket and listen to that) myapp.com. Complicated? Wait, it gets better!
the harder, OS-dependent one: you write an application that registers itself as protocol-provider with your browsers. E.g. my company does that with the protocol "ableton". Thus we can make the browser generate "ableton://AUTHORIZATION-REQUEST-RESULT" URLs which will then be communicated through Browser and OS to the running application, and thus you receive the necessary secret.
HTH
I'm a python beginner, and I want to make a basic google tasks client. It'll be a native app. The point I cant get is how to keep the 'client secret' actually secret, as it's to be included in the program code.
I've searched and found a post, quoting a google forums post, and basically suggesting to give the thing away.
I have spent hours trying to get the thing, but, I have no answer at the moment. So, I have two questions to ask:
What are the consequences of giving the client secret away?
If letting people see the secret is dangerous, is there a way to keep it secret, or, is there a way to do a classic login to support application specific passwords to log into the google account?
I assume you're talking about OAuth.
Yes, you embed the secret - but no, it's not really a secret; see another post here: OAuth - embedding client secret in your application?.
Google's docs actually say the same thing; from : https://developers.google.com/accounts/docs/OAuth2#installed
The client_id and client_secret obtained during registration are embedded in the source code of your application. In this context, the client_secret is obviously not treated as a secret.
And there's no point in trying to protect it - it has to make it's way over the wire to get to Google, and anyone with Fiddler, etc. could watch it in plain text.
As to impact: the idea behind the client secret, I believe, is to protect the client vendor (that's you). Theoretically, if I know your client key and secret, I could make a malicious website/client that lets users log in legitimately but then deletes all their tasks and it would look like you were responsible. That probably makes sense to defend against with web services, but for an installed client, the user presumably downloaded it from somewhere (app store, website, etc) that hopefully made sure it was legitimate.