0

Tl;dr: How to secure an internal API only used for AJAX calls on the website itself.

I have a REST API (PHP 7.2) that will be consumed by Javascript (client side)

Normally I build server-side apps (then I am in control and use either a secret or a token), however, with JS being public I am lost.

I wanted basic auth, cant because a user can view source and get the username and password.

I wanted to use a private key, again inspect element and the key is visible.

I wanted to whitelist the domain (PHP Side), the domain can be spoofed.

I wanted HMAC authentication, but again, inspect element and and see the HMAC message.

How do I secure a REST API that will be consumed by AJAX

Erik Thiart
  • 381
  • 6
  • 17
  • "with JS being public I am lost" — If the JS is public then anyone can run it, and if anyone can run the JS, why are you concerned that the API is also public? – Quentin Jan 28 '19 at 15:56
  • 1
    What attack scenario are you concerned about? – Evert Jan 28 '19 at 16:36
  • Someone gaining access to the API and using for malicious purposes or someone taking that API and flooding my server with requests to populate his own site (costing me compute). The API in question is to populate stock market data via AJAX (to dynamically update the prices) currently I use PHP to loop and render the stock prices on page load. If you leak stock market data (ie someone takes your API and run it on their site, I get billed.) – Erik Thiart Jan 28 '19 at 17:25

1 Answers1

0

What you are looking for (or, in this case, NOT looking for) is CORS (Cross-Origin Resource Sharing).

Simple way to do what you want - check the $_SERVER['REMOTE_ADDR'] against your server

if ($_SERVER['REMOTE_ADDR'] != 'myserverIP') {

(just so you know, that is secure - Reliability of PHP'S $_SERVER['REMOTE_ADDR'])

Then, you can also force the 'Access-Control-Allow-Origin' to match your domain (a second-level security check - perhaps you want to keep things in a certain folder, etc.

header("Access-Control-Allow-Origin: https://example.com");

and various other specific access_control methods if you like......

Here's a function I have been using (picked it up somewhere - most likely on SO and modified it a bit for my use...):

somewhere in PHP (I use a 'codes.php' file to store and easily change the codes as needed, then include it so they are globally available)

codes.php

$spIP = 127.0.0.1; // whatever your IP is
$splink = "https://your-domain-url.tld";

then in a functions file

function checkCORS()
{
    global $spIP, $splink;
    if ($_SERVER['REMOTE_ADDR'] != $spIP) {
        return false;
    }
    header("Access-Control-Allow-Origin: $splink");
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Max-Age: 6400');
    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        // you have to handle OPTIONS requests as some other method or you will get an error message
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
            header("Access-Control-Allow-Methods: POST");
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
            header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
        exit(0);
    }
    return true;
}

I'm using this on a secure-data program and so far, every way I've tried to hack into it hasn't worked (not saying there isn't some way in, but I can't find one..)

A nice tool to test with is available at https://www.test-cors.org/

Apps-n-Add-Ons
  • 2,026
  • 1
  • 17
  • 28