Securing JavaScript Web Applications

Securing JavaScript Web Applications

A5b424d4146905962a24acd6815aeb84?s=128

Stephen Thomas

June 17, 2013
Tweet

Transcript

  1. stephen@sathomas.me icon-comment icon-comment-alt icon-comments icon-comments-alt icon-credit-card icon-dashboard icon-download icon-download-alt icon-edit

    icon-envelope icon-envelope-alt icon-key icon-leaf icon-legal icon-lemon icon-lock icon-unlock icon-magic icon-magnet icon-map-marker icon-minus icon-minus-sign icon-resize-horiz icon-resize-verti icon-retweet icon-road icon-rss icon-screenshot icon-search icon-share icon-share-alt icon-shopping-c http://speakerdeck.com/sathomas icon-paper-clip icon-indent-right icon-tab Directional Icons icon-arrow-down icon-arrow-left icon-arrow-right icon-arrow-up icon-chevron-down icon-circle-arrow-down icon-circle-arrow-left icon-circle-arrow-right icon-circle-arrow-up icon-chevron-left icon-car icon-car icon-car icon-car icon-ch Video Player Icons icon-play-circle icon-play icon-step-backward icon-fast-backward icon-fas icon-ste Securing JavaScript Web Applications Stephen Thomas
  2. Most Common Attacks on Web Applications 1. Cross Site Scripting

    (XSS) 2. Cross Site Request Forgery (CSRF) (both are based on JavaScript) Source: Computer Weekly (quoting FireHost)
  3. Cross Site Scripting (What you think) your.web.app.com victim great content

  4. your.web.app.com victim great content + attack script evil.web.site.com 1 2

    Cross Site Scripting (Reality)
  5. The Vulnerability: User Supplied Content I like your post. Comment:

    Submit
  6. Naively Collect Content var NewCommentView = Backbone.View.extend({ ... events: {

    "click button": "save" }, save: function(ev){ ev.preventDefault(); var newComment = this.$('input[name=comment]').val(); this.model.save({newComment: newComment}); }, ... });
  7. Naively Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%=

    comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  8. The Attack I like your post. <script src="http://evil.web.site.com.badscript.js"> </script> Comment:

    Submit
  9. Naively Collect Content (including the script tag) var NewCommentView =

    Backbone.View.extend({ ... events: { "click button": "save" }, save: function(ev){ ev.preventDefault(); var newComment = this.$('input[name=comment]').val(); this.model.save({newComment: newComment}); }, ... });
  10. Deliver the Content your.web.app.com victim great content + attack script

    1
  11. Naively Render Content (including the script tag) var CommentView =

    Backbone.View.extend({ ... template: _.template('<p><%= comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  12. your.web.app.com victim great content + attack script evil.web.site.com 1 2

    Cross Site Scripting
  13. The Fix: Turn Bad Content I like your post! <script

    src="http://evil.web.site.com/badscript.js"> </script>
  14. The Fix: Into Safe Content I like your post! &lt;script

    src=&quot;http://evil.web.site.com/ badscript.js&quot;&gt; &lt;/script&gt;
  15. Escape on Input var NewCommentView = Backbone.View.extend({ ... events: {

    "click button": "save" }, save: function(ev){ ev.preventDefault(); var newComment = _.escape( this.$('input[name=comment]').val() ); this.model.save({newComment: newComment}); }, ... });
  16. Escape on Input var NewCommentView = Backbone.View.extend({ ... events: {

    "click button": "save" }, save: function(ev){ ev.preventDefault(); var newComment = _.escape( this.$('input[name=comment]').val() ); this.model.save({newComment: newComment}); }, ... });
  17. Naively Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%=

    comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  18. Naively Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%=

    comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  19. Safely Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%=

    comment %></p>'), render: function(){ this.$el.text(this.template(this.model.toJSON())); }, ... });
  20. Naively Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%=

    comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  21. Naively Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%=

    comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  22. Safely Render Content var CommentView = Backbone.View.extend({ ... template: _.template('<p><%-

    comment %></p>'), render: function(){ this.$el.html(this.template(this.model.toJSON())); }, ... });
  23. Cross Site Request Forgery (What you think) your.web.app.com suspicious.web.site.com

  24. your.web.app.com suspicious.web.site.com Cross Site Request Forgery (Reality)

  25. • JavaScript code loaded from suspicious.web.site.com can issue AJAX requests

    to your.web.app.com • If the user has a tab or window open to your.web.app.com, those AJAX requests will include your session authorization cookies. • Your server will think they’re legitimate requests and respond accordingly. The Vulnerability: AJAX Requests
  26. The Fix: Synchronization Tokens <?php setcookie("X-CSRF-Token", sha1(rand())); echo file_get_contents( "index.html"

    ); ?> • Server sends an additional cookie that is set to a random value.
  27. The Fix: Synchronization Tokens var csrf = readCookie("X-CSRF-Token"); if (csrf)

    { myApp.originalSync = Backbone.sync; Backbone.sync = function(method, model, options) { options || (options = {}); options.headers = { "X-CSRF-Token": csrf }; return myApp.originalSync(method,model,options); }; }
  28. The Fix: Synchronization Tokens var csrf = readCookie("X-CSRF-Token"); if (csrf)

    { myApp.originalSync = Backbone.sync; Backbone.sync = function(method, model, options) { options || (options = {}); options.headers = { "X-CSRF-Token": csrf }; return myApp.originalSync(method,model,options); }; } • Client reads the value of this special cookie.
  29. The Fix: Synchronization Tokens var csrf = readCookie("X-CSRF-Token"); if (csrf)

    { myApp.originalSync = Backbone.sync; Backbone.sync = function(method, model, options) { options || (options = {}); options.headers = { "X-CSRF-Token": csrf }; return myApp.originalSync(method,model,options); }; } • Client returns the value as a custom HTTP header.
  30. The Fix: Synchronization Tokens <?php if ($_SERVER['HTTP_X_CSRF_TOKEN'] != $_COOKIE['X-CSRF-Token']) {

    header('Status: 403 Forbidden'); exit(); } /* process request normally */ ?> • Server makes sure custom HTTP header is present and matches the cookie.
  31. Why Do Synchronization Tokens Work? Even though AJAX requests that

    suspicious.web.site.com makes to your.web.app.com will include your cookies, Javascript code from suspicious.web.site.com cannot read the values of those cookies. It cannot, therefore, set the custom HTTP header values for which your server is checking.
  32. Single Page Web Apps • Don’t re-learn old lessons the

    hard way. • Protect against JavaScript-based attacks such as XSS and CSRF. • Oh yeah, protect against other attacks (SQL Injection) also.