How to protect web client ID in GCP source code - google-app-engine

My GAE app publishes some APIs in GCP and uses the following structure:
# Replace the following lines with client IDs obtained from the APIs
# Console or Cloud Console.
WEB_CLIENT_ID = '????????????.apps.googleusercontent.com'
ALLOWED_CLIENT_IDS = [WEB_CLIENT_ID, endpoints.API_EXPLORER_CLIENT_ID]
SCOPES = [endpoints.EMAIL_SCOPE]
#endpoints.api(name=API_NAME,
version=API_VERSION,
description='An API to manage languages',
allowed_client_ids=ALLOWED_CLIENT_IDS,
scopes=SCOPES)
My doubt is if someone picks this source code from my machine or GitHub project. He or she can access the APIs using the discovered web client id.
What’s the best practice in this case?
I acknowledge that the client can expose the ID and someone have access to it. But I believe that is another matter.

There are many ways you can do this. One way is to always check in a default value for the client ID, so that when people check out your code, they have to modify it to deploy it. You can also move the client ID to its own module and not check it in at all, and make the expectation that they create their own module with their own client ID. This avoids having a modified state for a checked in file all of the time.

The client ID itself is not sufficient information to generate a valid token. The cryptography involved will prevent such a person from accessing your API.

Related

Exposing API Endpoints in Frontend Application

