Secure your Angular Applications

Gleb Bahmutov

aka "Dr Gleb"

Day 1

Track 2

11:00

Gleb Bahmutov

Conferences: ng-conf, AngularConnect 2016, etc.

I write things down

58 blog posts tagged "angularjs"

@bahmutov

- "Unit testing AngularJS code in record time"

- "Stop (silent) Angular (name) overrides"

- "Improving Angular web app performance"

Practical Angular advice for my team

@bahmutov

Work at Kensho

A DATA ANALYTICS AND MACHINE INTELLIGENCE COMPANY

Boston / NYC

Work at Kensho

A DATA ANALYTICS AND MACHINE INTELLIGENCE COMPANY

What happens to the value of X when Y happens?

@bahmutov

The Keynote was like

@bahmutov

This presentation is like

time

today

2013

future

Presentation timeline

Angular 1.0

Angular 1.x

Vue.js

Aurelia

Angular 2

OWASP top 10

Server-side

rendering

No more security?

1985

The Theme

Security is compromised when there is mismatch of expectations between two parts of the system

@bahmutov

Example

Database code assumes user email has been validated and sanitized

sqlCommand('SELECT * FROM USERS WHERE email=' + email)

@bahmutov

Example

Website code assumes database code checks the email in secure fashion

sqlCommand('SELECT * FROM USERS WHERE email=' + email)
https://mysite.com/user?email=joe@mysite.com or 1=1
https://mysite.com/user?email=joe@mysite.com

Injection attack

Example

Mismatch of expectations between the website and database systems

// the input data has been sanitized and can be trusted
// I expect SQL library to guard against invalid inputs

Injection attack

2013 uTest (now Applause)

Mobile test + crash + App Store data = profit

@bahmutov

performance, crashes

performance  - 2 stars

reliability         - 1 star

ui features      - 0.5 stars

....

@bahmutov

Current rating: 2.5

+ performance improvements

= 4.5

@bahmutov

Let us build a dashboard!

  • Angular 1

  • Node

  • various data sources

  • multiple audiences

@bahmutov

Designer

Me

mockups

website

Mockups

Balsamiq mockup https://balsamiq.com/products/mockups/

Business idea: export mockups as web components

Balsamiq mockup https://balsamiq.com/products/mockups/

Complex state and REST are saving our jobs, people!

Balsamiq mockup https://balsamiq.com/products/mockups/

Pixel pushing

Lorem ipsum dolor sit amet, esse deseruisse dissentias et duo, in tantas putant urbanitas nam. Per dicant copiosae ea, duo ex omnesque copiosae. In rebum zril integre mea, an zril ornatus disputando pri. Mei at laudem ceteros, vidisse electram intellegam mel an, amet consul qui eu. Vix id melius consetetur, nam ea congue legimus oportere. Vim ne fastidii volutpat salutatus, nulla ludus at duo.

@bahmutov

@bahmutov

Hue / Saturation / Value

saturation

grey

red

@bahmutov

Hue / Saturation / Value

value

dark

light

@bahmutov

Designer: red!

bold! white space!

Me: so like #ff000 and padding 3em?

@bahmutov

Color pushing

Lorem ipsum dolor sit amet, esse deseruisse dissentias et duo, in tantas putant urbanitas nam. Per dicant copiosae ea, duo ex omnesque copiosae. In rebum zril integre mea, an zril ornatus disputando pri. Mei at laudem ceteros, vidisse electram intellegam mel an, amet consul qui eu. Vix id melius consetetur, nam ea congue legimus oportere. Vim ne fastidii volutpat salutatus, nulla ludus at duo.

This background?

@bahmutov

Color pushing

Lorem ipsum dolor sit amet, esse deseruisse dissentias et duo, in tantas putant urbanitas nam. Per dicant copiosae ea, duo ex omnesque copiosae. In rebum zril integre mea, an zril ornatus disputando pri. Mei at laudem ceteros, vidisse electram intellegam mel an, amet consul qui eu. Vix id melius consetetur, nam ea congue legimus oportere. Vim ne fastidii volutpat salutatus, nulla ludus at duo.

Or this background?

@bahmutov

Color pushing

