mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-23 00:00:06 +01:00
127 lines
4.9 KiB
Markdown
127 lines
4.9 KiB
Markdown
![]() |
## Using Content Security Policy (CSP)
|
||
|
|
||
|
### What is it?
|
||
|
|
||
|
Modern browsers have recently implemented a new feature providing
|
||
|
a sort of "selinux for your web page". If the server sends some
|
||
|
new headers describing the security policy for the content, then
|
||
|
the browser strictly enforces it.
|
||
|
|
||
|
### Why would we want to do that?
|
||
|
|
||
|
Scripting on webpages is pretty universal, sometimes the scripts
|
||
|
come from third parties, and sometimes attackers find a way to
|
||
|
inject scripts into the DOM, eg, through scripts in content.
|
||
|
|
||
|
CSP lets the origin server define what is legitimate for the page it
|
||
|
served and everything else is denied.
|
||
|
|
||
|
The CSP for warmcat.com and libwebsockets.org looks like this,
|
||
|
I removed a handful of whitelisted image sources like travis
|
||
|
status etc for clarity...
|
||
|
|
||
|
```
|
||
|
"content-security-policy": "default-src 'none'; img-src 'self' data:; script-src 'self'; font-src 'self'; style-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'none';",
|
||
|
"x-content-type-options": "nosniff",
|
||
|
"x-xss-protection": "1; mode=block",
|
||
|
"x-frame-options": "deny",
|
||
|
"referrer-policy": "no-referrer"
|
||
|
```
|
||
|
|
||
|
The result of this is the browser won't let the site content be iframed, and it
|
||
|
will reject any inline styles or inline scripts. Fonts, css, ajax, ws and
|
||
|
images are only allowed to come from 'self', ie, the server that served the
|
||
|
page. You may inject your script, or deceptive styles: it won't run or be shown.
|
||
|
|
||
|
Because inline scripts are banned, the usual methods for XSS are dead;
|
||
|
the attacker can't even load js from another server. So these rules
|
||
|
provide a very significant increase in client security.
|
||
|
|
||
|
### Implications of strict CSP
|
||
|
|
||
|
Halfhearted CSP isn't worth much. The only useful approach is to start
|
||
|
with `default-src 'none'` which disables everything, and then whitelist the
|
||
|
minimum needed for the pages to operate.
|
||
|
|
||
|
"Minimum needed for the pages to operate" doesn't mean defeat the protections
|
||
|
necessary so everything in the HTML can stay the same... it means adapt the
|
||
|
pages to want the minimum and then enable the minimum.
|
||
|
|
||
|
#### Inline styles must die
|
||
|
|
||
|
All styling must go in one or more `.css` file served by the same server
|
||
|
(preferably... you can whitelist other sources in the CSP if you have to).
|
||
|
|
||
|
Inline styles are no longer allowed (eg, "style='font-size:120%'" in the
|
||
|
HTML)... they must be replaced by reference to one or more CSS class, which
|
||
|
in this case includes "font-size:120%". This has always been the best
|
||
|
practice anyway, and your pages will be cleaner and more maintainable.
|
||
|
|
||
|
#### Inline scripts must die
|
||
|
|
||
|
Inline scripts need to be placed in a `.js` file and loaded in the page head
|
||
|
section, from the server that provided the page.
|
||
|
|
||
|
Then, any kind of inline script, yours or injected or whatever, will be
|
||
|
completely rejected by the browser.
|
||
|
|
||
|
#### onXXX must be replaced by eventListener
|
||
|
|
||
|
Inline `onclick` or whatever are kinds of inline scripting and are banned.
|
||
|
|
||
|
Modern browsers have offered a different system called ["EventListener" for
|
||
|
a while](https://developer.mozilla.org/en-US/docs/Web/API/EventListener) which allows binding of events to DOM elements in JS.
|
||
|
|
||
|
A bunch of different named events are possible to listen on, commonly the
|
||
|
`.js` file will ask for one or both of
|
||
|
|
||
|
```
|
||
|
window.addEventListener("load", function() {
|
||
|
...
|
||
|
}, false);
|
||
|
|
||
|
document.addEventListener("DOMContentLoaded", function() {
|
||
|
...
|
||
|
}, false);
|
||
|
```
|
||
|
|
||
|
These give the JS a way to trigger when either everything on the page has
|
||
|
been "loaded" or the DOM has been populated from the initial HTML. These
|
||
|
can set up other event listeners on the DOM objects and aftwards the
|
||
|
events will drive what happens on the page from user interaction and / or
|
||
|
timers etc.
|
||
|
|
||
|
If you have `onclick` in your HTML today, you would replace it with an id
|
||
|
for the HTML element, then eg in the DOMContentLoaded event listener,
|
||
|
apply
|
||
|
|
||
|
```
|
||
|
document.getElementById("my-id").addEventListener("click", function() {
|
||
|
...
|
||
|
}, false);
|
||
|
```
|
||
|
|
||
|
ie the .js file becomes the only place with the "business logic" of the
|
||
|
elements mentioned in the HTML, applied at runtime.
|
||
|
|
||
|
#### Do you really need external sources?
|
||
|
|
||
|
Do your scripts and fonts really need to come from external sources?
|
||
|
If your caching policy is liberal, they are not actually that expensive
|
||
|
to serve once and then the user is using his local copy for the next
|
||
|
days.
|
||
|
|
||
|
Some external sources are marked as anti-privacy in modern browsers, meaning
|
||
|
they track your users, in turn meaning if your site refers to them, you
|
||
|
will lose your green padlock in the browser. If the content license allows
|
||
|
it, hosting them on "self", ie, the same server that provided the HTML,
|
||
|
will remove that problem.
|
||
|
|
||
|
Bringing in scripts from external sources is actually quite scary from the
|
||
|
security perspective. If someone hacks the jquery site to serve a hostile
|
||
|
payload in addition to the usual scripts, half the Internet will instantly
|
||
|
become malicious. However if you serve it yourself, unless your server
|
||
|
was specifically targeted you know it will continue to serve what you
|
||
|
expect.
|
||
|
|