Hide apiKey in JS and JSF - javascript

I have several fragments of code in which I have written my Google's api key or clientId such as:
<ffu:ffuAttachDocumentFromDriveBS
apikey="MY_API_KEY"
clientId="MY_CLIENT_ID"
fileId="#{documentManagerBean.fileId}"
downloadMethod="#{documentManagerBean.downloadFileAndUploadToBeeblos()}"
render="attachment_message, #{id_mpAttachment}_doc"/>
or
_doAuth: function(immediate, callback) {
gapi.auth.authorize({
client_id: MY_CLIENT_ID,
scope: 'https://www.googleapis.com/auth/drive',
immediate: immediate
}, callback);
}
so if anyone were to inspect the code in the browser, they would see them.
Is there any way to hide them?

No, the Google API Key must be placed in your HTML or JS files. So, yes, everyone can look.
But remeber that Google generate the API key for a specific domain.
So, a stolen key, can not be used.
The problem is that some developers, does not attach the API key to their domains properly, Google recommends attach to specific domain to prevent bad usages.

Google provides a Browser key and Server key. The browser key is okay to use on the browser. Your server key is the one you don't want exposed. You can use Cloud Functions or AWS Lambda to do your server side API calls.

Related

how to secure api keys in the front-end / client side Javascript?

Scenarios where i require to keep the keys as a secret
Azure / Google Maps API KEYs
STUN & TURN Server credentials for WEBRTC
First I was adding the keys directly , then I found out that those are very high vulnerabilities that others could take those credentials and use it for their needs
Later i tried using environment variables in .env file
But i could not find a way to use it properly
(express + nodeJS)
res.render("pages/crimestats", { apikey: process.env.AZURE_KEY })
here i was passing to the rendering page as a variable , but the key was still visible when i tried to see the source code on the browser.
So what is the proper way to use it ?
What i have in my mind
using an api call to get the key in the frontend
//but then any one can call that api request in the browser console right?

How to avoid data being exposed in DocuSign Clickwrap Call

