31

I have a web application that pulls data from my newly created JSON API.

My static HTML pages dynamically calls the JSON API via JavaScript from the static HTML page.

How do I restrict access to my JSON API so that only I (my website) can call from it?

In case it helps, my API is something like: http://example.com/json/?var1=x&var2=y&var3=z... which generates the appropriate JSON based on the query.

I'm using PHP to generate my JSON results ... can restricting access to the JSON API be as simple as checking the $_SERVER['HTTP_REFERER'] to ensure that the API is only being called from my domain and not a remote user?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
  • Just for clarification, are you trying to make it so only your server can use the API, or only people viewing websites from your server? – TM. May 13 '09 at 04:22
  • 1
    My JSON api is at: http://example.com/json?... and I only want my website http://example.com to be able to retrieve results from my JSON api. –  May 13 '09 at 04:45
  • Just out of curiosity, why are you trying to do this? It kinda goes against the grain of the web so would be good to know the reasoning behind the requirement. – Sam May 13 '09 at 04:50
  • @Sam - Instead of having a PHP/Ruby/JSP/etc page generate the results on my web site .... which is slow. I simply created an API to allows me to query the database for my needed information. So now, what I do is simply have an HTML page which loads super fast ... and then have the content dymanically inserted into the page via the JSON api. What I don't want to have happen is that now someone can essentially dump all data from my database b/c I have now created this API –  May 13 '09 at 04:54
  • @JamesZ - as long as you are not exposing data via your API that could be classified as sensitive or confidential, then I wouldn't worry about this. If you are, however, then you need to look at SSL and validating the client somehow before allowing API access, using username/password or client certificates. – Sam May 13 '09 at 07:55
  • 3
    So, instead of the browser making a single request to a server and the page being built on a high end server, you are going to make multiple server requests, and use interpreted javascript to cobble the page together using the puny client. I hope that turns out to be faster for you. P.S. This is only partially tongue-in-cheek – Darrel Miller May 13 '09 at 12:14
  • 5
    @Darrel: It really depends on the scale and functionality. At scales some of us have to deal with the static HTML is served by a CDN, the JSON is cached as appropriate, when stale, served by stateless, non-sticky clusters, and so on. At these scales the performance of one puny netbook or smartphone per client dwarfs the 0.1-0.01% of a server core available for each parallel session. – Szocske Jul 12 '11 at 17:42

8 Answers8

19

I think you might be misunderstanding the part where the JSON request is initiated from the user's browser rather than from your own server. The static HTML page is delivered to the user's browser, then it turns around and executes the Javascript code on the page. This code opens a new connection back to your server to obtain the JSON data. From your PHP script's point of view, the JSON request comes from somewhere in the outside world.

Given the above mechanism, there isn't much you can do to prevent anybody from calling the JSON API outside the context of your HTML page.

Greg Hewgill
  • 828,234
  • 170
  • 1,097
  • 1,237
  • If that's the case, how are people projecting their "RESTful" api's? Or are these API all public? –  May 13 '09 at 04:49
  • 1
    Generally, they are public APIs. Depending on the website, they may or may not be documented for public consumption, but they should all be constructed with an eye toward robustness in the face of malicious (or just undesireable) users. – Greg Hewgill May 13 '09 at 04:51
  • So how do people prevent users from submitting a REST query that simply dumps all data from the web applications database. –  May 13 '09 at 04:55
  • 6
    The API would be designed in such a way that there is no ability for the calling user to do things that you don't want done. For example, a poor JSON API would accept a SQL "where" clause and pass it straight through to the database. Even if it were sanitised and safe from injection attacks, you may not want your users being able to query for any range of records. A better API might accept a record ID or keywords or something and return a maximum of 10 records. The key is, you can't trust anything coming from the outside world. – Greg Hewgill May 13 '09 at 05:10
18

The usual method for restricting access to your domain is prepend the content with something that runs infinitely.

For example:

while(1);{"json": "here"} // google uses this method
for (;;);{"json": "here"} // facebook uses this method

So when you fetch this via XMLHttpRequest or any other method that is restricted solely to your domain, you know that you need to parse out the infinite loop. But if it is fetched via script node:

<script src="http://some.server/secret_api?..."></script>

It will fail because the script will never get beyond the first statement.

fearphage
  • 16,176
  • 1
  • 24
  • 32
  • 1
    Say what? Your recommendation is to create an infinite loop? Either I'm completely missing something here or you're pulling my leg. –  May 13 '09 at 04:38
  • 17
    Except anyone can still call it from server-side code without any hassle and just strip out the loops. – Matthew Flaschen May 13 '09 at 04:41
  • 4
    Yes, JameZ, that is correct. An infinite loop prepended to the JSON will prevent your JSON from being loaded into script nodes. Yes, it will not prevent access from the server-to-server. This is not a fix-all solution. It is just another layer of security. It will prevent your data from being loaded client-side using the script node. – fearphage May 13 '09 at 05:29
  • I can load it with an xhr, remove the loop part and then add it to a newly created script block, can't I? – Amarghosh Nov 26 '09 at 16:02
  • 2
    Amarghosh: Your explanation works from the same domain. However, xhr is domain restricted. To answer your question: No, you cannot. – fearphage Jan 20 '10 at 18:00
  • 2
    some also return unparsable code before the json, for instance, angularjs expects a `)]}',\n` at the beginning of json responses. – Mortimer Nov 08 '12 at 17:20
  • I know I am bit late but I am just wondering that if we are using infinite loop then how we are going run everything else and make are code execution to be stuck at one place. I mean that json will not execute then why its already there? please help me understand this – Kramer Apr 13 '20 at 11:42
