Possible vulnerability by content disposition header - security

Consider a response with Content-Type: application/json;charset=UTF-8 header and Content-Disposition: attachment;filename=text.txt header, with content-disp header appearing first , i am able to use the content-disp in my favor as a CSRF attack which make the file autodownload on the victim machine by including the URL as src in an iframe but if i am doing fetch i am blocked by CORB as the response does have application/json as content-type is there any way to smuggle the data i was able to download to a remote server

You may be able to trick the victim's browser into making the request and perhaps even downloading the response somewhere, but you cannot access the response in Javascript unless it has an Access-Control-Allow-Origin header that allows this.
The most you could do is measure the running time of the request and deduce something from that, using a mechanism like this:
var running, time = 0;
function tick(ts) {
if (running) time += ts;
requestAnimationFrame(tick);
}
function attack() {
requestAnimationFrame(tick);
document.querySelector('form').submit();
}
<body onload="attack()">
<form action="https://cross.origin.resource" target="target"></form>
<iframe name="target" onload="if (running) alert(time); else running = true;"></iframe>
</body>

Related

What's the purpose of setting http headers and setting appropriate status codes?

I understand headers are used to convey additional information between client and a server. I recently developed a small web-app in node for learning and have not updated any response status codes in headers manually.
For any get request I simply send back the appropriate response file which could be an ejs or html file, without bothering to update the headers. Well the app works just fine but now I see lot of other codes where the headers are updated before sending the response.
For e.g, res.writeHead(200, {'Content-Type': 'text/html'});
Whom does it help? Is it for debugging ? What's the big picture I am missing?
Let's start with an example of an http response borrowed from MDN:
HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: "51142bc1-7449-479b075b2891b"
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html
<!DOCTYPE html... (here come the 29769 bytes of the requested web page)
In this HTTP response, there are three parts to it. The first line gives you the protocol and the status. The next block of lines (up until an empty line) are the headers. Then, follows the (optional) content itself.
The status is a required part of an http response as it's sent with every response and informs the recipient whether this is a normal response with content (200) or a redirect (3xx) or an error (4xx or 5xx). Within nodejs, the status will generally default to 200 so if you don't set it, it will automatically be set to 200.
Http headers are used by the recipient for various things. What they are used for depend upon which header you're talking about. Here are a few examples:
Content-Type - Tells the recipient what type of content is in the body of the response. If they don't know the type of the content, then they have to guess and try to figure it out and that's far, far, far less desirable than telling them whether the content is meant to be text/plain or application/json or text/html or image/jpeg, etc...
Content-Length - Tells the recipient how long the content is in bytes. This is required when the content's format does not, by itself, tell the recipient where the end of the response is.
Transfer-Encoding - Tells the recipient how the body of the response is encoded for transfer (for example gzip).
Cache-Control - Gives the recipient information about how long this response can be cached.
Set-Cookie - Sends the recipient a cookie for the client to save and send back with future requests to this origin.
Location - A URI that goes with a 3xx status to indicate where the client should redirect to.
These were just a few examples. There are probably thousands of possible headers, each with their own purpose.
Whom does it help?
It's used by the code that is receiving the http response to know how to interpret it.
Is it for debugging?
That's not its main purpose, but when deubgging a problem, you may look at the detailed headers to see if they are what you expect them to be. For example, if req.body is empty in Express when you're expecting it to be populated, you would look at the content-type to see if it's what you were expecting and matches something you have middleware installed for to read and parse and put into req.body because if the content-type isn't something you expected, then your code that receives this response wouldn't be configured properly to handle it.
What's the big picture I am missing?
Headers are meta data that describes what's in the response and gives the recipient information that is often necessary for knowing how to read the response properly.

What is the purpose of Content-Type at response header?

const binary = xxxxx;
res.set('Content-Type', 'image/jpg');
res.send(binary);
The above is sample on returning an image, defining on express route. What I don't understand is that despite I change Content-Type to application/json, it seems to work too?
const binary = xxxxx;
res.set('Content-Type', 'application/json'); //Still returning an image?
res.send(binary);
The Content-Type header merely indicates the media type of the resource. Browsers can use MIME sniffing and completely ignore it in some cases. To prevent this behaviour, you can set X-Content-Type-Options to nosniff.
A question arises: do I even need to set this entity header? Without setting it you force browsers to sniff the MIME type every single time and thus potentially interpret and display the response body as a content type other than the intended one. If your server allows the user to upload files, a hacker can carry out an XSS attack by manipulating the content in a way to be accepted by the web application and rendered as HTML by the browser. He can inject code in e.g. an image file and make the victim execute it by viewing the image.