I'm seeking advice for a better solution for avoid data from being exposed in javascript. The following is sample code provided by DocuSign for call Clickwrap API. Is it possible to make this javascript call from C#?
<div id="ds-terms-of-service"></div>
<script src="https://demo.docusign.net/clickapi/sdk/latest/docusign-click.js"></script>
<script>docuSignClick.Clickwrap.render({
environment: 'https://demo.docusign.net',
accountId: 'c4b87cc4-0000-433f-8c6a-a52209d4942a',
clickwrapId: 'ba495cf9-1111-4b9e-9a99-be0357d683ff',
clientUserId: '1234'
'#ds-terms-of-service'
);
</script>
UPDATE: you can use this endpoint to retrieve a URL that's unique to the specific user that is agreeing to the clickwrap. That URL won't have any information that you don't want to expose. (the URL you get has a special unique token that can only be used for this particular user and does not expose anything else).
One approach is to get this information on the server when user interact with your app so it's not exposed directly on the source.
To see how this is done, you can look at the new MyClickwrap sample app. You can get the code downloaded (C#, .NET.Core) from GitHub to see how it was built

Do I need to hide API key when using google maps js API? If so, how? [duplicate]

This question already has answers here:
How do I securely use Google API Keys
(2 answers)
Closed last month.
According to https://developers.google.com/maps/documentation/javascript/tutorial#HTML5 ,
it seems I can add the following tag to my html and start using maps js API.
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
But this will reveal my API key.
After searching on google and browsing answers on stackoverflow, I feel that maybe there is no need to hide this API key. I just need to set the referer when I create the API key on google, as explained in
https://stackoverflow.com/a/2256312/1316649
So, even others know my API key, they cannot use it from another domain. Am I right?
But google says you shouldn't embed API key in code:
https://support.google.com/cloud/answer/6310037
So, do I need to hide API key when using google maps js API? If so, how?
Update: By 'API key', I meant browser API key.
You can create multiple API keys with different restrictions to use them safely. For embedding a map, the Google Maps documentation has instructions for creating a correctly restricted API key so that it cannot be abused for other purposes at Get an API Key - Restricting API keys. It's OK to include a restricted API key in your source code, because you cannot embed a map properly without doing that anyway.
If you need server-side API access, you can create a second API key with less restrictions. That one should be kept secret.
While the above answers are helpful, none of them addresses the following vulnerability:
Once a user has access to your API key, even if they are restricted to using it only from your domain, they can still use it as much as they want. In the crudest sense, this could mean a million page refreshes (and map loads) in a very small amount of time, consequently putting you over your usage quota.
I haven't come across any solutions that address this issue. Unless I'm missing something...?
Relevant usage limits for google maps javascript api here.
The link that you posted that says you shouldn't embed API keys in code is related to Google's Cloud Platform. You are fine to leave your API key for Google Maps in your code.
To hide an API key for any service:
Design a web server that will accept requests for the third party service and proxy requests to them.
Design your interface to make requests to the web server that you designed in step 1.
Store the key on the web server you built in step 1, and apply authentication, authorization and rate limiting to the 3rd party proxied requests.
The 3rd party libraries that you use might to build and interface in step 2 might force you to use certain hosts or might force you to include an API key. For the first problem, you'll have to either edit their library code, or provide the 3rd party client library with a different http request library, like redefining the fetch() function in javascript for example. For the second problem, just add a garbage key and your proxy server can ignore it and re-write it with the real key.
Benefits:
Hide your keys.
Track who, what, and when for requests and responses. You could do this other ways though.
Could add a layer of caching to the service to speed up requests that other users have made before, provided that their terms of service allows it.
Caveats:
Because users will be making requests pretending to be your server, you are accepting a security risk. If the user intentionally makes maliciously formatted requests, they are doing so while appearing to be your server. You can however log all requests if you want to audit them later.
It takes more than a couple seconds to build and configure the proxy server with all the security you require.
You now need to handle all of these web requests which may be a lot of traffic.
You may want to intercept the responses the server returns in case it returns the key in the response body during in an error or otherwise.
You are adding another link in the chain and it will make the service slightly less reliable.
Mentions
I wanted to mention that this is essentially what "#OLTO and SUGI-cube Project" was trying to demonstrate in their answer and what #Brandon Miller was suggesting as a solution in a comment.
No need to hide API key, you just have to make it useless,
You can simply use key restrictions on the google API console.
from google API console choose:-
credentials
choose your API key or create a new one
Application restrictions
HTTP referrers (websites)
and then add your Website restrictions
I would recomend restricting your API keys to only be used by the IP addresses & referrer URLs that need them: By restricting the IP addresses, referrer URLs, you can reduce the impact of a compromised API key.
You can specify the hosts and apps that can use each key from the console by opening the Credentials page and then either creating a new API key with the settings you want, or editing the settings of an API key.
This should help you to secure your API keys.
Jeff
The two most accepted ways I have seen are to either use the [#googlemaps/js-api-loader]1 Or to - and this is important - restrict your keys the way google tells you to.
Hello there,
Even if it's too late to post an answer I believe this would help the community, so i have worked out a solution that will hide the script tag from the DOM, I managed to do it by deleting the script tag after loading the script OR if it were an error while loading it,
So here is the proposed solution and I'm happy to hear from you if it doesn't fit:
<script>
const deleteMapScriptTag = () => {
document.body.removeChild(
Array.from(document.body.getElementsByTagName('script')).find(
(item) =>
item.src.includes('https://maps.googleapis.com/maps/api/js?key=')
)
)
}
</script>
So that part will search for the google maps script tag and remove it from the document, and we will call it after the
onload, onerror
events provided by the script tag as demonstrated below :
<script onload="deleteMapScriptTag()" onerror="deleteMapScriptTag()"
src="https://maps.googleapis.com/maps/api/js?key={YOUR_API_KEY}&libraries=places"></script>
this will remove the script tag from the DOM as soon as it loads or fails loading, preventing users from inspecting the file and acquiring your key.
Although removed from the DOM, the original script tag (and therefore the key) will still be visible when viewing the page source.
Additionally, the full URL of the script can also be inspected via the Network requests panel in developer tools which also exposes the API key.
You need to hide API key
You need to hide API key when using google maps js API. It is not enough for you to set the referer.
I have local web server on my PC and can change my hosts file, So that I can spoof domain name of my HTML as your domain.
If you reveal your API key in your HTML, someone might access Google map with that key. This could mean a million page refreshes (and map loads)!
This is bad example from Google.
<script defer
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
How to hide API key from HTML
I use Environment Variables and CGI to hide my API key from HTML as follows.
1. set Environment Variables
I set Google Maps API key in Environment Variables and passing it to my CGI scripts.
nginx + fcgiwrap are running on my server, so I set API key in my fcgiwrap.conf like this.
fcgiwrap.conf
location /cgi-bin/ {
........
fastcgi_param GOOGLE_MAPS_API_KEY  YOUR_API_KEY; <= SET YOUR API KEY HERE
}
2. make CGI script
I made python CGI like this. This is same as src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY in SAMPLE of Google.
getapijs.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import requests
import os
url = 'https://maps.googleapis.com/maps/api/js'
key = os.environ['GOOGLE_MAPS_API_KEY'] # get Environment Variables
mysrc = url + "?key=" + key
response = requests.get(mysrc) # get src from Google Maps url
print("'Content-Type': 'text/javascript; charset=UTF-8'") # header for HTML
print("")
print(response.text)
3. call CGI from javascript
call your CGI from window.onload. This is same as <script defer ... &callback=initMap> in SAMPLE from Google.
main.js
function initMap() {
var uluru = {lat: -25.344, lng: 131.036};
var map = new google.maps.Map(document.getElementById('map'), {zoom: 4, center: uluru});
var marker = new google.maps.Marker({position: uluru, map: map});
}
window.onload = function() {
fetch("/cgi-bin/getapijs.py").then(res=>{
return res.text();
}).then(mytext => {
eval(mytext);
}).then(() => {
initMap();
}).catch(() =>{
// error handling
});
}
4.read main.js in your HTML
set <script src="main.js"></script> in your header section.
index.html
<!DOCTYPE html>
<html>
<head>
<style>
/* Set the size of the div element that contains the map */
#map {
height: 400px; /* The height is 400 pixels */
width: 100%; /* The width is the width of the web page */
}
</style>
<title>Hello World</title>
<script src="main.js"></script>
</head>
<body>
<h3>My Google Maps Demo</h3>
<!--The div element for the map -->
<div id="map"></div>
</body>
</html>

Google Plus API - Keyinvalid

I am trying to use the javascript sdk to do an oauth login and access the google plus api. Basically the same code here: https://developers.google.com/api-client-library/javascript/features/authentication
In my firebug console, this is the url that is sending the api request to:
https://content.googleapis.com/discovery/v1/apis/plus/v1/rest?fields=servicePath%2Cresources%2Cparameters%2Cmethods&pp=0&key={key}
This is the error that comes back:
{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}
I have:
1. Added Google Plus Api to my project
2. Created oauth credentials
3. Setup my consent screen
However, I am still getting the error.
The reason is that you have the key defined in the request. As specified in the discovery API docs (https://developers.google.com/discovery/v1/getting_started#before_starting):
"The APIs Discovery Service provides only public methods that do not
require authentication. In addition, unlike the requests you make to
many other Google APIs, the requests you make to the Discovery Service
API should not include an API key. If you do provide a key, the
requests will fail. This behavior helps ensure that you don't
accidentally disclose your API key when distributing tools that are
based on the Google APIs Discovery Service."
So you can solve the problem by removing the key from your request entirely.
If you are using Google's javascript client to do this and the error occurs when loading further APIs, you have to unset the key first:
gapi.client.setApiKey( null );
gapi.client.load( "plus", "v1", function( apiresponse ) { ... } );
If another function requires the key later, you have to set it again.
To avoid setting and unsetting the key constantly, I load all the needed APIs before authentication, then set the API key and thus will no longer have the issue.

Google OAuth WildCard Domains

I am using the google auth but keep getting an origin mismatch. The project I am working has sub domains that are generated by the user. So for example there can be:
john.example.com
henry.example.com
larry.example.com
In my app settings I have one of my origins being http://*.example.com but I get an origin mismatch. Is there a way to solve this? Btw my code looks like this:
gapi.auth.authorize({
client_id : 'xxxxx.apps.googleusercontent.com',
scope : ['https://www.googleapis.com/auth/plus.me',
state: 'http://henry.example.com',
'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'],
immediate : false
}, function(result) {
if (result != null) {
gapi.client.load('oath2', 'v2', function() {
console.log(gapi.client);
gapi.client.oauth2.userinfo.get().execute(function(resp) {
console.log(resp);
});
});
}
});
Hooray for useful yet unnecessary workarounds (thanks for complicating yourself into a corner Google)....
I was using Google Drive using the javascript api to open up the file picker, retrieve the file info/url and then download it using curl to my server. Once I finally realized that all my wildcard domains would have to be registered, I about had a stroke.
What I do now is the following (this is my use case, cater it to yours as you need to)
On the page that you are on, create an onclick event to open up a new window in a specific domain (https://googledrive.example.com/oauth/index.php?unique_token={some unique token}).
On the new popup I did all my google drive authentication, had a button to click which opened the file picker, then retrieved at least the metadata that I needed from the file. Then I stored the token (primary key), access_token, downloadurl and filename in my database (MySQL).
Back on step one's page, I created a setTimeout() loop that would run an ajax call every second with that same unique_token to check when it had been entered in the database. Once it finds it, I kill the loop and then retrieve the contents and do with them as I will (in this case I uploaded them through a separate upload script that uses curl to fetch the file).
This is obviously not the best method for handling this, but it's better than entering each and every subdomain into googles cloud console. I bet you can probably do this with googles server side oauth libraries they use, but my use case was a little complicated and I was cranky cause I was frustrated at the past 4 days I've spent on a silly little integration with google.
Wildcard origins are not supported, same for redirect URIs.
The fact that you can register a wildcard origin is a bug.
You can use the state parameter, but be very careful with that, make sure you don't create an open redirector (an endpoint that can redirect to any arbitrary URL).

Categories

Resources