Getting Single Page Application Security Right
Getting Single Page Application Security Right
iMinds-DistriNet, KU Leuven
Description: Cooking
Parse request
Deadline: 25/02/2015
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
http://arstechnica.com/security/2015/04/no-joke-googles-april-fools-prank-inadvertently-broke-sites-security/
http://arstechnica.com/security/2015/04/match-coms-http-only-login-page-puts-millions-of-passwords-at-risk/
20
#Devoxx #SecureSPA @PhilippeDeRyck
Presentation Overview
Session Management
Go to some-shop.com 2
7ad3e9f78bc808
Hello stranger Logged_in: false
true
Login as NotPhilippe User: NotPhilippe
Hello NotPhilippe Admin: false
Cookie: sessionid=1
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
http://news.softpedia.com/news/CSRF-Vulnerability-in-eBay-Allows-Hackers-to-Hijack-User-Accounts-Video-383316.shtml
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/
First request
Set-Cookie: session=
Set-Cookie: CSRF-Token=123
TRANSPARENT TOKENS
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
service.response = function(response) {
var token = response.headers("Authorization");
if(token) {
console.log("Storing the authorization token");
$localStorage.authorizationToken = "Bearer " + token;
}
return response;
}
}
Show Reviews
Reviews page
reviews
Add review
Thanks for the review!
http://blogs.apache.org/infra/entry/apache_org_04_09_2010
http://blogs.apache.org/infra/entry/apache_org_04_09_2010
Show Reviews
Reviews page
reviews
Add review
Thanks for the review!
https://www.xssposed.org/incidents/top/
Description: Cooking
Parse request
Deadline: 25/02/2015
<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>
KNOCKOUT.JS EXAMPLE
<script src=knockout-2.3.0.js"></script>
<div data-bind="x:alert(1)" />
<script>
ko.applyBindings();
</script>
https://code.google.com/p/mustache-security/
https://code.google.com/p/mustache-security/
<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/
<script src=angular1.1.5.min.js"></script>
<div class="ng-app">
{{constructor.constructor('alert(1)')()}}
</div>
https://code.google.com/p/mustache-security/
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
<textarea ng-model=x></textarea>
<div>{{x}}</div>
USER INPUT
RENDERED HTML
<textarea ng-model=x></textarea>
<div ng-bind=x></div>
USER INPUT
RENDERED HTML
<textarea ng-model=x></textarea>
<div ng-bind-html=x></div>
USER INPUT
RENDERED HTML
http://stackoverflow.com/questions/9381926/angularjs-insert-html-into-view
<textarea ng-model=x></textarea>
<div ng-bind-html=x | sanitize></div>
USER INPUT
RENDERED HTML
<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
<div ng-bind=x">
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1) />
</div>
<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
http://stackoverflow.com/questions/9381926/angularjs-insert-html-into-view
<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);
}
}]);
USER INPUT
RENDERED HTML
http://stackoverflow.com/questions/11450602/show-property-which-includes-html-tags
USER INPUT
RENDERED HTML
USER INPUT
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1)/>
RENDERED HTML
USER INPUT
<img src=http://some-shop.com/coolcar.png"
onerror=alert(1)/>
RENDERED HTML
4. Four new browser communication mechanisms, and how they affect your app
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
script-src self
https://cdnjs.cloudflare.com;
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
http://caniuse.com/#search=csp
default-src
Specifies the default sources of all content
Can be overwritten with more specific directives for each type
connect-src, form-action
Specifices the destination of these actions
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;
<script>
function run() { EXTERNALIZED SCRIPT
alert(booh!');
} <script src="myscript.js"></script>
</script> <a href="#" id="myLink">...</a>
http://caniuse.com/#search=csp
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
https://code.google.com/p/mustache-security/
EXAMPLE POLICY
Content-Security-Policy:
default-src 'self';
report-uri http://some-shop.com/csp-report.cgi
Load page
www.example.com www.websec.be
www.example.com
Load page
Load script
from websec.be
www.websec.be
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
www.websec.be
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.websec.be/profile', false);
xhr.send();
www.example.com
Load page
Access-Control-Allow-Origin:
http://www.example.com www.websec.be
www.example.com
Load page
Access-Control-Allow-Origin:
http://www.example.com www.websec.be
Access-Control-Allow-Credentials: true
www.example.com
Load page
www.websec.be
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
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
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: *
Get image
<canvas>
</canvas>
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;
<canvas>
</canvas>
Access-Control-Allow-Origin:
http://www.example.com
https://goo.gl/17hVTa