From b381cbf06642518546fc3606953afb5db0656efa Mon Sep 17 00:00:00 2001 From: Markus Graf Date: Tue, 6 Oct 2015 09:16:07 +0200 Subject: [PATCH] protect against bots with timeouts --- Gemfile | 2 ++ Gemfile.lock | 10 ++++++++++ app/assets/stylesheets/application.sass | 3 +++ app/controllers/application_controller.rb | 14 ++++++++++++++ app/controllers/supporters_controller.rb | 6 ++++-- app/views/layouts/application.html.slim | 2 ++ app/views/shared/_flashes.slim | 5 +++++ config/locales/de.yml | 2 ++ spec/features/support_spec.rb | 14 ++++++++++++++ 9 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 app/views/shared/_flashes.slim diff --git a/Gemfile b/Gemfile index d21f266..7711cf9 100644 --- a/Gemfile +++ b/Gemfile @@ -43,6 +43,8 @@ group :development, :test do gem 'byebug' gem 'mongoid-rspec', '3.0.0' gem 'rspec-rails', '~> 3.0' + + gem 'pry-rails' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index ae3bcf3..a4a73c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,6 +54,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + coderay (1.1.0) coffee-rails (4.1.0) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) @@ -80,6 +81,7 @@ GEM nokogiri (>= 1.5.9) mail (2.6.3) mime-types (>= 1.16, < 3) + method_source (0.8.2) mime-types (2.6.2) mini_portile (0.6.2) minitest (5.8.1) @@ -98,6 +100,12 @@ GEM nokogiri (1.6.6.2) mini_portile (~> 0.6.0) origin (2.1.1) + pry (0.10.2) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-rails (0.3.4) + pry (>= 0.9.10) puma (2.14.0) rack (1.6.4) rack-test (0.6.3) @@ -174,6 +182,7 @@ GEM activesupport (>= 3.1, < 5.0) railties (>= 3.1, < 5.0) slim (~> 3.0) + slop (3.6.0) spring (1.4.0) sprockets (3.3.5) rack (> 1, < 3) @@ -212,6 +221,7 @@ DEPENDENCIES jquery-rails mongoid (~> 5.0.0) mongoid-rspec (= 3.0.0) + pry-rails puma rails (= 4.2.4) rails-i18n diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass index c12d7f0..4c5f3a7 100644 --- a/app/assets/stylesheets/application.sass +++ b/app/assets/stylesheets/application.sass @@ -15,3 +15,6 @@ */ @import "bootstrap-sprockets" @import "bootstrap" + +body + padding-top: 20px diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..ba34add 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,18 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + + INPUT_TIMEOUT = 2.seconds # We estimate that a user needs more then x seconds to enter some informations + + # calculate how long a user needed for a form input, ussually we just + def input_to_fast? + fail 'session[:form_timestamp] not set' unless session[:form_timestamp] + duration = Time.now - session[:form_timestamp].to_time + duration < INPUT_TIMEOUT + end + + # Set the current timestamp that marks entering a form + def set_form_timestamp + session[:form_timestamp] = Time.now + end end diff --git a/app/controllers/supporters_controller.rb b/app/controllers/supporters_controller.rb index ab481ac..58e6bfb 100644 --- a/app/controllers/supporters_controller.rb +++ b/app/controllers/supporters_controller.rb @@ -1,14 +1,16 @@ class SupportersController < ApplicationController + before_filter :set_form_timestamp, only: :new + def new @supporter = Supporter.new end def create @supporter = Supporter.new supporter_form_params - if @supporter.save + if !input_to_fast? && @supporter.save redirect_to thanks_path else - flash.now[:danger] = t 'shared.actions.failed' + flash.now[:danger] = t '.timeout' if input_to_fast? render :new end end diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 97eff1b..a049302 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -5,5 +5,7 @@ html = javascript_include_tag 'application', 'data-turbolinks-track' => true = csrf_meta_tags body + = console if Rails.env.development? .container + = render 'shared/flashes' = yield diff --git a/app/views/shared/_flashes.slim b/app/views/shared/_flashes.slim new file mode 100644 index 0000000..07724d9 --- /dev/null +++ b/app/views/shared/_flashes.slim @@ -0,0 +1,5 @@ +- if flash.any? + #flashes + - flash.each do |type, message| + div class="alert alert-#{type} fade in" + = message diff --git a/config/locales/de.yml b/config/locales/de.yml index eb37365..e8e45f4 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -12,6 +12,8 @@ de: form: title: Ja, ich will helfen Cannabis in der Schweiz endlich zu legalisieren submit: Unterstützung zusichern + create: + timeout: Ihre Eingabe war zu schnell. pages: thanks: diff --git a/spec/features/support_spec.rb b/spec/features/support_spec.rb index 71c258f..541465f 100644 --- a/spec/features/support_spec.rb +++ b/spec/features/support_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' feature 'Support announcement' do scenario 'User announces support' do + allow_any_instance_of(SupportersController).to receive(:input_to_fast?).and_return(false) visit root_path fill_in 'Vorname', with: 'Christoph' @@ -18,4 +19,17 @@ feature 'Support announcement' do expect { click_button 'Unterstützung zusichern' }.to change { Supporter.count }.by(1) end + + scenario 'A bot tries to enter data' do + visit root_path + + click_button 'Unterstützung zusichern' + + expect(page).to have_content 'Ihre Eingabe war zu schnell.' + + # Test twice just to be sure that it's not become an update action + click_button 'Unterstützung zusichern' + + expect(page).to have_content 'Ihre Eingabe war zu schnell.' + end end