Lorem ipsum dolor sit amet, esse deseruisse dissentias et duo, in tantas putant urbanitas nam. Per dicant copiosae ea, duo ex omnesque copiosae. In rebum zril integre mea, an zril ornatus disputando pri. Mei at laudem ceteros, vidisse electram intellegam mel an, amet consul qui eu. Vix id melius consetetur, nam ea congue legimus oportere. Vim ne fastidii volutpat salutatus, nulla ludus at duo.

#1e1e1e

#222222

@bahmutov

color swatch

AngularConnect with custom colors

@bahmutov

Color pushing by

  • Sitting together with designer

  • Designer running local deploy

  • Something better?

or

or

@bahmutov

<!-- assumes jQuery / Bootstrap / Angular 1 -->
<link rel="stylesheet" href="color-pusher/dist/color-pusher.css">
<script src="color-pusher/dist/color-pusher.js"></script>
// if DEBUG_SERVER
<div ng-app="color-pusher">
    <color-pusher></color-pusher>
</div>
// endif

If you own the page

@bahmutov

load color-pusher via code snippet

If you do not control the page source

color-pusher demo

color-pusher widget

Requires changes to the page 👎

Creates an extra feature flag / backdoor 👎

@bahmutov

color-pusher code snippet

DevTools, hard to update 👎

@bahmutov

color-pusher Chrome extension!

Can be installed once, auto updates 👍

Can be DevTools panel 👍

@bahmutov

@bahmutov

color-pusher DevTools panel

@bahmutov

Panels can be inspected just like other web apps in DevTools

@bahmutov

color-pusher extension

<link href="libs/bootstrap.min.css" rel="stylesheet" />
<link href="libs/color-pusher.css" rel="stylesheet" />
<link href="libs/angular-csp.css" rel="stylesheet" />
<link href="color-pusher-panel.css" rel="stylesheet" />

<script src="libs/jquery.min.js"></script>
<script src="libs/bootstrap.min.js"></script>
<script src="libs/angular.js"></script>
<script src="libs/color-pusher.js"></script>

<script src="color-pusher-panel.js"></script>

Note 1: where is the lib code?

color-pusher extension

<link href="https://maxcdn.../font-awesome.min.css"
    rel="stylesheet" />

<script src="https://code.jquery.com/jquery-3.1.0.min.js">
</script>

Can load resources from https, blob, filesystem, http://localhost

@bahmutov

Extension docs

Currently, we allow whitelisting origins with the following schemes: blob, filesystem, https, chrome-extension, and chrome-extension-resource.

@bahmutov

Security rule 1

Whitelisting good behavior works. Blacklisting all possible bad behavior does not.

@bahmutov

Example: make a list of trusted websites

google.com

hackme.com

easymoney.ru

bitcoinz.cn

mybank.ro

netflx.com

...

@bahmutov

Allowing only secure resources (https, wss) is a simple whitelisting rule

@bahmutov

Insecure features are cut

No data from JSONP http

<script src="http://foo...?callback=JSONP">
</script>
var url = 'http://www.colourlovers.com/api/palette/' 
  + $scope.paletteId
var options = {
  url: url,
  params: {
    format: 'json',
    jsonCallback: 'JSON_CALLBACK'
  }
}
$http.jsonp(url, options)
JSONP({results: 'foo', ...})

@bahmutov

Why does JSONP exist?

Why not CORS? Why not https?

@bahmutov

source: http://www.summitpost.org/

JSONP

AJAX

WWW

CORS

HTTPS everywhere

W3C standards

ECMA (JavaScript)

URI and HTTP (IETF)

1989

2000

2004

2006

Remember: Internet is like Rome

On top of Athens

With huge construction going on at all times

Planned in 2 week sprints

@bahmutov

https://www.nostarch.com/tangledweb.htm

Read this book

@bahmutov

color-pusher extension

Note 2: ng-csp

<!DOCTYPE html>
<html ng-csp ng-app="color-pusher-panel">
 ...
</html>

@bahmutov

Quick definitions

Inline script

<script>
alert('foo')
</script>
<button onclick="alert('foo')">Click</button>

@bahmutov

Quick definitions

External script

<script src="lib/foo.js"></script>
<script src="https://whatever.com/foo.js"></script>