5

In my opinion, you can't restrict the access, only make it harder. It's a bit like access-restriction by obscurity. Referrers can be easily forged, and even with the short-lived key a script can get the responses by constantly refreshing the key.

So, what can we do?

Identify the weakness here:

http://www.example.com/json/getUserInfo.php?id=443

The attacker now can easily request all user info from 1 to 1.000.000 in a loop. The weak point of auto_increment IDs is their linearity and that they're easy to guess.

Solution: use non-numeric unique identifiers for your data.

http://www.example.com/json/getUserInfo.php?userid=XijjP4ow

You can't loop over those. True, you can still parse the HTML pages for keys for all kinds of keys, but this type of attack is different (and more easily avoidable) problem.

Downside: of course you can't use this method to restrict queries that aren't key-dependent, e.g. search.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
stefs
  • 17,549
  • 6
  • 37
  • 45
4

Any solution here is going to be imperfect if your static pages that use the API need to be on the public Internet. Since you need to be able to have the client's browser send the request and have it be honored, it's possibly for just about anyone to see exactly how you are forming that URL.

You can have the app behind your API check the http referrer, but that is easy to fake if somebody wants to.

If it's not a requirement for the pages to be static, you could try something where you have a short-lived "key" generated by the API and included in the HTML response of the first page which gets passed along as a parameter back to the API. This would add overhead to your API though as you would have to have the server on that end maintain a list of "keys" that are valid, how long they are valid for, etc.

So, you can take some steps which won't cost a lot but aren't hard to get around if someone really wants to, or you can spend more time to make it a tiny bit harder, but there is no perfect way to do this if your API has to be publically-accessible.

matt b
  • 132,562
  • 64
  • 267
  • 334
  • This is probably the most elegant work-around, but I agree it's only a work-around and not a real solution (there is none). – Matthew Flaschen May 13 '09 at 04:41
  • So what you're saying is that ... if I checked the 'HTTP_REFERER' - that at least provides ***some*** limited assurance that only my website is using my JSON api. But then again, the 'HTTP_REFERER' can easily be faked. –  May 13 '09 at 04:47
4

The short answer is: anyone who can access the pages of your website will also be able to access your API.

You can attempt to make using your API more difficult by encrypting it in various ways, but since you'll have to include JavaScript code for decrypting the output of your API, you're just going to be setting yourself up for an arms race with anyone who decides they want to use your API through other means. Even if you use short-lived keys, a determined "attacker" could always just scrape your HTML (along with the current key) just before using the API.

If all you want to do is prevent other websites from using your API on their web pages then you could use Referrer headers but keep in mind that not all browsers send Referrers (and some proxies strip them too!). This means you'd want to allow all requests missing a referrer, and this would only give you partial protection. Also, Referrers can be easily forged, so if some other website really wants to use your API they can always just spoof a browser and access your API from their servers.

Laurence Gonsalves
  • 125,464
  • 31
  • 220
  • 273
  • 1
    I don't want encrypt it, I simply want to restrict "who" can call my JSON api. The only person I want to be able to call the API is my own web server and no remote users. –  May 13 '09 at 04:40
  • Yes, that's what the last paragraph of my answer is about. You could try using the referrer, but it can be easily spoofed. – Laurence Gonsalves May 13 '09 at 05:08
0

Sorry, maybe I'm wrong but... can it be made using HTTPS?

You can (?) have your API accessible via https://example.com/json/?var1=x&var2=y, thus only authenticated consumer can get your data...

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
NilColor
  • 3,342
  • 2
  • 27
  • 44
  • 5
    SSL doesn't guarantee identity, it just prevents 3rd parties from seeing your data while it is in transit between browser and server. – RQDQ Jul 01 '10 at 12:48
0

Are you, or can you use a cookie based authentication? My experience is based on ASP.NET forms authentication, but the same approach should be viable with PHP with a little code.

The basic idea is, when the user authenticates through the web app, a cookie that has an encrypted value is returned to the client browser. The json api would then use that cookie to validate the identity of the caller.

This approach obviously requires the use of cookies, so that may or may not be a problem for you.

RQDQ
  • 14,445
  • 1
  • 27
  • 53
0

Sorry, there's no DRM on the web :-)

You can not treat HTML as a trusted client. It's a plain text script interpreted on other people's computers as they see fit. Whatever you allow your "own" JavaScript code do you allow anyone. You can't even define how long it's "yours" with Greasemonkey and Firebug in the wild.

You must duplicate all access control and business logic restrictions in the server as if none of it were present in your JavaScript client.

Include the service in your SSO, restrict the URLs each user has access to, design the service keeping wget as the client in mind, not your well behaved JavaScript code.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Szocske
  • 6,750
  • 2
  • 18
  • 24
  • Oh, and when people start using your service in ways your HTML does not allow? Take the free business development advice and roll with it! – Szocske Jul 12 '11 at 18:05