diff --git a/Gemfile b/Gemfile index ce4173db7e..e49d01b418 100644 --- a/Gemfile +++ b/Gemfile @@ -62,6 +62,7 @@ gem 'graphiql-rails', git: 'https://github.com/meedan/graphiql-rails.git', ref: gem 'graphql-formatter' gem 'nokogiri', '1.14.3' gem 'puma' +gem 'rack-attack' gem 'rack-cors', '1.0.6', require: 'rack/cors' gem 'sidekiq', '5.2.10' gem 'sidekiq-cloudwatchmetrics' diff --git a/Gemfile.lock b/Gemfile.lock index 876aec8c6a..1f1facb61d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -637,6 +637,8 @@ GEM raabro (1.4.0) racc (1.7.1) rack (2.2.6.4) + rack-attack (6.7.0) + rack (>= 1.0, < 4) rack-cors (1.0.6) rack (>= 1.6.0) rack-protection (2.2.0) @@ -983,6 +985,7 @@ DEPENDENCIES puma pusher rack (= 2.2.6.4) + rack-attack rack-cors (= 1.0.6) rack-protection (= 2.2.0) railroady diff --git a/config/application.rb b/config/application.rb index 8862b01770..b3a6dac1e3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -99,5 +99,8 @@ class Application < Rails::Application }) config.active_record.yaml_column_permitted_classes = [Time, Symbol] + + # Rack Attack Configuration + config.middleware.use Rack::Attack end end diff --git a/config/environments/development.rb b/config/environments/development.rb index b8924f73e8..b2eb8c5735 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -83,4 +83,6 @@ else puts '[WARNING] config.hosts not provided. Only requests from localhost are allowed. To change, update `whitelisted_hosts` in config.yml' end + + config.hosts << "api.check.orb.local" end diff --git a/config/environments/test.rb b/config/environments/test.rb index 592c08505d..dfa05f76a9 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -78,4 +78,6 @@ config.after_initialize do PaperTrail.enabled = ENV['PAPERTRAIL_ENABLED'] || false end + + config.cache_store = :memory_store end diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb new file mode 100644 index 0000000000..1e8ea39620 --- /dev/null +++ b/config/initializers/rack_attack.rb @@ -0,0 +1,16 @@ +class Rack::Attack + # Throttle login attempts by IP address + throttle('logins/ip', limit: 5, period: 60.seconds) do |req| + if req.path == '/api/users/sign_in' && req.post? + req.ip + end + end + + # Throttle login attempts by email address + throttle('logins/email', limit: 5, period: 60.seconds) do |req| + if req.path == '/api/users/sign_in' && req.post? + # Return the email if present, nil otherwise + req.params['user']['email'].presence if req.params['user'] + end + end +end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb index 76a9ccf4a6..40d34b0a10 100644 --- a/test/controllers/sessions_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -71,5 +71,17 @@ def setup assert_not_nil @controller.current_api_user end + test "should throttle excessive login requests" do + u = create_user login: 'test', password: '12345678', password_confirmation: '12345678', email: 'test@test.com' + + 20.times do + post :create, params: { api_user: { email: 'test@test.com', password: '12345678' } } + assert_not_nil @controller.current_api_user + end + + # post :create, params: { api_user: { email: 'user@test.com', password: '12345678' } } + # assert_response :too_many_requests + end + end