@bahmutov

Quick definitions

Inline style

<style>
h1 { color: red }
</style>
<button style="color: red">Click</button>

@bahmutov

Quick definitions

External style

<link rel="stylesheet" href="lib/app.css">
<link rel="stylesheet" 
    href="https://whatever.com/app.css">

@bahmutov

Chrome Extension DevTools Panel runs in Content Security Policy (CSP) mode

Angular 1 uses inline styles and eval

By default extension CSP only allows external scripts and styles

@bahmutov

Angular 1 creates inline style tag

@bahmutov

<!DOCTYPE html>
<html ng-csp ng-app="color-pusher-panel">
<link href="libs/angular-csp.css" rel="stylesheet" />
<script src="libs/angular.js"></script>
</html>

Angular 1 in locked down CSP mode

Note: ng-csp mode can make $parse service 30% slower

no eval used

@bahmutov

Present day

Boston, September 2016

@bahmutov

What if a tree's password is stolen in the forest and no one hears it?

@bahmutov

You can have your

website hacked but only once.

@bahmutov

Open Web Application Security Project (OWASP)

Top 10 security threats

@bahmutov

  1. Injection
  2. Authentication / session
  3. Cross-site scripting (XSS)
  4. Direct object reference
  5. Security misconfiguration
  6. Sensitive data exposure
  7. Lack of access control
  8. Cross-site request forgery (XSRF)
  9. Insecure 3rd party code
  10. Unvalidated redirects

Top 10 attacks 2013

@bahmutov

We will look at these 3 threats

  1. Injection
  2. Authentication / session
  3. Cross-site scripting (XSS)
  4. Direct object reference
  5. Security misconfiguration
  6. Sensitive data exposure
  7. Lack of access control
  8. Cross-site request forgery (XSRF)
  9. Insecure 3rd party code
  10. Unvalidated redirects

Cross Site Request Funkery: Securing Your Angular Apps From Evil Doers

Dave Smith

Day 1

Track 2

13:50

Panel Q&A: Security and Performance

Martin Probst, Dave Smith, Tim Ruffles and John Mueller

Day 1

Track 5

11:45

Authentication / session

Who are you again?

An attacker impersonating a legitimate user can do a lot of damage

@bahmutov

Common problems

User authentication token is sent via insecure connection

Use https* for every communication link

@bahmutov

https* SSL can be tricky

@bahmutov

Old and weak SSL ciphers

# put strong ciphers first
# ban weak ciphers at the end
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:!CAMELLIA:!DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

@bahmutov

SSL part way

http://bahmutov.github.io

https://glebbahmutov.com

GitHub pages

CloudFlare

@bahmutov

Insecure auth token storage

Auth cookie not marked "httpOnly" and "secure"

cookie cannot be accessed by JavaScript code

cookie only included with https requests

+ "SameSite"

@bahmutov

General advice

Leave password management and authentication to the professionals

@bahmutov

Cross-site scripting (XSS)

Is this code or data?

Attacker's input is treated as trusted code and is executed when another user views the website

@bahmutov

Name:

John Doe

<span>{{ name }}<span>

<span>John Doe<span>

@bahmutov

Name:

<script>alert('hi')</script>

<span>{{ name }}<span>

<span><script>alert('hi')</script><span>

innerText or innerHTML?

@bahmutov

innerText or innerHTML?

Direct text or escaped?    ">" or "&gt;"

@bahmutov

innerText or innerHTML?

Dangerous, but has styling, elements, etc

Really dangerous if one user's content

can be viewed by another user

@bahmutov

My Site

My content

 

 

 

 

 

 

 

 

User comments

Ads, etc

 

 

 

 

 

 

 

<script>stealStuff()</script>

@bahmutov

Will a script embedded in the user-entered content be executed (for another user)?

NEVER EVER EVER?

@bahmutov

Not to us!

@bahmutov

Story time: how bad things happen to good people

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: 'Hello world'
  })
})
// Jade template
doctype html
html(lang="en")
  head
    title Example
  body
    h1 #{greeting}

@bahmutov

<body>
  <h1>Hello world</h1>
