I'd like to generate a file on a website using JavaScript and provide it for download by the user. I learned that this is not possible using plain JavaScript and HTML5.
I'm thinking of posting the generated file contents to a CGI function on my server that just echoes the data. By setting the right headers I could provide the data for download this way.
I'm wondering if such an echo CGI function could be misused and result in security problems. The website (also the CGI function) is password protected using basic authentication.
Any comments?
It is indeed possible to do this using data: URLs:
text file
However, if you still wish to use the CGI method, there are security risks of allowing a page to echo POSTed data. This is known as reflected Cross Site Scripting (XSS).
Say you have a CGI listening on https://example.com/cgi-bin/downloader.
Assume the user is already authenticated with your basic authentication and then receives an email containing a link. The user visits the website in the email (say evil.com) which creates a POST request submitted by JavaScript to https://example.com/cgi-bin/downloader containing a HTML document which also contains some JavaScript to send the cookies from your domain to the attacker. Even if you are setting the correct content-type header to identify the HTTP response as XML, some browsers (IE) will try to sniff the content and present the Content Type that the browser thinks it is to the user (HTML in this case). To avoid this, make sure that the following header is set in the response:
X-Content-Type-Options: nosniff
So using this header in combination with a Content Type of text/xml in the response should mitigate the risk of XSS. I also recommend the Content-Disposition header being set so the file will be downloaded rather than displayed:
Content-Disposition: attachment; filename="bar.xml"
To prevent other sites making requests to your CGI service in the first place, you could use a CSRF prevention method. The recommended method is the "Synchronizer Token Pattern" and this involves creating a server side token that is tied to user session that must be submitted with the POST request. Whether this is possible with your system using basic authentication is for you to decide. The Referer header can be used, although this is less secure:
checking the referer is considered to be a weaker form of CSRF protection. For example, open redirect vulnerabilities can be used to exploit GET-based requests that are protected with a referer check and some organizations or browser tools remove referrer headers as a form of data protection. There are also common implementation mistakes with referer checks. For example if the CSRF attack originates from an HTTPS domain then the referer will be omitted. In this case the lack of a referer should be considered to be an attack when the request is performing a state change. Also note that the attacker has limited influence over the referer. For example, if the victim's domain is "site.com" then an attacker have the CSRF exploit originate from "site.com.attacker.com" which may fool a broken referer check implementation. XSS can be used to bypass a referer check.
In short, referer checking is a reasonable form of CSRF intrusion detection and prevention even though it is not a complete protection. Referer checking can detect some attacks but not stop all attacks. For example, if you HTTP referrer is from a different domain and you are expecting requests from your domain only, you can safely block that request.
You should also test this to make sure that script will not be executed when an XML is downloaded using Internet Explorer.
If all it does is echo data coming from the client, I don't see any security issues. That data was already known to the client. You are just changing headers so the browser will allow you to save the contents as a file.
You are not disclosing any information to parties that don't already have access.
Related
I am testing CSRF on one website. The website prevents CSRF by checking the referrer field. Now I have created an HTML page which sends the request. How can I add the referrer field to that request using JavaScript or any other method?
A web page shouldn’t be able to add a Referer header to a request using JavaScript. However, according to Wikipedia, there are still some problems with relying on the Referer header:
Some proxies automatically strip out the Referer header. So it’s not always there.
A web page “can suppress the Referer header by issuing requests from FTP or HTTPS URLs.”
“[O]ld versions of Flash (before 9.0.18) allow malicious Flash to generate GET or POST requests with arbitrary HTTP request headers using CRLF Injection.”
If I build an SSL'ed API that authenticates with a session ID held within a cookie, adds a nonce as a query parameter, and always responds with a JSON 'Object' response (as opposed to a JSONP-style response with a callback), is it secure in general, and in particular against XSRF?
The intent with such an API to only have it available to pages on my own domain, and to be free to expose private data (such as username and emails) through this API (but not be consumable by other domains)--and retain a reasonable amount of simplicity for developers on the team.
Let me at least share what I understand about this approach, and why I think it's secure. Please enlight me if wrong!:
A <script> tag dropped on a 3rd-party domain to our site would send my cookies, but would not be able to parse the JSON object response (and the response would always deliberately be a JSON object at the top level). Also, I need to make sure that API calls that affect state on the server are all protected by non-GET method access, because <script> tags must use GET and so can not cause havok by attempt to call state-changing calls (in other words, the API would be adherent to REST in so far as HTTP methods go). Also, I deliberately do not support JSONP because it would be a security hole.
Man-in-the-middle used to hijack cookies (the session) is not a concern because I'm using SSL with valid certificates.
Replay attacks are a temporally limited concern because of the use of a nonce will limit how long one could send in a replay of an HTTPS request, because the server will make sure that the API call is only valid for a small amount of time in a typical nonce-validating way.
XMLHttpRequest can not make cross-domain calls, so it can't request anything from my site.
CORS (Cross ORigin Resource Sharing) is not of concern because I don't have a crossdomain.xml file or any other advertisement of cross-domain support associated with HTML 5.
An iframe in a 3rd-party site doesn't matter because even though it can load my page graphically, the host site can't access any data within that iframe, and because I've made no attempt to support cross-domain iframe communication (so they can attempt to set # on the iframe URL like folks do to enable communication between cross-domain iframes, but my page won't be responsive to it).
EDIT:
A nonce would also protect against even cross-domain GET requests (i.e., <script> tags) as russau says. In thinking on that specifically, I like the idea of asking for a nonce in a 'POST' API call that is not itself nonce protected; it should be the case that only XmlHTTPRequest's on the same domain can then generate a nonce to begin with. This seems to be a simple way of making the generation of nonce's developer-friendly. (i.e., nothing server-side for the website/javascript developers--just ask for your nonce from the same API you are using to develop against, and make requests with that nonce until you get a 'bad nonce' response--then ask for a new one, and repeat.
The only attack I can imagine is DNS rebinding. If your webserver is configured properly (a name-based vhost should be sufficient) you should be pretty safe though.
Just wondering if it's possible to use an XMLHTTPReq to login to a website, and store the cookie. Specifically I'm after the PHPSessionID from the website I am logging into.
I then want to pass this cookie into another request to submit a form.
Any ideas of how to do this?
Cheers,
Nick
You will be able to get your own site's cookies from document.cookie. In the AJAX callback, use a library to parse the value and read the cookie you're looking for.
Of course, if the server sets the cookie HttpOnly (which it should be doing), it won't be available in document.cookie.
At this pont, you need to reevaluate what you're doing:
If the form points to your website, your server script would have access to the cookie anyway.
If you're sending the user's session ID to another domain, why? This is a huge red flag that screams security problem.
If you're logging in to another site, then no – the same-origin policy prevents you from accessing another site's cookies.
Edit: Since this is for your own use, you can do this in a way you're not limited by the browser's origin restrictions. Some thoughts:
You could make a Chrome extension. Extensions aren't subject to origin restrictions, and the development model and API is pretty much the same as what you'd do on a regular web page.
You could use Node, which has no restrictions. You'd be able to invoke your script from the command line, but the API is going to be slightly different that what you'd use in a web page.
Use your language and framework of choice to POST to the login page, get the Set-Cookie header in the response, and use it to send a Cookie header in another POST to the form target.
You can only send cross-origin requests using XHR if both the browser and server support CORS. Additionally, the third party site needs to allow your site to send such requests and to receive its responses. If it doesn’t, you aren’t allowed to send the request or receive its response respectively.
I know its violates the Same origin policy, and that is why it is not possible through simple ajax request. I could use JSONP. But using JSONP for login doesn't sound secure ( no post only get ).
So is there a more secure way of implementing login into https through ajax ?
Not only does it violate the same origin policy, but since the page you are calling from is insecure it has the potential to be interfered with and leak all the data you are trying to keep secure.
Use HTTPS for the entire process.
Better yet, keep using HTTPS while people are logged in, otherwise you will have the Firesheep problem.
As we've discussed in the comments below, this is what Facebook does for their registration page, although there are some vulnerabilities to this method. While it won't appear secure to the user (no lock icon), the actual request is done over HTTPS. If you controlled the entirety of the receiving page, there would be nothing less secure about doing a JSONP request over GET. However, a man-in-the-middle attack could modify the receiving page on load, and cause the returned credentials to be sent to an attacker.
On the plus side though, no one that's just sniffing packets is going to be able to get the credentials: an attack would have to be fairly targeted.
Regarding cookies, technically, JSONP could "return" cookies; you'd just return name-value pairs of the cookies you wanted to set, and have a function on the receiving page set them.
But unless the browser treats <script>s differently, and it might, you should be able to set a cookie in the normal way using the Response Headers of your JSONP response.
I have already working modal login dialog. The problem is that if the origin page is loaded via http I still want to pass credentials to server via https. And of course I want to do with as little rewriting of working code as it can be.
I cannot use JSONP for my case because login data is passed to server via POST AJAX request.
Any ideas?
The Same Origin Policy makes this impossible (at least in browsers which don't support cross domain XHR, which is enough).
(And since the host document is served over HTTP it is subject to interception and alteration on the wire, which would make the data vulnerable even if it was transported over SSL)
Just out of curiosity, why don't you force the user to a secure page to begin with? Why had a similar issue a while back, so now, we force the user to https (via redirect) as soon as they hit our page.
Please note that according to Same-origin policy it should be not possible, as you're trying to post non-secured credentials to secured page. And if login landing page is not using SSL, then an attacker could modify the page as it is sent to the user and change the form submission location or insert JavaScript which steals the username/password as it is typed. So login landing page must use SSL.
To illustrate, the following table gives an overview of typical outcomes for checks against the URL "http://www.example.com/dir/page.html".
Compared URL Outcome Reason
http://www.example.com/dir/page2.html Success Same protocol and host
http://www.example.com/dir2/other.html Success Same protocol and host
http://u:pass#www.example.com/x/o.html Success Same protocol and host
http://www.example.com:81/dir/other.html Failure Same protocol and host but different port
https://www.example.com/dir/other.html Failure Different protocol
http://en.example.com/dir/other.html Failure Different host
http://example.com/dir/other.html Failure Different host (exact match required)
http://v2.www.example.com/dir/other.html Failure Different host (exact match required)
http://www.example.com:80/dir/other.html Depends Port explicit. Depends on implementation in browser.
Unlike other browsers, Internet Explorer does not include the port in the calculation of the origin, using the Security Zone in its place.
How to relax the same-origin policy
In some circumstances the same-origin policy is too restrictive, posing problems for large websites that use multiple subdomains. Here are four techniques for relaxing it:
document.domain property,
Cross-Origin Resource Sharing,
Cross-document messaging,
JSONP,
If you really what to do that, it is possible, but you need to make sure that your public key certificate of your website has been verified by certification authority therefore it is valid.
If it is not, you may try to add your certificate to the white list in your web browser. Or try with different web browsers.
Alternatevely you can make sure that users are always on a secure pages when being presented with the login form or disable modal form for login forms.
Other workaround include adding rewrite rule by forwarding the non-secured traffic into ssl, e.g.
# Various rewrite rules.
<IfModule mod_rewrite.c>
RewriteEngine on
# Force <front> to ssl for modal use of secure log in module.
RewriteRule http://www.example.net/^$ https://www.example.net [R=301,L]
See also:
Is posting from HTTP to HTTPS a bad practice?
Is it secure to submit from a HTTP form to HTTPS?
How to stop “secure and nonsecure items” warning on your site?
Getting Chrome to accept self-signed localhost certificate
Installing root certificate in Google Chrome