Stolen API key - Ideas on how to conceal/ prevent?

Hi all, I have a personal website that I use to display my FCC projects online for potential employers to look at. My weather app is on there and recently was contacted by someone pointing out an error. I fixed it and only today got an email saying my API calls reached the limit, 2500 calls today. Clearly this person has stolen my app and is using my API key heavily.
Yes I can just change my API key, but they can just get it again. Why they don’t just get there own, I don’t know?
I’ve read about creating a proxy on/where my site is hosted, but I don’t know how to do it and googling for info doesn’t bring up anything useful/understandable, or maybe to create a call to a php file which only returns the key to a specific page? But again I’d be lost on how to implement it, the most php I’ve done is the email side of things.

Ideas or links to a tutorial would be great, anyone else have this issue?

3 Likes

the update exercise description includes a glitch that hides the API key and allows you to access weather info…

https://fcc-weather-api.glitch.me/

they give an example of the JSON you can expect:

https://fcc-weather-api.glitch.me/api/current?lat=35&lon=139

if you are brave, you can build something similar in glitch for you own key… maybe a long term project?

Something I like to pretend I will do some day after I do all the microservices projects…

Google solves this security problem by allowing you to enter valid IP/host referrers where the API will only work. Other API providers should do the same so quota-theft can be avoidable.

3 Likes

I thought about protecting my api key with a proxy. It would be trivial to setup a simple proxy server on glitch. But then anyone could just use my proxy and run up my quota without needing to steal my api key. To make it really secure, and actually solve the problem, the proxy would need some sort of strategy to limit requests from the same ip. I don’t know much about security, and I imagine a competent hacker could easily defeat any security strategy that I might come up with. So in the end, I just put my key in the open without using a proxy.

1 Like

Thanks for replying everyone, yeah this is really an eye opener into the security side of things, small as it may be, but has made me think about future projects, I’ve also read about the need to secure things like where users can alter data such as - changing a username … which could cause a security concern for malicious code to be entered.

I think I’ll need to put some time into learning the solutions for this issue. Great solutions above. I can say it’s clear what approach needs to be taken, it’s just implementing it that’s still very unclear.

1 Like

Include an authentication header with your request and check it on the proxy. Here is the MDN page about authentication. Of course a bad guy could intercept your network requests so you would want to use HTTPS too but you should be doing that anyway on public sites.

If you want to get even fancier, OAUTH2 is what the major sites are using.

I wouldn’t want to require a password or login for the weather app. I just want the proxy to respond only if the request originates from my client app, not someone else’s app. The client and proxy need some kind of secret handshake to recognize each other, and it needs to be obscured so that a hacker couldn’t reproduce it even though they have access to the client source code. I wonder if that is possible?

1 Like

EDITED
I tried that, and it looks like it works. In expess, req.ip refers to the ip of the express app, not the ip of the request’s origin, so that doesn’t work to restrict access. You could use req.get('Referer') to check the request’s origin, but the headers are easily spoofed using command line tools like curl, so that is not secure either.
As far as not setting the ‘Access-Control-Allow-Origin’ headers, that will stop a compliant client (such as a modern browser) from making the request, but, again, command line tools such as curl do not respect the Access-Control headers, and make the request anyway.
I don’t think there is a reliable way for the proxy to determine the true origin and authenticity of a request without resorting to passwords or oauth logins.
Here is the app and the source that I was playing around with, and
Here is the link to collaborate and edit the code

Currently I’m on shared hosting with an apache set up and no access to ssh to install node. To be honest I’m thinking of changing hosting provider anyway (to a VPS) as I’m now on the data visualisation course and would like to be able to work with node on my own site.

Linode looks pretty good and cheap too, anybody any experience with them?

My solution right now for this problem is really just hiding the key. What I have done is create an ajax call to a php file in the same folder which checks that its my site that has done the request before returning the key. Code below. After I’m done with the api call to wunderground. I reassign that variable used to store the key to : key = ’ ';
I think that stops console.logging the variable that returns with the key. But probably its visible, through network and sources tab? Also I realise someone can just delete the reassignment to the key variable.

//============================