</body>

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: '<script>alert("1");</script>'
  })
})
// Jade template
doctype html
html(lang="en")
  head
    title Example
  body
    h1 #{greeting}

Jade variables are HTML escaped by default

@bahmutov

<body>
 <h1>&lt;script&gt;alert(&quot;1&quot;);&lt;/script&gt;</h1>
</body>

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: '{{ a + b }}'
  })
})
// Jade template
doctype html
html(lang="en")
  head
    title Example
  body
    h1 #{greeting}

@bahmutov

<body>
 <h1>{{ a + b }}</h1>
</body>

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: '{{ a + b }}'
  })
})
// Jade template
doctype html
html(lang="en")
  head
    script(src="https://code.angularjs.org/1.0.1/angular-1.0.1.js")
    title Example
  body(ng-app)
    h1 #{greeting}

Angular template

@bahmutov

<body ng-app="" class="ng-scope">
    <h1 class="ng-binding">0</h1>
</body>

@bahmutov

"AngularJS better not execute {{ <script>...</script> }} tags or I will loose it!"

@bahmutov

AngularJS does NOT execute {{ <script>...</script> }} tags

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: 'Hi {{constructor.constructor(\'alert(1)\')()}}'
  })
})
// Jade template
doctype html
html(lang="en")
  head
    script(src="https://code.angularjs.org/1.0.1/angular-1.0.1.js")
    title Example
  body(ng-app)
    h1 #{greeting}

@bahmutov

@bahmutov

Template is NOT in DOM

@bahmutov

Runs on every digest cycle

Mismatch between Jade, Angular, template strings and developer's expectations

@bahmutov

constructor.constructor

{{ eval('alert(42)') }} 
// does not work
{{ Function('alert(42)')() }} 
// does not work
// this $scope.Function...

@bahmutov

constructor.constructor

var f = {}
f.constructor 
// function Object(){[native code]}
f.constructor.constructor
// function Function(){[native code]}

Need to get "Function" somehow

{{constructor.constructor('alert(1)')()}}

@bahmutov

Will never happen to me

Angular 1.0 edition

{{constructor.constructor('alert(1)')()}}

fixed in Angular 1.2

@bahmutov

constructor.constructor

@bahmutov

Blacklisting

@bahmutov

// Sandboxing Angular Expressions
// ------------------------------
// Angular expressions are generally considered safe because these expressions only have direct
// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
// obtaining a reference to native JS functions such as the Function constructor.
//
// As an example, consider the following Angular expression:
//
//   {}.toString.constructor(alert("evil JS code"))
//
// We want to prevent this type of access. For the sake of performance, during the lexing phase we
// disallow any "dotted" access to any member named "constructor".
//
// For reflective calls (a[b]) we check that the value of the lookup is not the Function constructor
// while evaluating the expression, which is a stronger but more expensive test. Since reflective
// calls are expensive anyway, this is not such a big deal compared to static dereferencing.
//
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
// practice and therefore we are not even trying to protect against interaction with an object
// explicitly exposed in this way.
//
// A developer could foil the name check by aliasing the Function constructor under a different
// name on the scope.
//
// In general, it is not possible to access a Window object from an angular expression unless a
// window or some DOM object that has a reference to window is published onto a Scope.

Angular expressions are generally considered safe

Blacklisting and setting high security expectations are difficult to pull off

@bahmutov

{{ (_=''.sub).call.call({}[$='constructor'].getOwnPropertyDescriptor ( _.__proto__,$).value,0,'alert(1)')() }}

Will never happen to me

continued

works in Angular 1.2

@bahmutov

{{ objectPrototype = ({})[['__proto__']]; objectPrototype[['__defineSetter__']]('$parent', $root.$$postDigest); $root.$$listenerCount[['constructor']] = 0; $root.$$listeners = [].map; $root.$$listeners.indexOf = [].map.bind; functionPrototype = [].map[['__proto__']]; functionToString = functionPrototype.toString; functionPrototype.push = ({}).valueOf; functionPrototype.indexOf = [].map.bind; foo = $root.$on('constructor', null); functionPrototype.toString = $root.$new; foo(); }} {{ functionPrototype.toString = functionToString; functionPrototype.indexOf = null; functionPrototype.push = null; $root.$$listeners = {}; baz ? 0 : $root.$$postDigestQueue[0]('alert(location)')(); baz = true;'' }} 

