📜 ⬆️ ⬇️

Exotic HTTP Headers


Hi Habrahabr! This article will demonstrate the effect of using some important and exotic HTTP headers, most of which are related to security.

X-XSS-Protection


An XSS (cross-site scripting) attack is a type of attack in which malicious code can be injected into an attacked page.

For example, like this:

<h1>Hello, <script>alert('hacked')</script></h1> 

This type of attack is easy to detect and the browser can easily deal with it: if the source code contains a part of the request, then this may be a threat.
')
And the X-XSS-Protection header controls this browser behavior.

Accepted values:


Create a web server sandbox on node.js to see how this works.

 var express = require('express') var app = express() app.use((req, res) => { if (req.query.xss) res.setHeader('X-XSS-Protection', req.query.xss) res.send(`<h1>Hello, ${req.query.user || 'anonymous'}</h1>`) }) app.listen(1234) 

I will use Google Chrome 55.

No title


 http://localhost:1234/?user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E 

Nothing happens, the browser successfully blocks the attack. Chrome, by default, blocks the threat and reports this to the console.



He even highlights the problem area in the source code.



X-XSS-Protection: 0


 http://localhost:1234/?user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E&xss=0 

Oh no!



X-XSS-Protection: 1


 http://localhost:1234/?user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E&xss=1 

The page has been cleared due to an explicit title.



X-XSS-Protection: 1; mode = block


 http://localhost:1234/?user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E&xss=1;%20mode=block 

In this case, the attack will be prevented by blocking the page loading.



X-XSS-Protection: 1; report = http: // localhost: 1234 / report


 http://localhost:1234/?user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E&xss=1;%20report=http://localhost:1234/report 

The attack was prevented and the message was sent to the appropriate address.



X-Frame-Options


With this header you can protect yourself from the so-called Clickjacking [Clickjacking].

Imagine that an attacker has a YouTube channel and wants more subscribers.

He can create a page with the “Do not click” button, which will mean that everyone will click on it. But over the button there is an absolutely transparent iframe and in this frame there is a channel page with a subscription button. Therefore, when you click on the button, the user actually subscribes to the channel, unless of course he was logged into YouTube.

Let's demonstrate it.

First you need to install an extension to ignore this header.

Create a simple page.

 <style> button { background: red; color: white; padding: 10px 20px; border: none; cursor: pointer; } iframe { opacity: 0.8; z-index: 1; position: absolute; top: -570px; left: -80px; width: 500px; height: 650px; } </style> <button>Do not click his button!</button> <iframe src="https://youtu.be/dQw4w9WgXcQ?t=3m33s"></iframe> 



As you can see, I placed a frame with a subscription right above the button ( z-index: 1 ) and therefore, if you try to click on it, the frame is actually pressed. In this example, the frame is not completely transparent, but this is corrected by the opacity value : 0 .

In practice, this will not work, because YouTube has the right title, but I hope the meaning of the threat is clear.

To prevent a page from being used in a frame, use the X-Frame-Options header.

Accepted values:


We will need a web server for demonstration.

 var express = require('express') for (let port of [1234, 4321]) { var app = express() app.use('/iframe', (req, res) => res.send(`<h1>iframe</h1><iframe src="//localhost:1234?h=${req.query.h || ''}"></iframe>`)) app.use((req, res) => {   if (req.query.h) res.setHeader('X-Frame-Options', req.query.h)   res.send('<h1>Website</h1>') }) app.listen(port) } 

No title


Everyone will be able to embed our website at localhost: 1234 into the frame.



X-Frame-Options: deny


The page generally can not be used in the frame.



X-Frame-Options: sameorigin


Only pages with the same source will be able to embed in the frame. The sources match if the domain, port, and protocol are the same.



X-Frame-Options: allow-from localhost : 4321


It seems that Chrome ignores this option, because there is a Content-Security-Policy header (it will be described below). It does not work in Microsoft Edge.

Below is Mozilla Firefox.



X-Content-Type-Options


This header prevents attacks with the substitution of MIME types ( <script src = "script.txt"> ) or unauthorized hotlink ( <script src = "https://raw.githubusercontent.com/user/repo/branch/file.js"> )

 var express = require('express') var app = express() app.use('/script.txt', (req, res) => { if (req.query.h) res.header('X-Content-Type-Options', req.query.h) res.header('content-type', 'text/plain') res.send('alert("hacked")') }) app.use((req, res) => { res.send(`<h1>Website</h1><script src="/script.txt?h=${req.query.h || ''}"></script>`) }) app.listen(1234) 

No title


 http://localhost:1234/ 

Although script.txt is a text file with type text / plain , it will run as a script.



