As seen in GitHub's blog, they've implemented HTML5's JavaScript pushState feature for tree browsing (for modern browsers), bringing AJAX navigation without Hash Bangs.
The code is simple:
$('#slider a').click(function() {
history.pushState({ path: this.path }, '', this.href)
$.get(this.href, function(data) {
$('#slider').slideTo(data)
})
return false
})
This quite elegantly allows them to:
Request the just the new content through AJAX instead of a full page
Animate the transition
And change the browsers URL (not just the #, as Twitter does — twitter.com/stackexchange → twitter.com/#!/stackexchange )
My question is, how does JavaScript prevent against the use of pushState by one website to imitate another, resulting in a convincing phishing attack?
At the very least it seems that the domain can't be changed, but what about multiple paths within a site, potentially by multiple unrelated and untrusting content providers? Could one path (I.E. /joe) essentially imitate another (pushState /jane) and provide imitative content, with possibly malicious purposes?
My understanding is that this is perfectly consistent with the Same Origin Policy that governs XMLHttpRequest, setting cookies, and various other browser functions. The assumption is that if it's on the same domain + protocol + port, it's a trusted resource. Usually, as a web developer, that's what you want (and need) in order for your AJAX scripts to work and your cookies to be readable throughout your site. If you are running a site where users can post content, it's your job, not the browser's, to make sure they can't phish or keylog each other's visitors.
Here's some more detail on what the FireFox folks are thinking about pushState - it doesn't seem like this is an issue for them. There's another discussion of a possible pushState security hole here, but it's a different concern, about being able to hide a malicious querystring on the end of someone else's URL.
As nrabinowitz has stated and in more layman's terms: it's limited to the same domain, just like ajax calls and cookies. So it's completely safe—though a little sneaky to the end user.
Us (developers) have been doing this forever with hash tags forever but it's better because:
It looks cleaner.
On revisit of a deep link you can actually surface real html data to support things like SEO and Facebook Open Graph (both send spiders to scape the html of your page).
Servers don't have access to hash tags data so you don't see it in your server logs so it helps some with analytics.
It helps fix hash tag issues. For example I had an Nginx rewrite to redirect users visiting my app to the same https url. It works in all browsers but Safari which will redirect you to just the domain without the hash (so annoying!)
Related
I am building a site to help students schedule their university courses. It will include things like days, times, professor, etc. I want to fetch the "rating" of professors off www.ratemyprofessors.com and have it show on my site. For example, at https://www.ratemyprofessors.com/ShowRatings.jsp?tid=1230754 you can see Michael has a rating of 4.6. I want to request that data and have it show on the site. I can't scrape it beforehand as their ratings change and I want it to show their current rating. Am I able to do this with an XmlHttpRequest? How would I do that? I'm hoping to do it in JavaScript.
Browser won't let http requests towards third party websites leave your webpage unless the target site allows it. This is called CORS. See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS. While you may be lucky if that site allows (or doesn't disallow), that may change in the future, leaving you in a bind (malfuntioning feature).
Also, what you're planning to do is called web scraping, and typically it isn't favored by webmasters, so you might eventually get blocked or stumble upon a change in content markup, again leaving you in the same bind.
I would ask the owner of that site for permission and, perhaps, API access.
Otherwise, your option #1 is to try making that http-request from the browser-level script (yes, you can use ajax, XmlHttpRequest, the new fetch API, or a third-party script), which will work only if CORS isn't a problem.
Your option #2 is to make the same request from the server (so, ajax to your server app, which scrapes the remote site), and this would be the workaround for the potential CORS problem. Again, CORS is an obstacle only at the browser level, cause browsers are coded to intercept that to minimize potential harm to user's data. However, this option is subject to eventually having your server blocked from accessing the remote site, which would be done by that site's owner and by simply configuring it to not accept connections from IP addresses that they detect as belonging to your site. Pretty cool, huh?
Both of these options are further subject to the problem of dealing with content changes, which would be in hands of your post-request script, whether executing at the browser (option 1) or at the server (option 2), which could be an ongoing maintenance. Either way, craft it in such a way to treat that 3rd-party data as a nice-to-have (so, don't crash your page when fetching that other data fails).
Edit: I would have to try this to be certain, but it's something to think about: you could embed a hidden iframe in your page, targetting that remote webpage (as in your example), then parse the iframe's content once it's available. Note that this endeavor (did I spell that right) is not trivial AT ALL, and it would cost quite a chunk of development time (and it wouldn't be a task a beginner could reasonably complete, at least not quickly), and - again - I am not 100% certain that it would even be possible, as the iframe-hosting webpage may not have access to iframe's content when it'a served by a 3rd-party website. So, this would potentially be option #3, and it would be at-browser solution (so, lots of javascript), however not susceptible to CORS blocking. Phew, a lot of words, I know - but they do make sense, if you can believe me.
Hope that helps decide. Good luck.
I have some HTML/PHP pages that include javascript calls.
Those calls points on JS/PHP methods included into a library (PIWIK) stored onto a distant server.
They are triggered using an http://www.domainname.com/ prefix to point the correct files.
I cannot modify the source code of the library.
When my own HTML/PHP pages are locally previewed within a browser, I mean using a c:\xxxx kind path, not a localhost://xxxx one, the distant script are called and do their process.
I don't want this to happen, only allowing those scripts to execute if they are called from a www.domainname.com page.
Can you help me to secure this ?
One can for sure directly bypass this security modifying the web pages on-the-fly with some browser add-on while browsing the real web site, but it's a little bit harder to achieve.
I've opened an issue onto the PIWIK issue tracker, but I would like to secure and protect my web site and the according statistics as soon as possible from this issue, waiting for a further Piwik update.
EDIT
The process I'd like to put in place would be :
Someone opens a page from anywhere than www.domainname.com
> this page calls a JS method on a distant server (or not, may be copied locally),
> this script calls a php script on the distant server
> the PHP script says "hey, from where damn do yo call me, go to hell !". Or the PHP script just do not execute....
I've tried to play with .htaccess for that, but as any JS script must be on a client, it blocks also the legitimate calls from www.domainname.com
Untested, but I think you can use php_sapi_name() or the PHP_SAPI constant to detect the interface PHP is using, and do logic accordingly.
Not wanting to sound cheeky, but your situation sounds rather scary and I would advise searching for some PHP configuration best practices regarding security ;)
Edit after the question has been amended twice:
Now the problem is more clear. But you will struggle to secure this if the JavaScript and PHP are not on the same server.
If they are not on the same server, you will be reliant on HTTP headers (like the Referer or Origin header) which are fakeable.
But PIWIK already tracks the referer ("Piwik uses first-party cookies to keep track some information (number of visits, original referrer, and unique visitor ID)" so you can discount hits from invalid referrers.
If that is not enough, the standard way of being sure that the request to a web service comes from a verified source is to use a standard Cross-Site Request Forgery prevention technique -- a CSRF "token", sometimes also called "crumb" or "nonce", and as this is analytics software I would be surprised if PIWIK does not do this already, if it is possible with their architecture. I would ask them.
Most web frameworks these days have CSRF token generators & API's you should be able to make use of, it's not hard to make your own, but if you cannot amend the JS you will have problems passing the token around. Again PIWIK JS API may have methods for passing session ID's & similar data around.
Original answer
This can be accomplished with a Content Security Policy to restrict the domains that scripts can be called from:
CSP defines the Content-Security-Policy HTTP header that allows you to create a whitelist of sources of trusted content, and instructs the browser to only execute or render resources from those sources.
Therefore, you can set the script policy to self to only allow scripts from your current domain (the filing system) to be executed. Any remote ones will not be allowed.
Normally this would only be available from a source where you get set HTTP headers, but as you are running from the local filing system this is not possible. However, you may be able to get around this with the http-equiv <meta> tag:
Authors who are unable to support signaling via HTTP headers can use tags with http-equiv="X-Content-Security-Policy" to define their policies. HTTP header-based policy will take precedence over tag-based policy if both are present.
Answer after question edit
Look into the Referer or Origin HTTP headers. Referer is available for most requests, however it is not sent from HTTPS resources in the browser and if the user has a proxy or privacy plugin installed it may block this header.
Origin is available for XHR requests only made cross domain, or even same domain for some browsers.
You will be able to check that these headers contain your domain where you will want the scripts to be called from. See here for how to do this with htaccess.
At the end of the day this doesn't make it secure, but as in your own words will make it a little bit harder to achieve.
I'm using a server-side node.js function to get the text of a URL passed by the browser, to auto-index that url in a bookmarking service. I use jsdom for server-side rendering. BUT I get blocked from popular sites, despite the requests originating from legitimate users.
Is there a way to implement the URL text extraction on the browser side, such that requests would always seem to be coming from a normal distribution of users? How do I get around the cross-site security limitations in the browser? I only need the final DOM-rendered text.
Is a bookmarklet the best solution? When the user wants to bookmark the page, I just append a form in a bookmarklet and submit the DOM-rendered text in my bookmarklet?
I know SO hates debates, but any guidance on good methods would be much appreciated.
You could certainly do it client-side but I think that would be overly complex. The client would have to send the html to your service & that would require very careful sanitising & might be difficult to control the volume of incoming data.
I would probably simply track the request domains and ensure that I limited the frequency of calls to any single domain. That should be fairly straight forward if using something like Node.JS where you could easily set up any number of background fetch tasks. This would also allow you to fine tune the bandwidth used.
We have a very simple api and want to return to render some content on the another site. I want to do send something like from our api endpoint(for example http://domain.com/api/endpoint/1 which is just included like <script src='http://domain/api/endpoint/1'></script>):
document.write('here is my value<br />');
document.write('Let me give you some inforamtion about this<br />');
and just escape it. There's no concern about https or the content. Two people have vaguely told me that they think this is unsafe but I don't really see how it is if we handle it correctly on our side (which would be true anyway)? Is this safe? Am I missing anything? JSONP is overkill for something like this - we want the most simple technique possible.
thx in advance
There's no security issue with the fact that you're including a script from one domain onto a web page on another, as long as you control both domains. There are plenty of sites that serve their script tags from CDN or from content subdomains or whole other domains.
The use of document.write is obsolete and probably will cause you headaches.
The only security concerns you would have is if your API allows user content to be document.write'ed onto the page, as then you become vulnerable to cross site scripting attacks, where someone sets their username as something like this:
/><script>document.write('<script src="myevilpage.com"></script>'</script>
and then your code happily injects that onto the page and everyone who then visits it gets a virus and their computer explodes.
Generally you will want to escape any user based input before sending it on your API, also sanitize it in your javascript and then insert it into the page as a textNode or similar trick to stop people being able to manipulate your page.
I'm working on a user login system and I have come up with a solution that I wanted to run past you fine folks to make sure I wasn't about to create a giant security flaw.
Here is what we have.
You start on an HTTP page that when you click a link will open a modal window. The first link from an HTTP page when clicked will repopulate the modal with an iFrame that links to an HTTPS page. Since I can't have the HTTPS talk to the HTTP page I'm using a document.location setting on the HTTPS iframe page to make the success page HTTP. Then the HTTP page talks back to the parent window.
So:
HTTP (click) -> Opens iFrame in HTTPS -> Login over HTTPS secure on Success document.location -> HTTP success page -> window.parent.success_msg(deferred); calls to the parent window.
It's working great in all browsers so far...haven't tested IE yet, but I wanted to verify this wasn't a really terrible practice before I present it.
Thanks!
An iframe to an HTTPS URL within an HTTP page is really bad practice because it makes it hard for the user to get more detailed information (in particular security-related information) about that page. (Sure, you can probably right click and find a way to check the properties of the iframe, but even users who'd know how to do that probably wouldn't do it).
With an HTTPS iframe like this, you prevent the browser from displaying the usual security symbols: lock, green/blue bar and, more importantly, the address of the site (an attacker could just put their own link to their www.some-other-site.example instead of the indented site; www.some-other-site.example could have a legitimate certificate and the browser wouldn't give any alert message).
This practice is especially bad as an HTTPS iframe within a page served over HTTP, but it's not good either when the containing page is served over HTTPS. You can't easily verify the identity of the server serving the framed page either. (Sadly, this is (or at least was) what's recommended by 3-D Secure...)
If you want to do the authentication over HTTPS, switch the full page to HTTPS and then back, by giving a non-secure cookie. Of course, this isn't very secure (someone could intercept that security token, as popularised by FireSheep), but this is better in that at least, the user will be able to check that the page where they enter their credentials is the legitimate one. (This should be done carefully too, see this question.)
The best way is to stay over HTTPS without iframes after authentication if you can.