cors+s3+browser cache+chrome extension

Yes. this is a complex question. i will try to nake it brief.
My website fetches resources from s3.
I also have an extension that needs to prefetch that s3 file when someone does a google query, so later when they go on my site ,the resource is cached.
At this point I should probably stress that I'm not doing anything malicious. just a matter of user experience.
My problem is. that making an ajax request to s3 fron the extension (either from content-script or background) doesn't send an origin header.
This means that the resource is downloaded and cached without an allow origin header. s3
doesnt add that allow-origin:* if theres no origin in the request. so later, on my site it fails due to missing allow-origin header in cached file :-(
Any ideas on a better way to prefetch to browser cache?
Is there a way to force the ajax request to send an origin? Any origin?
Since I have an allow-origin:* on my s3 bucket, I think any origin will do accept null.
Thanks
Edit: Ended up using one of Rob W's solutions. You are awesome.
Let me comment on each of the options he suggested:
Not to add the host premissions on my manifest - clever idea but wouldn't work for me since I have a content script which runs on any website, so I must use a catch-all wildcard, and I don't think there is an "exclude" permission option.
I tried it, it issues a null origin, which as expected ends up in S3 sending the allow-origin:* header as required. this means I don't get that "allow-origin header missing" error, however the file is then not served from cache. I guess for it to be actually served from cache in chrome this has to be exactly the same origin. so that was very close but not enough.
third option is a charm. And it is the simplest. I didn't know I was able to manipulate the origin header. So I do that and set the exact origin of my website - And it works. The file is cached and later served from cache. I must stress that i had to add a Url filter to only apply this to requests going out to my s3 bucket, otherwise I expect this would wreak havoc on the user's browser.
Thanks. Well done
You've got three options:
Do not add the host permission for S3 to your manifest file. Then the extension will not have the blanket permission to access the resource, and an Origin request header will be sent with the request.
Use a non-extension frame to perform the AJAX request. For example, the following method will result in a cross-origin GET request with Origin: null.
function prefetchWithOrigin(url) {
var html = '<script>(' + function(url) {
var x = new XMLHttpRequest();
x.open('GET', url);
x.onloadend = function() {
parent.postMessage('done', '*');
};
x.send();
} + ')(' + JSON.stringify(url) + ');</script>';
var f = document.createElement('iframe');
f.src = 'data:text/html,' + encodeURIComponent(html);
(document.body || document.documentElement).appendChild(f);
window.addEventListener('message', function listener(event) {
// Remove frame upon completion
if (event.source === f.contentWindow) {
window.removeEventListener('message', listener);
f.remove();
}
});
}
Use the chrome.webRequest.onBeforeSendHeaders event to manually append the Origin header.

NodeJS/express: Cache and 304 status code

When I reload a website made with express, I get a blank page with Safari (not with Chrome) because the NodeJS server sends me a 304 status code.
How to solve this?
Of course, this could also be just a problem of Safari, but actually it works on all other websites fine, so it has to be a problem on my NodeJS server, too.
To generate the pages, I'm using Jade with res.render.
Update: It seems like this problem occurs because Safari sends 'cache-control': 'max-age=0' on reload.
Update 2: I now have a workaround, but is there a better solution?
Workaround:
app.get('/:language(' + content.languageSelector + ')/:page', function (req, res)
{
// Disable caching for content files
res.header("Cache-Control", "no-cache, no-store, must-revalidate");
res.header("Pragma", "no-cache");
res.header("Expires", 0);
// rendering stuff hereā€¦
}
Update 3:
So the complete code part is currently:
app.get('/:language(' + content.languageSelector + ')/:page', pageHandle);
function pageHandle (req, res)
{
var language = req.params.language;
var thisPage = content.getPage(req.params.page, language);
if (thisPage)
{
// Disable caching for content files
res.header("Cache-Control", "no-cache, no-store, must-revalidate");
res.header("Pragma", "no-cache");
res.header("Expires", 0);
res.render(thisPage.file + '_' + language, {
thisPage : thisPage,
language: language,
languages: content.languages,
navigation: content.navigation,
footerNavigation: content.footerNavigation,
currentYear: new Date().getFullYear()
});
}
else
{
error404Handling(req, res);
}
}
Easiest solution:
app.disable('etag');
Alternate solution here if you want more control:
http://vlasenko.org/2011/10/12/expressconnect-static-set-last-modified-to-now-to-avoid-304-not-modified/
Try using private browsing in Safari or deleting your entire cache/cookies.
I've had some similar issues using chrome when the browser thought it had the website in its cache but actually had not.
The part of the http request that makes the server respond a 304 is the etag. Seems like Safari is sending the right etag without having the corresponding cache.
I had the same problem in Safari and Chrome (the only ones I've tested) but I just did something that seems to work, at least I haven't been able to reproduce the problem since I added the solution. What I did was add a metatag to the header with a generated timstamp. Doesn't seem right but it's simple :)
<meta name="304workaround" content="2013-10-24 21:17:23">
Update
P.S
As far as I can tell, the problem disappears when I remove my node proxy (by proxy i mean both express.vhost and http-proxy module), which is weird...
As you said, Safari sends Cache-Control: max-age=0 on reload. Express (or more specifically, Express's dependency, node-fresh) considers the cache stale when Cache-Control: no-cache headers are received, but it doesn't do the same for Cache-Control: max-age=0. From what I can tell, it probably should. But I'm not an expert on caching.
The fix is to change (what is currently) line 37 of node-fresh/index.js from
if (cc && cc.indexOf('no-cache') !== -1) return false;
to
if (cc && (cc.indexOf('no-cache') !== -1 ||
cc.indexOf('max-age=0') !== -1)) return false;
I forked node-fresh and express to include this fix in my project's package.json via npm, you could do the same. Here are my forks, for example:
https://github.com/stratusdata/node-fresh
https://github.com/stratusdata/express#safari-reload-fix
The safari-reload-fix branch is based on the 3.4.7 tag.
Old question, I know. Disabling the cache facility is not needed and not the best way to manage the problem. By disabling the cache facility the server needs to work harder and generates more traffic. Also the browser and device needs to work harder, especially on mobile devices this could be a problem.
The empty page can be easily solved by using Shift key+reload button at the browser.
The empty page can be a result of:
a bug in your code
while testing you served an empty page (you can't
remember) that is cached by the browser
a bug in Safari (if so,
please report it to Apple and don't try to fix it yourself)
Try first the Shift keyboard key + reload button and see if the problem still exists and review your code.
Operating system: Windows
Browser: Chrome
I used Ctrl + F5 keyboard combination. By doing so, instead of reading from cache, I wanted to get a new response. The solution is to do hard refresh the page.
On MDN Web Docs:
"The HTTP 304 Not Modified client redirection response code indicates
that there is no need to retransmit the requested resources. It is an
implicit redirection to a cached resource."
// just add * in URL
app.get('/api*', (req, res)=>{
// do something
});

Express (node.js) seems to be replacing my content-type with application/json

I've written an express web app route that is essentially a proxy - it pipes the contents of an input stream (a zip file) into the server response's output stream.
I'd like the browser to prompt the user for download or whatever is most appropriate for a zip file. However, when I load this route in a browser, the contents of the input stream (the zip file's contents) show up in the browser window as text, rather than prompting a download. l
This is the code sending the response:
res.statusCode = 200;
res.setHeader ('Content-Length', size);
res.setHeader ('Content-Type', 'application/zip');
console.log ("content-type is " + res.header('Content-Type'));
inputStream.pipe (res);
The console.log statement above outputs "content-type is application/zip".
However, when I examine the request in Chrome's network tab, I see that the response's content-type is "application/json". This implies that express, or something else, is overriding my content-type header, or perhaps has already sent it.
Does anyone know what is changing the content-type on me, and how I could make sure the content-type is the one I set?
Thanks for any help.
You should check the order of the middleware, it's really tricky and can mess things up if they are in the correct order.
You can check the correct order here in the Connect webpage

Resources