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

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

#1

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?


#2

You really should not use JavaScript to make the calls to API which need a key, because of the very problem you are having. You do want a proxy where the call to the API such as a weather API is handled on the backend of your server. Your JavaScript would simply make a call to your proxy (basically an API) which would then make the actual call to the weather API and then send the results back to your JavaScript call. No one would see your key this way.

When I go to put my projects up for the mass public, I will be using my own proxy to avoid this problem and the constraints of using a service like Codepen for a production website.


#3

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…


#4

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.


#5

That is true. I should have known that as I do the same thing on a couple of clients’ websites.


#6

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.


#8

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.


#9

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.


#10

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?


#11

I could be wrong, but if both the client page (with the AJAX call to the proxy API) and then proxy itself are on the same server, then as long as you don’t specify the Access-Control-Allow-Origin:to allow any domains on the proxy server, then only your client page will be able to access the proxy. To prevent a server based call to your proxy, you would need to use Passport (with Node and Express) or another middleware to add a variety of authentication schemes (such as what @Jaldhar suggests) for your API.

If you only want to restrict based on the IP of the other server, then you can define an express middleware that checks each incoming request and if the IP is not the correct one, return an error.

An example of that might look like this with Node and Express:

var app = express();
app.use(function (req, res, next) {
  if (req.ip !== '1.2.3.4') { // Wrong IP address
    res.status(401);
    return res.send('Permission denied');
  }
  next(); // correct IP address, continue middleware chain
});

I may be over simplifying some of this, so I invite anyone who can provide more detailed knowledge to chime in.


#12

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


#13

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”;
}


#14

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


#15

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


#16

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.


#17

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!


#18

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.


#19

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.


#20

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.