Will never happen to me

for Angular 1.3

@bahmutov

{{ 'this is how you write a number properly. also, numbers are basically arrays.'; 0[['__proto__']].toString = [][['__proto__']].pop; 0[['__proto__']][0] = 'alert("TROLOLOLn"+document.location)'; 0[['__proto__']].length = 1; 'did you know that angularjs eval parses, then re-stringifies numbers? :)'; $root.$eval("x=0", $root); }}

Will never happen to me

for Angular 1.4

@bahmutov

Searching for sandbox escapes

@bahmutov

Sandbox escapes matter today

Udemy, Microsoft Support    1.4.9
Indiegogo                   1.4.8
Google Insights             1.3.15
JetBlue, Ford               1.2.8
Viacom                      1.0.8

@bahmutov

Vue.js

The double mustaches interprets the data as plain text, not HTML. In order to output real HTML, you will need to use triple mustaches

constructor.constructor escape works

{{
 vue.$el.insertAdjacentHTML('afterend', 
    '<script>alert(1)</script>')
}}

Aurelia

constructor.constructor escape works

@bahmutov

Angular 1.6

The Angular expression sandbox will be removed from Angular from 1.6 onwards, making the code faster, smaller and easier to maintain

Expression Sandbox Removal

@bahmutov

Angular 1.6

 the existence of the sandbox only made some developers incorrectly believe that the expression sandbox protected them [from XSS attacks]

Expression Sandbox Removal

@bahmutov

Angular 2

 Angular templates are the same as executable code

constructor.constructor escape works

@bahmutov

Lesson: you cannot sanitize your way out of <script> tags.

At least warn people!

@bahmutov

Solution: disable inline JavaScript

<body>
  <script>function willNotRun() {...}</script>
  <script src="assets/js/script.js"></script>
</body>

external

inline

@bahmutov

Attacker (probably*) cannot change JavaScript on our server

@bahmutov

Disable inline JavaScript with Content-Security-Policy header

Content-Security-Policy:
  default-src 'self'; 
  script-src 'self' ajax.googleapis.com/angularjs/1.5.8/

Wide support for CSP (browsers, web apps)

@bahmutov

GitHub headers

$ curl -I https://github.com/
HTTP/1.1 200 OK
Server: GitHub.com
Date: Thu, 21 Jan 2016 00:23:13 GMT
Content-Type: text/html; charset=utf-8
Status: 200 OK
Cache-Control: no-cache
Vary: X-PJAX
X-UA-Compatible: IE=Edge,chrome=1
Set-Cookie: logged_in=no; domain=.github.com; path=/; expires=Mon, 21 Jan 2036 00:23:13 -0000; secure; HttpOnly
X-Request-Id: 49ac67736ceec9bf127572e7c42c8235
X-Runtime: 0.007182
Content-Security-Policy: default-src *; base-uri 'self'; 
connect-src 'self' live.github.com wss://live.github.com uploads.github.com status.github.com 
api.github.com www.google-analytics.com api.braintreegateway.com client-analytics.braintreegateway.com 
github-cloud.s3.amazonaws.com; font-src assets-cdn.github.com; form-action 'self' github.com 
gist.github.com; frame-src 'self' render.githubusercontent.com gist.github.com checkout.paypal.com; 
img-src 'self' data: assets-cdn.github.com identicons.github.com www.google-analytics.com 
checkout.paypal.com collector.githubapp.com *.githubusercontent.com *.gravatar.com *.wp.com; 
media-src 'none'; object-src assets-cdn.github.com; script-src assets-cdn.github.com; 
style-src 'self' 'unsafe-inline' 'unsafe-eval' assets-cdn.github.com
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
Public-Key-Pins: max-age=300; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; pin-sha256="JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg="; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: deny
X-XSS-Protection: 1; mode=block
Vary: Accept-Encoding
X-Served-By: d0e230454cb69aa01d4f86fc3a57b17f
X-GitHub-Request-Id: AC552DBA:3AD3:FB489A:56A024F1

Disable inline JavaScript using meta tag

