ryandlf
ryandlf

Reputation: 28545

Get Domain Name That a JSONP Request is Coming From For Security

I would like to allow users of my application to be able to place some code on their website and interact minimally with my server via jsonp requests. However, I only want remote requests to be able to come from remote sites that I specifically allow. Basically, the user must have an account with my application.

For security, my plan is:

  1. To store the domain the requests will be coming from before they are allowed to place the code on the site.
  2. When the server receives a remote request, it gets the domain name from the request and matches it against the database.
  3. If the domain matches, the request is verified and returned with data etc. If its different I will assume its an XSS attack of some sort and deny the request, returning an error to the remote.

How do I get the domain that the request is coming from? For testing purposes on my dev box, i've set up a separate static website running on a different port

Server domain: localhost:8090
Remote domain: localhost:8095

Using getRemoteHost seems to return 127.0.0.1. In a real world situation, would this be the ip of the remote domain and is it possible to then find the human readable version of the domain name from said ip? Storing and matching against ip is probably not a good idea since I have no idea if the remote sites that are attempting to connect to my service are being served from dynamic ip's or not...

Maybe i'm taking the wrong approach here, so please correct me if i'm wrong. Its not possible to request a username and password from the users of the remote sites because they will not have actual accounts.

Maybe a better way is to store an encrypted token of some sort in the db as opposed to the domain, and when I give the user the code snippet to place on their site, it could include a hidden input that contains an encrypted version of this token that gets passed with each request? In this case however, wouldn't anyone be able to copy the hidden element by viewing the page source and have the same access to the server?

Upvotes: 1

Views: 262

Answers (1)

willglynn
willglynn

Reputation: 11520

On the one hand, you say "I only want remote requests to be able to come from remote sites that I specifically allow", but using JSONP means you won't be seeing requests from the remote sites -- you'll be seeing requests from browsers that visited the remote site. This distinction is subtle but important, because it severely restricts your options.

If you want to know what site originated the JSONP request, you can check the Referer header. Except... you can't rely on it being set, especially across HTTP/HTTPS transitions. Then you might think "hey, I'll use CORS", but of course, anyone can create a request with any headers they want. I could write an HTTP client that claims to be one of your remote sites and access your interface with their credentials -- so if headers are your only way of restricting access, know that it's readily defeated.

The next step above Referer header is to issue tokens and to use these for identification. However, as you point out, the remote site has to give this to the browser in order for the browser to create a request. So, someone could copy it and use it wherever. Back to the same issue.

The next thing you could do is to issue tokens (for identification) along with a shared secret (for authorization). Then, you can require the remote site to use the shared secret to sign the request -- saying "yes, it's actually me, the remote site". In this way, the remote site gives the user's browser the signature, but not the secret itself. You need to make sure that the thing you're signing is enough to validate the remote site's intent (i.e. whatever action the site wants the user's browser to perform), and it should include a timestamp to prevent replay attacks. This requires some server-side calculations from the remote site, but would prevent users from taking any action except ones the remote site explicitly allows.

However... you mention that users on these remote sites won't have accounts. If this means they're available to the public, then anyone could fetch one of these remote sites, pull out the signature, and use it themselves to do the same thing that the user of the remote site could do. This is not a problem with the implementation, it's a problem with the architecture: your design calls for random web browsers to interact with your service, and there's simply no way to prevent man-in-the-middle. Using a shared secret allows you some degree of control, but you can't stop it.

At this point, I would suggest you step back and consider what problems you're actually trying to solve. What does this interface do? Do browsers on these remote sites need to be interfacing with you directly? Who are you trying to keep out, and why? What happens if they bypass your checks anyway?

Upvotes: 3

Related Questions