X-Content-Type-Options: nosniff


 http://localhost:1234/?h=nosniff 

This time the types do not match and the file will not be executed.



Content-Security-Policy


This is a relatively young headline and helps reduce the risks of XSS attack in modern browsers by specifying in the header what resources can be loaded on the page.

For example, you can ask the browser not to execute inline skrpity and download files from only one domain. Inline skrpity can look not only as <script> ... </ script> , but also as <h1 onclick = "..."> .

Let's see how it works.

 var request = require('request') var express = require('express') for (let port of [1234, 4321]) { var app = express() app.use('/script.js', (req, res) => {   res.send(`document.querySelector('#${req.query.id}').innerHTML = ' ${req.query.id}-'`) }) app.use((req, res) => {   var csp = req.query.csp   if (csp) res.header('Content-Security-Policy', csp)   res.send(`     <html>     <body>       <h1>Hello, ${req.query.user || 'anonymous'}</h1>       <p id="inline">   inline-?</p>       <p id="origin">   origin-?</p>       <p id="remote">   remote-?</p>       <script>document.querySelector('#inline').innerHTML = ' inline-'</script>       <script src="/script.js?id=origin"></script>       <script src="//localhost:1234/script.js?id=remote"></script>     </body>     </html>     `) }) app.listen(port) } 

No title


It works the way you expected.



Content-Security-Policy: default-src 'none'


 http://localhost:4321/?csp=default-src%20%27none%27&user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E 

default-src applies the rule for all resources (images, scripts, frames, etc.), the value 'none' blocks everything. Below is a demonstration of what is happening and the errors shown in the browser.



Chrome refused to run any scripts. In this case, it will not even work to download favicon.ico .

Content-Security-Policy: default-src 'self'


 http://localhost:4321/?csp=default-src%20%27self%27&user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E 

Now you can use resources from one source, but still you can not run external and inline-scripts.



Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'


 http://localhost:4321/?csp=default-src%20%27self%27;%20script-src%20%27self%27%20%27unsafe-inline%27&user=%3Cscript%3Ealert(%27hacked%27)%3C/script%3E 

This time we allowed the execution and inline-scripts. Please note that the XSS attack in the request was also blocked. But this will not happen if both unsafe-inline and X-XSS-Protection are simultaneously put : 0 .



Other values


The site content-security-policy.com beautifully shows many examples.


I did not check this, but I think the following headings are equivalent:


If you look at the headers of facebook.com or twitter.com, you will notice that these sites use a lot of CSP.

Strict-Transport-Security


HTTP Strict Transport Security (HSTS) is a security policy mechanism that protects a site from an unsafe connection attempt.

Let's say we want to connect to facebook.com . If you do not type https: // before the request, then the protocol, by default, will be selected HTTP and therefore the request will look like http://facebook.com .

 $ curl -I facebook.com HTTP/1.1 301 Moved Permanently Location: https://facebook.com/ 

After that, we will be redirected to a secure version of Facebook.

If you connect to a public WiFi point that belongs to an attacker, then the request can be intercepted and instead of facebook.com, the attacker can substitute a similar page to find out the login and password.

To protect against such an attack, you can use the above header, which tells the client to use the https version of the site next time.

 $ curl -I https://www.facebook.com/ HTTP/1.1 200 OK Strict-Transport-Security: max-age=15552000; preload 

If a user was logged in to Facebook at home, and then tried to open it from an unsafe access point, then nothing threatens him, because browsers remember this header.

But what will happen if you connect to an insecure network for the first time? In this case, the defense will not work.

But browsers have a trump card in this case. They have a predefined list of domains for which you should only use HTTPS.

You can send your domain to this address. There you can also find out if the title is used correctly.

Accepted values:


And if you need to switch to HTTP before the expiration of the max-age, or if the preload is set? You can set the value max-age = 0 and then the rule for switching to the https version will stop working.

Public key pins


HTTP Public Key Pinning (HPKP) is a security policy mechanism that allows HTTPS sites to protect themselves from fraudulent or fraudulent certificates.

Accepted values:


Instead of the Public-Key-Pins header, you can use the Public-Key-Pins-Report-Only , in this case, only key matching error messages will be sent, but the browser will still load the page.

Facebook does this:

 $ curl -I https://www.facebook.com/ HTTP/1.1 200 OK ... Public-Key-Pins-Report-Only:     max-age=500;     pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=";     pin-sha256="r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=";     pin-sha256="q4PO2G2cbkZhZ82+JgmRUyGMoAeozA+BSXVXQWB8XWQ=";     report-uri="http://reports.fb.com/hpkp/" 

Why do you need it? Are not trusted certificate authorities (CA) enough?