<meta http-equiv="Content-Security-Policy" 
    content="script-src 'self'; ....">

Wide support for CSP (browsers, web apps)

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: 'Hi {{constructor.constructor(\'alert(1)\')()}}'
  })
})
// Jade template
doctype html
html(lang="en")
  head
    meta(http-equiv="Content-Security-Policy",
      content="script-src https://code.angularjs.org 'self';")
    script(src="https://code.angularjs.org/1.0.1/angular-1.0.1.js")
    title Example
  body(ng-app)
    h1 #{greeting}

Back to our example

Added

@bahmutov

@bahmutov

// server
const app = koa()
app.use(function * () {
  this.render('index.jade', {
    greeting: 'Hi {{40 + 2}}'
  })
})

Regular Angular expressions work with CSP

@bahmutov

// server
const app = koa()
const helmet = require('koa-helmet')
app.use(helmet.csp({
  directives: {
    defaultSrc: [],
    scriptSrc: ['https://code.angularjs.org'],
    styleSrc: [],
    imgSrc: [],
    objectSrc: [],
    sandbox: ['allow-scripts']
  }
}))
// $ http localhost:4001
// HTTP/1.1 200 OK
// Content-Security-Policy: default-src; \
//   script-src https://code.angularjs.org; \
//   style-src; img-src; object-src; sandbox allow-scripts

Use response headers for CSP

app.get('/', function (req, reqs) {
  res.render('index', {
    title: 'Example',
    analyticsId: '4xy-0123456'
  });
});
head
  title #{ title }
  script.
    var analyticsId = '#{ analyticsId }';
  script.
    // use variable analyticsId

inline

Problems: Server-side templates:

Solution: render external JS

// analytics-config.js
var analyticsId = '4xy-0123456';
head
  meta(http-equiv="Content-Security-Policy",
    content="script-src 'self';")
  title #{ title }
  script(src="js/analytics-config.js")
  script(src="js/analytics.js")

@bahmutov

js-to-js middleware

// views/config.js
module.exports = {
  analyticsId: 'default-id'
}
// server.js
var app = require('express')()
var jsToJs = require('js-to-js')
app.engine('js', jsToJs)
app.get('/js/analytics-config.js', function (req, res) {
  res.setHeader('content-type', 'application/javascript');
  res.render('config.js', {
    analyticsId: '4xx-xxxxx'
  });
});

actual value

@bahmutov

js-to-js middleware

// js/config.js
var analyticsConfig = {
  "analyticsId": "4xx-xxxxx"
};

User receives

@bahmutov

js-to-js also wraps JS scripts (like Google Analytics)

@bahmutov

Pass entire config at once

var jsToJs = require('js-to-js');
app.get('/js/config.js',
  jsToJs('demoConfig', { foo: 42, bar: 21 }));

User receives

// js/config.js
var demoConfig = { foo: 42, bar: 21};

@bahmutov

With a little help, Express/Hapi/... can send ZERO inline JavaScript

@bahmutov

It is free!

CSP + Ng downsides

CSP Is Dead, Long Live CSP!

On the Insecurity of
Whitelists and the Future of Content Security Policy

@bahmutov

There are weird Angular template exploits that do not need "Function" or "eval" (bypassing CSP)

@bahmutov

The solution is a very strict user data validation against whitelist of rules

@bahmutov

CSP in WebWorkers

new Worker(URL.createObjectURL(blob))

uses CSP of the parent document

new Worker('worker.js')

uses CSP in Content-Security-Policy HTTP header

@bahmutov

CSP in WebWorkers

Bad news: this is CSP level 2 spec

NOT supported by IE / Edge

Whitelist allowed worker locations using 'child-src' CSP property

@bahmutov

XSS in ServiceWorker

If an attacker can load a malicious ServiceWorker

It will be HARD to remove it

@bahmutov

XSS into ServiceWorker via JSONP :)

@bahmutov

Insecure 3rd party code

Just add one more library

Are you using a library with a known security vulnerability?

@bahmutov

Upgrade to safe version

@bahmutov

Using old versions is a technical debt and a security risk

@bahmutov

Insecure 3rd party code

We trust this library

