From 538c22f61a860f688ea0e0f04e321486dedcb299 Mon Sep 17 00:00:00 2001 From: Kyle Drake Date: Fri, 21 Jun 2013 15:57:58 -0700 Subject: [PATCH 1/5] attempt to plug CSRF attack --- app.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app.rb b/app.rb index bb466ab7..c3edf165 100644 --- a/app.rb +++ b/app.rb @@ -200,7 +200,7 @@ get '/site_files/text_editor/:filename' do |filename| end post '/site_files/save/:filename' do |filename| - halt 'You are not logged in!' if current_site.nil? + require_login_ajax tmpfile = Tempfile.new 'neocities_saving_file' @@ -243,8 +243,16 @@ def dashboard_if_signed_in redirect '/dashboard' if signed_in? end +def require_login_ajax + halt 'You are not logged in!' unless signed_in? && csrf_safe +end + +def csrf_safe + (request.referer =~ /.+\.neocities\.org/i).nil? +end + def require_login - redirect '/' unless signed_in? + redirect '/' unless signed_in? && csrf_safe end def signed_in? From be9c7cad5a2319b6567c53d0e733afae69d6980f Mon Sep 17 00:00:00 2001 From: mikeycgto Date: Sat, 22 Jun 2013 12:00:43 -0400 Subject: [PATCH 2/5] Use a random token and verify it on POST requests --- app.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app.rb b/app.rb index c3edf165..3ed73b75 100644 --- a/app.rb +++ b/app.rb @@ -232,6 +232,10 @@ get '/privacy' do slim :'privacy' end +before do + redirect '/' if request.post? && !csrf_safe? +end + def sites_name_redirect path = request.path.gsub "/sites/#{params[:name]}", '' # path += "/#{params[:file]}" unless params[:file].nil? @@ -244,15 +248,19 @@ def dashboard_if_signed_in end def require_login_ajax - halt 'You are not logged in!' unless signed_in? && csrf_safe + halt 'You are not logged in!' unless signed_in? end -def csrf_safe - (request.referer =~ /.+\.neocities\.org/i).nil? +def csrf_safe? + csrf_token == params[:csrf_token] +end + +def csrf_token + session[:_csrf_token] ||= SecureRandom.base64(32) end def require_login - redirect '/' unless signed_in? && csrf_safe + redirect '/' unless signed_in? end def signed_in? From 751a6687252fd535c2e4933ccc298dc06676c6e5 Mon Sep 17 00:00:00 2001 From: mikeycgto Date: Sat, 22 Jun 2013 12:01:09 -0400 Subject: [PATCH 3/5] Add CSRF token into signin view --- views/signin.slim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/views/signin.slim b/views/signin.slim index 11e8720e..0248d542 100644 --- a/views/signin.slim +++ b/views/signin.slim @@ -5,10 +5,12 @@ .row .span12 form method="POST" action="/signin" + input name="csrf_token" type="hidden" value="#{csrf_token}" + fieldset div: input name="username" type="text" placeholder="Your username" div: input name="password" type="password" placeholder="Your password" div: button class="btn btn-large btn-success" href="#" style="margin-top: 10px" Sign in .row .span12 - a href="/new" I don't have an account yet. \ No newline at end of file + a href="/new" I don't have an account yet. From bdfaf0022aeb124ce92c6edd159a820b4e3f2ad3 Mon Sep 17 00:00:00 2001 From: mikeycgto Date: Sat, 22 Jun 2013 16:10:47 -0400 Subject: [PATCH 4/5] Add csrf_token input to dash, new, and site_files/new views --- views/dashboard.slim | 1 + views/new.slim | 7 ++++--- views/site_files/new.slim | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/views/dashboard.slim b/views/dashboard.slim index 6a59a2df..d30ab1ed 100644 --- a/views/dashboard.slim +++ b/views/dashboard.slim @@ -66,6 +66,7 @@ javascript: h4: a href="/site_files/#{current_site.username}.zip" Download Entire Site form method="POST" action="/site_files/delete" id="deleteFilenameForm" + input name="csrf_token" type="hidden" value="#{csrf_token}" input type="hidden" id="deleteFilenameInput" name="filename" .modal.hide.fade id="deleteConfirmModal" tabindex="-1" role="dialog" aria-labelledby="deleteConfirmModalLabel" aria-hidden="true" diff --git a/views/new.slim b/views/new.slim index 7355fa9b..f94b8a69 100644 --- a/views/new.slim +++ b/views/new.slim @@ -14,9 +14,10 @@ javascript: .row .span8.offset3 - form method="POST" action="/create" + form method="POST" action="/create" + input name="csrf_token" type="hidden" value="#{csrf_token}" h2 Create a new Home Page - + .row .span6 p First, enter a username. This will also be used as your site path.
Do not forget this, it will be used to sign in to and manage your home page.
It cannot contain spaces, and can only use the following characters: a-z A-Z 0-9 _ - @@ -71,4 +72,4 @@ javascript: .row style="margin-top: 10px" .span3.offset1 - input.btn.btn-success.btn-large type="submit" value="Create Home Page" \ No newline at end of file + input.btn.btn-success.btn-large type="submit" value="Create Home Page" diff --git a/views/site_files/new.slim b/views/site_files/new.slim index f53d9447..36ac46f9 100644 --- a/views/site_files/new.slim +++ b/views/site_files/new.slim @@ -13,6 +13,7 @@ .row .span12.text-center form method="POST" action="/site_files/upload" enctype="multipart/form-data" + input name="csrf_token" type="hidden" value="#{csrf_token}" h4 Select a file from your computer: h4: input type="file" name="newfile" p: input.btn.btn-success.btn-large type="submit" value="Upload File" @@ -31,4 +32,4 @@ h4 If the file already exists, it will be overwritten without warning. h4 It has to be legal to share this content in the United States. h4 It must fit into your home page space (5MB). - h4 The file uploader will automatically scrub any characters not matching: a-z A-Z 0-9 _ - . \ No newline at end of file + h4 The file uploader will automatically scrub any characters not matching: a-z A-Z 0-9 _ - . From bcf9b63fa4296059de5db12859123c55133b83e3 Mon Sep 17 00:00:00 2001 From: mikeycgto Date: Sat, 22 Jun 2013 16:53:39 -0400 Subject: [PATCH 5/5] Update csrf_safe? to check the headers. Add some JS code to insert the CSRF token into the XHR request headers. --- app.rb | 2 +- views/layout.slim | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app.rb b/app.rb index 3ed73b75..a596a913 100644 --- a/app.rb +++ b/app.rb @@ -252,7 +252,7 @@ def require_login_ajax end def csrf_safe? - csrf_token == params[:csrf_token] + csrf_token == params[:csrf_token] || csrf_token == request.env['HTTP_X_CSRF_TOKEN'] end def csrf_token diff --git a/views/layout.slim b/views/layout.slim index 7149126d..5c33cdb2 100644 --- a/views/layout.slim +++ b/views/layout.slim @@ -9,6 +9,7 @@ html link href="/css/styles.css" rel="stylesheet" meta property="og:title" content="NeoCities" meta property="og:description" content="NeoCities is the new Geocities. Create your own free home page, and do whatever you want with it." + meta name="csrf-token" content="#{csrf_token}" script src="/js/jquery.min.js" body @@ -40,6 +41,16 @@ html script src="/js/bootstrap.min.js" + javascript: + !function(){ + var csrf_token = $('meta[name="csrf-token"]').attr('content'); + + $(document).ajaxSend(function(ev, jqxhr){ + jqxhr.setRequestHeader('X-CSRF-Token', csrf_token); + }); + }(); + + javascript: (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), @@ -47,4 +58,4 @@ html })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-41925541-1', 'neocities.org'); - ga('send', 'pageview'); \ No newline at end of file + ga('send', 'pageview');