let key = “”;
function sendData()
{
jQuery.ajax({
type: “POST”,
url: ‘weather_echo.php’,
success:function(data) {
key = data;
}
});
}
sendData();
key = “nothing”;
data = “nothing”;

//=================================

if($URL_REF[‘host’] != ‘mywebsite.com’)
{
echo “Not my Website”;
exit;
}
if($URL_REF[‘host’] == ‘mywebsite.com’)
{
echo “This is the API Key”;
}

Nazarja,

I did roughly what you describe using an AJAX call to a PHP file:

        $.ajax({
          type: 'POST',
          url: page,
          data: options,
          contentType: 'application/x-www-form-urlencoded',
          dataType: 'html',
          success: function(result) {
            if (result.error) {
              console.log("Search results but the query failed: " + result.error);
            } else {
              results = result;
            }
          },
          error: function(data, text, other) {
            console.log('The query failed: ' + data.status, text, other);
          },
          complete: function(result) {
            cb(handleData);
          },
          xhrFields: {
            withCredentials: true
          }
        });
   }

For the PHP, you’ll have to read the API documentation and experiment to see exactly how to format it but mine looks like this:


$options = array (
  'trace' => true,
  'exceptions' => 0,
  'login' => 'username',
  'password' => 'yourKey'
  );
$client = new SoapClient('https://apiSiteURL.com/andMore', $options);


$params = $_POST;
$result = $client->APImethod($params);
echo json_encode($result->resultsObject);
?>

This uses SOAP, which I think is a little old now but you can also use curl to do it, I think.

Good luck!

Bruce

1 Like

That looks pretty good, thanks for your input and reply. I’ll give that a go.
Thanks.

They are people who have nothing to do. Yesterday, I realized that a person I knew from a course that I am doing, has injected code to the a data base I have done in a web application I use to display in job interviews. But that person, that did not care. I had to delete everything and start from scratch (and have the suspicion that he has used a keylogger in the PC I use in the course to steal the passwords of my email and social networks). All that only for prove that he have the reason. They are sick for recognition.

The solution that I realized late, is to make everything: client-side.

That’s really crap, I suppose what makes matters worse is it seems like that’s a showpiece for you and the fact that you know the guy.
Hopefully you can get it rebuild quickly.

Says a lot about people’s characters, they need to make a conscious decision to ruin someone’s work. I don’t really understand if you spent a lot of time to get to a certian and put in a lot of work, thats the type of thing you choose to do?

Good luck rebuilding!

Did you mean server-side? Because client-side is the browser, and EVERYBODY can see your code if it’s on the client-side.

The rule in web dev is do not trust anything coming from the client side. For example, you have an online form and you have some form validation happening via Javascript. When you submit that form to the server, the receiving program on the server-side should perform another validation of the form data and you shouldn’t rely on the client-side javascript form validation. CS form validation is good for instant feedback to the user that something is wrong with their input and notifies them quickly, but you need another validation on the server side before saving those form values to the database.

Otherwise, the attacker can easily do SQL injection attack to your database.

Also, need to be strict about allowable values… for example, if you’re expecting only record IDs to be passed, and your URL/form receive non-numeric input, then you know that’s abnormal and you either terminate the program or re-route back again to the homepage.

Be represented on the client side (whenever possible). In my case, they are only landing pages or mini single web apps, but no database.

Having a database was an exceptional case. I have already restarted the data base and I re-created the fields. So the page is already working again (luckily).

In the records of the database, I saw that he has entered code through HTML tags. The page is only for see the interaction of use of that page.

That is why it is a question related to low self-esteem, it’s just to show that he can do that. Since that page no has a real use which he can really benefit from.

I think that’s how they start to try things, to be able to generate greater damage later on. Sorry for the distraction in the theme of the post. It only made me remember for the bad intention of stealing the keys of the API.

This will not prevent the key from being visible in browser dev tools. A simple node/express server may solve the problem. It should accept requests from the client, add api key, query external api, return result to client,. If you prefer using php, you’ll have to use the same logic, i.e. don’t return key to client, return result of the request to external api.

Btw, if an external api provider does not have an option to bind an api key to a specific domain(s), you will be able to do it yourself in case you use a proxy-server to make api requests. This means you can use one api key for several frontends deployed in different domains quite securely.