Can an attacker change our server / web app code?

@bahmutov

If using CDN

Be sure CDN serves what you expect using Server Resource Integrity (SRI)

<script src="https://example.com/example-framework.js"
        integrity="sha384-oqVuA..."
        crossorigin="anonymous"></script>

@bahmutov

If using NPM

A simple Node server example depends on > 100 modules

@bahmutov

NPM for server

webpack / browserify

loaders

gulp / grunt

build plugins

lint tools

Bower for web

jQuery

Angular

Bootstrap

App code

@bahmutov

NPM for server and web

webpack / browserify

jQuery

loaders

App code

Angular

gulp / grunt

Bootstrap

build plugins

lint tools

NPM checksums (like SRI) https://github.com/zaach/npm-seal

jade-angular-template-attack@1.0.0
├─┬ koa@1.2.0
│ ├─┬ accepts@1.3.2
│ │ └── negotiator@0.6.0
│ ├── co@4.6.0
│ ├─┬ composition@2.3.0
│ │ └── any-promise@1.1.0
│ ├── content-disposition@0.5.1
│ ├── content-type@1.0.1
│ ├─┬ cookies@0.6.1
...
npm list

@bahmutov

What if someone unpublished negotiator@0.6.0 and then published malicious code under negotiator@0.6.0?

negotiator@0.6.1?

@bahmutov

Kik-gate

@bahmutov

  1. Guy has bunch of NPM modules including "kik" and "left-pad"
  2. Company Kik asks him to release "kik" name from NPM registry
  3. Guy refuses
  4. Kik goes to NPM Registry Co and Co gives "kik" name to Kik
  5. Guy gets angry and removes ALL his packages from NPM registry
  6. Bunch of packages depend on "left-pad" and stop working
  7. Guy #2 grabs the released names AND PUBLISHES his code under same names and versions
  8. NPM Registry Co un-unpublishes original modules
  9. NPM Registry co changes its "unpublishing policy"

@bahmutov

You cannot* unpublish after 24 hours

Even unpublishing after 1 second is a RISK

* "fs" unpublished and restored: http://status.npmjs.org/incidents/dw8cr1lwxkcr

New NPM policy

@bahmutov

If you push sensitive info to public GitHub or publish to NPM registry - it is out

Problem: Can someone publish under YOUR name?

@bahmutov

All major NPM projects has some sensitive information leaked

NPM auth token, GitHub tokens, passwords, SSH keys, etc

@bahmutov

Solution = prevention

@bahmutov

Lessons and conclusions

@bahmutov

Match expectations

Each public project: library, framework and application should a security page

@bahmutov

If you find a security flaw

@bahmutov

Review the boundary code periodically

(your) simple code is secure

Code is rarely simple

@bahmutov

function f() {
  alert('foo')
}

@bahmutov

f``

@bahmutov

WHAT?!

@bahmutov

ES6 - WAT?!

Shai Reznik says

@bahmutov

f`` - popup
f   - popup
``
f;  - no popup
``

@bahmutov

ES6 Symbols - customize JS

  1. @@iterator to make the object iterable
  2. @@hasInstance to customize instanceof
  3. @@toPrimitive to convert an object to a primitive
  4. @@toStringTag to create the default description of an object
  5. @@species to create derived objects
  6. Create regular expression like objects: @@match, @@replace, @@search and @@split
  7. @@isConcatSpreadable to flat an object to array elements
  8. @@unscopables for properties accessibility within with

@bahmutov

Test262 is a JavaScript Sideshow

Mike Pennisi

When we started extending Test262 to cover brand new language features, we knew we were in for some surprises. Even so, we never could have anticipated the horrors we would uncover.

Test262 is a JavaScript Sideshow

Mike Pennisi

... However, these language features can sometimes interact in strange ways. ... All of it is valid JavaScript, ... None of it would be accepted in a code review.

Implement the security early

It is much harder to secure a system later

Crash reporting can warn you about CSP violations and JS probing

@bahmutov

Hacking is a question of economics

Make yourself too expensive to hack for the value you protect!

@bahmutov

Thank you

Stay safe!

@bahmutov

Day 1

Track 2

11:00

slides.com/bahmutov/angular-connect-2016