Javascript Ajax (More)
(or indeed to any Javascript-modified page, Ajax or not)
- If the page changes without changing the URL, it can be difficult to give the user a URL to link to,
if they want to bookmark / link to
a certain configuration of the page.
-
Changing the URL without refreshing page
- Manipulating the browser history
- The problem is: Not enough to change URL.
This URL needs to mean something to the Javascript when you re-load it.
On load, Javascript must parse URL and go to that configuration.
- Examples:
-
Google Maps.
Make 5 drag moves West.
Page contents change 5 times.
URL changes 5 times.
But not actual 5 new page loads.
- GeoHive
(Irish historical maps).
Does not change URL.
Cannot link to any map view.
- XMLHttpRequest Level 1 (XHR)
- Same origin policy
- Basic security scheme is that JS
must make Ajax requests only to server it came from.
- Why is this needed?
- Consider my site:
- Login to ancientbrain.com (AB).
- Then client sends session credentials to AB with each request.
- Pages can use JS to make Ajax calls to do things, such as Edit, Save and Delete resources.
- Each request is allowed since it comes with session credentials.
- So can you set up a hacker 3rd party web page to make "delete" JS Ajax calls to AB?
- Yes! If JS on third-party sites could make Ajax calls.
- User clicks on your site,
your JS runs automatically, makes Ajax "delete" call,
this is sent from my browser to AB with my session credentials
(because I am logged in to AB and have the correct session credentials).
So the "delete" call works.
Simply clicking on the rogue site means my AB files are deleted.
- Same-origin policy stops this.
Ajax call to AB can only be made by JS on AB webpages.
- Same-origin policy is essential for logins. Without it, can't have safe logins and Ajax commands to do things.
- More on my example:
- My site has a further twist: I actually allow users upload JS.
You run pages with other users' JS in it.
You do that anyway on the Web, but here I am allowing it on ancientbrain.com, where you are logged in.
- So how do I stop that JS making server calls to Delete your files?
- Answer: Two servers.
Logged in pages run on 1st server ancientbrain.com.
User-submitted pages run on 2nd server run.ancientbrain.com
and cannot make Ajax calls to 1st server.
JSONP as workaround to same-origin policy
- JSONP
- Workaround to same-origin policy using JSON.
-
Bit of a hack.
Server must co-operate.
JSON format only.
-
More suitable for distributing feeds of content
than for making logged in read/write calls.
- JSONP is not using XMLHttpRequest at all.
It is just old-fashioned "Load some JS through <script"
- Cross-site request forgery (CSRF)
- How does this attack work?
- PHP endpoints:
- CSRF attack: Trick the victim into making the HTTP call.
- Hacker sets up a link (say in email) for victim to click.
When clicked by the victim
it sends HTTP request to PHP endpoint to Delete files.
Victim is logged in. Files are deleted.
- In fact, on ancientbrain.com it is trivially easy to get user to click your links. This happens all the time.
It is how the site is designed - you interact with uploaded programs.
- How to stop CSRF:
- What we want:
Delete request has to come from a Delete button on the AB page.
It cannot come independently.
How to do this?
- Solution: CSRF tokens.
Logged in pages are sent secret tokens (e.g. long ID numbers) in the JS.
Token is unguessable - must be different for each user, and change regularly.
These tokens are returned when JS on page makes Ajax call.
The HTTP link the hacker sets up to click does not have the token and so does not work.
- If hacker studies the site he will see the tokens, but his tokens will be different to yours,
so his tokens will not work on the link you click.
- Need two unique IDs: CSRF token and Session ID.
The latter is not enough, since that is always
sent to server when the logged-in user clicks a link.
- XMLHttpRequest Level 2 (XHR2)
- Cross-origin resource sharing (CORS)
- Standard for allowing controlled cross-origin Ajax.
- Server must co-operate.
Server returns HTTP response headers.
(e.g. PHP can output a response header before payload.)
-
Allow one site:
Access-Control-Allow-Origin: http://allowedsite.com
Allow all sites:
Access-Control-Allow-Origin: *
- With a CORS site: You can make a call from 3rd party JS.
The server declares it is safe.
Server must say so, since default is to ban it.
- CORS vs JSONP.
XHR2 with CORS probably better solution than JSONP.
Can retrieve any data format.
Better error handling.
- Need server to send headers.
e.g. At time of writing, Flickr does not.
- Rogue browser:
- Note a rogue browser could send any calls to a regular (non-CORS) server with credentials
and the server would obey them. It would not know what JS they came from.
-
But the browser won't do this.
People use browsers that are safe.
- The browser asks the server if non-site JS call is safe.
If not, it won't do it.
- This is enforced by the browser, not by the servers.
People use browsers that are safe.
CORS proxies
Even if the server does not cooperate, you can use a CORS proxy
like
corsproxy.io
to fetch arbitrary resources off the Internet:
var url = ... // some resource on a normal website
var curl = "https://corsproxy.io/?" + encodeURIComponent ( url ); // CORS-enabled URL
$.get ( curl, function ( data )
{
// we have got the data
});
- CORS and same-origin policy
are about the type of client, not about the servers.
- You can use a command-line or server-side program to wget any HTTP resource, no problem.
Your program does not have to send any credentials automatically.
In any case, you know the program. It was not delivered in the JS of a random hacker web page.
-
Conclusion: Command-line wget is safe.
No "same origin policy" needed.
- The web client however (the web browser) is a strange sort of environment.
(1) You are running JS written by untrusted people all the time, without knowing what it is doing.
(2) Any call to a site gets automatically sent with session credentials (or else login is not possible).
- Conclusion: (1) and (2) are really useful and make the Web work.
But they mean we need a
same origin policy.
- How does the CORS proxy work?
- The CORS proxy makes a regular server-side HTTP call (like wget) to get the resource.
No credentials are sent.
- The CORS proxy sets the CORS header.
- Hence your browser can get the resource from it.
Your browser sends no credentials to the CORS proxy.
Your browser can fetch the resource with no "login" possibility and so no "same origin" security needed.