I am building a frontend application using react and I am wondering whether it is risky to expose API endpoint URLs (ex: https://myapi.com/v1/getitems) on GitHub, assuming that the endpoint has several security measures that are not exposed such as CORS and JWT Token Bearer Authentication. I would assume not, since, if someone were to take the endpoint and send requests, they would need a token and be allowed to do so by CORS.
Is there any security risk in doing so?
Yes. Don't add the base url of your api on github in plain view. Even though you might have CORS and Authorization, that doesn't stop a malicious actor to keep spamming your back-end.
What you do is create a .env file in your root folder. You can use a library like #beam-australia/react-env and there are others as well.
You add in the .env file the values that are important for your environment and that are usually secrets, and you want them to not be visible in your code. You do it like so:
API_URL="https://myapi.com/v1"
And then you access this variable in your code with env("API-URL") ( in the #beam-australia/react-env case, but others libraries work the same). Keep in mind that you need to add .env in .gitignore so that the .env file is not pushed to github.
Regarding requests, you can make one like so:
fetch(`${env("API_URL}/getitems`)
This way your code will be stripped of the API's base url and if someone sees your code it will see only the endpoint, not the full url.
Publishing the code of the API is risky on its own. Somebody can find a vulnerability in it and instantly hack it. If you add the address of the API to the code you help this kind of attacks. They can get the address with some investigation; OSINT and social engineering too, but better to reduce the attack surface.
As of the secrets, they must never be near to the code, because you or another developer can accidentally publish it. It happened too many times with many developers, so better to take this seriously. If you want to keep the address in secret, then you must extract it from the code and put it in the configuration of the production environment which is imported from a totally different location than your code. Using environment variables like Alex suggested is a good idea for this. Nowadays it is common to use docker, which has a way to manage secrets, so you don't need to reinvent the wheel: https://docs.docker.com/engine/swarm/secrets/ Another aspect that the configuration belongs to the actual instance. In OOP terms you want to describe the injected properties of an object in its class or in a global variable, which is an antipattern.
As of client side REST clients like javascript applications running in the browser or Android/iOS apps, you must not publish your secrets along with the REST client, otherwise it is not a secret anymore. In that case you need a server side part for the REST client and for example sign and encrypt JWT there with a secret key. It is your decision whether this server side part of the REST client sends the HTTP request to the REST API and in that case you can hide the URI of the REST API or it just manages the JWT and the client side part of the REST client sends it. If the server side part of the REST client sends the HTTP request to the REST API, then you can even use traditional sessions with session cookies between the client side and the server side parts of the REST client, but you cannot use them between the server side part of the REST client and the REST API where the communication must be stateless. Though it does not make much sense to have a separate REST API if you don't have multiple different REST clients in this scenario e.g. browser clients for JS and JSless browsers, Android and iOS clients, fully automated clients running on servers, etc. So don't confuse the REST client - REST API relationship with the browser - HTTP server relationship, because they are not necessarily the same. Most of the REST clients run on servers, not in the browser.

How to use external api access tokens without exposing them to the user?

Sorry if this might be a bit of a trivial question, but I wanna be sure and couldn't exactly find a definitive answer online.
I am writing a small app that uses Mapbox, and I am using react-map-gl for it. They require the access token on the client side, so they suggest using an environment variable. My question is would it be okay to simply create a .env file in the front-end folder and put the variable there?
Thanks!
You can't get away from revealing API keys on the front end. If someone wants to dig around in your source code, they will find them.
However, you should always configure any API key that is visible on the Internet to be restricted to specific referrers, i.e. the domain of your website.
Usually this is done during creation of an API key through your provider's dashboard.
For Mapbox, you can read the documentation on restricting API tokens here. It states:
You can make your access tokens for web maps more secure by adding URL restrictions. When you add a URL restriction to a token, that token will only work for requests that originate from the URLs you specify. Tokens without restrictions will work for requests originating from any URL.
(emphasis my own)
They require the access token on the client side, so they suggest using an environment variable. My question is would it be okay to simply create a .env file in the front-end folder and put the variable there?
There are two reasons one uses environment variables in front-end development:
As a convenience, to keep environment-specific configuration removed from source code.
To keep sensitive information out of source code. You shouldn't commit API tokens or other similarly sensitive details to your version control.
Using environment variables in front-end code will not to keep their values secret from the end user. Whatever the value of an environment variable is at build time will be visible in the compiled output.

How to use generated clientid with Google cloud endpoints for authenticating 3rd party users without redeploying app

In my case we work with other companies which would consume our APIs along with our internal javascript client. I think we need to create a web client id for javascript client. But when exposing APIs externally, is it correct to generate new web client id per company? If so do we have to update clientid each time and redeploy application?
I'm following this documentation and in their example client ids are hardcoded, if I need to give access to new 3rd party users, then I need to generate new client id for them but I'd expect to not redeploy application.
Update: I've created a feature request as per #Alex's suggestion below.
Unfortunately the docs at https://cloud.google.com/appengine/docs/python/endpoints/auth very specifically say, and I quote,
Because the allowed_client_ids must be specified at build time, you
must rebuild and redeploy your API backend after adding or changing
any client IDs in the authorized list of allowed_client_ids or
audiences
so it appears that your perfectly-reasonable use case is very explicitly not covered at this time.
I recommend you visit said page and enter a feature request via the "Write Feedback" link (around the upper right corner of the page) as well as entering a feature request on the Endpoints component of the App Engine feature tracker, https://code.google.com/p/googleappengine/issues/list?can=2&q=component=Endpoints&colspec=ID%20Type%20Component%20Status%20Stars%20Summary%20Language%20Priority%20Owner%20Log -- we monitor both, but with different processes, so trying both is best.
Sorry to be a bearer of bad news. For now, it seems the only workaround is to distribute to the other companies one of a bunch of client ids generated in advance (you can only change the valid bunch when you re-deploy, sigh) and perhaps add some extra, app-layer authorization check of your own -- exactly the kind of work endpoints should be doing on your behalf:-(.
You can use an asterisk as the client ID, that will allow any client to call it without redeploying your API backend. Not sure if this is a documented feature or not, but it works (at least) with both Python and Java.
#Api(name = "myapi",
version = "v1",
scopes = {"https://www.googleapis.com/auth/userinfo.email"},
description = "My flashy API",
clientIds = {"*"})
public class MyAPI { ... }

SmartCloud OAuth2.0 Registering applications

I am busy writing an mobile application that connects with IBM SmartCloud. Since I want to use OAuth 2.0 I find it difficult to handle the Secret Key and the Client ID.
Since I have to Register the Application within the IBM SmartCloud console, and copy the Client ID and Secret Key to the App I am creating... Well that ain't the biggest problem, but when someone wants to use my app on another environment he doesn't have the same Secret Key and Client Id.
What is the best way to deal with this, because I want to make it usable for others, and not only for my use.
You may want to refer to the ibmsbt.openntf.org - the sources include directory includes a project for iOS. It's tested with IBM Connections On Premises
http://www.openntf.org/main.nsf/project.xsp?r=project/Social%20Business%20Toolkit%20SDK/releases/F07E34DFDDA6C06686257C6B006C6393
The project uses a callback to a custom PROTOCOL/URL : ibmsbt://myapp?code=
For IBM Connections/SmartCloud, you'll want to register an OAuth2 Key.
When you register you'll want to register your application, ibmsbt://myapp/
Then You can use these endpoints and parameters:
https://apps.na.collabserv.com/manage/oauth2/token/manage/oauth2/authorize?response_type=code&client_id=app_20085940_1384885218905&callback_uri=ibmsbt%3A%2F%2Fmyapp%2Fcallback
https://apps.na.collabserv.com/manage/oauth/authorizeToken?oauth_token=OAUTH_TOKEN

Using a subdomain to identify a client

I'm working on building a Silverlight application whereas we want to be able to have a client hit a url like:
http://{client}.domain.com/
and login, where the {client} part is their business name. so for example, google's would be:
http://google.domain.com/
What I was wondering was if anyone has been able, in silverlight, to be able to use this subdomain model to make decisions on the call to the web server so that you can switch to a specific database to run a query? Unfortunately, it's something that is quite necessary for the project, as we are trying to make it easy for their employees to get their company specific information for our software.
Wouldn't it work to put the service on a specific subdomain itself, such as wcf.example.com, and then setup a cross domain policy file on the service to allow it to access it?
As long as this would work you could just load the silverlight in the proper subdomain and then pass that subdomain to your service and let it do its thing.
Some examples of this below:
Silverlight Cross Domain Services
Silverlight Cross Domain Policy Helpers
On the server side you can check the HTTP 1.1 Host header to see how the user came to your server and do the necessary customization based on that.
I think you cannot do this with Silverlight alone, I know you cannot do this without problems with Javascript, Ajax etc. . That is because a sub domain is - for security reasons - treated otherwise than a sub-page by the browsers.
What about the following idea: Insert a rewrite rule to your web server software. So if http://google.domain.com is called, the web server itself rewrites the URL to something like http://www.domain.com/google/ (or better: http://www.domain.com/customers/google/). Would that help?
Georgi:
That would help if it would be static, but alas, it's going to all be dynamic. My hope was to have 1x deployment for the application, and to use the http://google.domain.com/ idea to switch to the correct database for the user. I recall doing this once when we built an asp.net website, using the domain context to figure out what skin to use, etc.
Ates: Can you explain more about what you are saying... sounds like you are close to what I am trying to come up with. Have you seen such a tutorial for this?
The only other way I have come up with to make this work is to have a metabase that when the user logs in, it will switch them to the appropriate database as required... was just thinking as well that telling Client x to hit:
http://ClientX.domain.com/ would have been sweeter than saying to hit http://www.domain.com/ and login. It seemed as if they were to hit their name, and to show it personalized for them right from the login screen would have been much more appealing for the client base.
#Richard B: No, I can't think of any such tutorial that I've seen before. I'll try to be more verbose.
The server-side approach in more detail:
Direct *.example.com to the same IP in your DNS settings.
The backend app that handles login checks the Host HTTP header (e.g. the "HTTP_HOST" server variable in some platforms). That would contain the exact subdomain.example.com that the client used for reaching your server. Extract the subdomain part and continue...
There can also be a client-side-only approach. I don't know much about Silverlight but I'm assuming that you should be able to interface Silverlight with JavaScript. You could read document.location with JavaScript and pass it to your Silverlight applet, whereon further data fetching etc. logic would rely on the subdomain that was passed in by JavaScript.
#Ates:
That is what we did when we wrote the ASP.Net system... we pushed a slew of *.example.com hosts against the web server, and handled using the HTTP headers. The hold-up comes when dealing with WCF pushing the info between the client and the server... it can only exist in one domain...
So, for example, when you have {client}.example.com and {sandbox}.example.com, the WCF service can't be registered to both. It also cannot be registered to just *.example.com or example.com, so that's where the catch 22 is coming in at. everything else I have the prior knowledge of handling.
I recall a method by which an application can "spoof" another domain name in certain instances. I take it in this case, I would need to do such a configuration? Much to research yet I believe.

Resources