An attacker can create a certificate for facebook.com and, by deceit, force the user to add it to their trust store or he can be an administrator.

Let's try to create a certificate for facebook.

 sudo mkdir /etc/certs echo -e 'US\nCA\nSF\nFB\nXX\nwww.facebook.com\nno@spam.org' | \ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \   -keyout /etc/certs/facebook.key \   -out /etc/certs/facebook.crt 

And make it trusted on the local system.

 # curl sudo cp /etc/certs/*.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates # Google Chrome sudo apt install libnss3-tools -y certutil -A -t "C,," -n "FB" -d sql:$HOME/.pki/nssdb -i /etc/certs/facebook.crt # Mozilla Firefox #certutil -A -t "CP,," -n "FB" -d sql:`ls -1d $HOME/.mozilla/firefox/*.default | head -n 1` -i /etc/certs/facebook.crt 

And now we will launch a web server using this certificate.

 var fs = require('fs') var https = require('https') var express = require('express') var options = { key: fs.readFileSync(`/etc/certs/${process.argv[2]}.key`), cert: fs.readFileSync(`/etc/certs/${process.argv[2]}.crt`) } var app = express() app.use((req, res) => res.send(`<h1>hacked</h1>`)) https.createServer(options, app).listen(443) 

Switch to server

 echo 127.0.0.1 www.facebook.com | sudo tee -a /etc/hosts sudo node server.js facebook 

Let's see what happened

 $ curl https://www.facebook.com <h1>hacked</h1> 

Fine. curl confirms the certificate.

Since I already went to Facebook and Google Chrome saw his headlines, he should report the attack but allow the page, right?



Nope Keys were not checked due to local root certificate [Public-key pinning bypassed]. It is interesting…

Well, what about www.google.com ?

 echo -e 'US\nCA\nSF\nGoogle\nXX\nwww.google.com\nno@spam.org' | \ sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \   -keyout /etc/certs/google.key \   -out /etc/certs/google.crt sudo cp /etc/certs/*.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates certutil -A -t "C,," -n "Google" -d sql:$HOME/.pki/nssdb -i /etc/certs/google.crt echo 127.0.0.1 www.google.com | sudo tee -a /etc/hosts sudo node server.js google 

The same result. I think this is a feature.

But in any case, if you don’t add these certificates to your local storage, you won’t be able to open sites, because there is no option to continue an insecure connection in Chrome or add an exception in Firefox.





Content-Encoding: br


The data is compressed using Brotli .

The algorithm promises better compression than gzip and a comparable unarching speed. Supported by Google Chrome .

Of course, for him there is a module in node.js.

 var shrinkRay = require('shrink-ray') var request = require('request') var express = require('express') request('https://www.gutenberg.org/files/1342/1342-0.txt', (err, res, text) => { if (err) throw new Error(err) var app = express() app.use(shrinkRay()) app.use((req, res) => res.header('content-type', 'text/plain').send(text)) app.listen(1234) }) 

Initial size: 700 Kb
Brotli: 204 Kb
Gzip: 241 Kb





Timing-Allow-Origin


Using the Resource Timing API, you can find out how long it took to process resources on a page.

Since load time information can be used to determine whether a user has visited a page before (taking note that resources can be cached), the standard is considered vulnerable if such information is given to any hosts.

 <script> setTimeout(function() { console.log(window.performance.getEntriesByType('resource')) }, 1000) </script> <img src="http://placehold.it/350x150"> <img src="/local.gif"> 

It seems that if you do not specify Timing-Allow-Origin , then you can get detailed information about the time of operations (domain search, for example) only for resources with one source.



You can use it like this:


Alt-svc


Alternate Services [Alternative Services] allow resources to be located in different parts of the network and can be accessed using different protocol configurations.

This is used by Google:


This means that the browser, if it wishes, can use QUIC , this is HTTP over UDP, through port 443 for the next 30 days (ma = 2592000 seconds, or 720 hours, that is, 30 days). I have no idea what the parameter v , version means?

P3p


Below are a few P3P headers I have met:


Some browsers require third party cookies to support the P3P protocol to indicate privacy measures.

The organization that founded P3P, the World Wide Web Consortium (W3C), suspended work on the protocol several years ago because modern browsers do not fully support the protocol. As a result, P3P is outdated and does not include the technologies that are currently used on the network, so most sites do not support P3P.

I did not go too deep, but apparently the title is needed for IE8 to accept third party cookies.

For example, if privacy setting in IE is high, then all cookies from sites that do not have a compact privacy policy will be blocked, but those that have headers like the ones mentioned above will not be blocked.

Source: https://habr.com/ru/post/317720/


All Articles