Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ may have unexpected consequences if applied to other TIMDEX UI apps.
- `RESULTS_THROTTLE_PERIOD` - time in minutes for `/results` and `/record` endpoint throttle (default 1 minute). Throttled requests are redirected to Turnstile for verification.
- `TURNSTILE_GRACE_PERIOD` - time in minutes that an IP is whitelisted from throttling after successfully passing Turnstile verification (default 15 minutes). This prevents users from being re-challenged repeatedly.
- `RACK_ATTACK_VERBOSE_LOGGING` - Set to `false` to disable detailed Rack::Attack throttle event logging to stdout (default `true`). Useful for reducing test output noise while still maintaining throttle functionality.
- `RACK_ATTACK_REDIS_URL` - Optional. If set, uses a dedicated Redis cache store for Rack Attack throttle counters, isolating throttle traffic from application cache. On Heroku, provision with: `heroku addons:create heroku-redis:mini --as RACK_ATTACK_REDIS` (Heroku automatically appends `_URL` to create this env var). If unset, falls back to `Rails.cache`.
- `BLOCKED_USER_AGENTS` - comma-separated list of user agent strings to hard-block with 403 Forbidden responses (bypasses throttling; much cheaper). Default blocks `Sogou web spider` which was responsible for 76.94k spoofed attack requests from non-Chinese IPs. Example: `"Sogou web spider,BadBot/2.0"`
- `REDIRECT_REQUESTS_PER_PERIOD`- number of requests that can be made that the query string starts with our legacy redirect parameter to throttle per `REQUEST_PERIOD`
- `REDIRECT_REQUEST_PERIOD`- time in minutes used along with `REDIRECT_REQUESTS_PER_PERIOD`
Expand Down
28 changes: 28 additions & 0 deletions config/initializers/rack_attack.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Rack::Attack
Rails.logger.info "Rack Attack initializer loading"

# List of throttles that honor the Turnstile grace cookie. These throttles will redirect to the Turnstile challenge
# page instead of returning 429, allowing verified users to continue during the grace period without increasing
# limits for unverified traffic.
Expand Down Expand Up @@ -53,6 +55,32 @@ def self.valid_format_token?(req)

# Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new

# If we have a dedicated Rack Attack cache, use it. Separate cache in prod allows us to ensure that throttling does
# not interfere with our core result caching.
#
# To provision a dedicated Redis for Rack Attack on Heroku, use:
# heroku addons:create heroku-redis:mini --as RACK_ATTACK_REDIS
# Note: Heroku automatically appends "_URL" to the addon name, creating the RACK_ATTACK_REDIS_URL env var.
rack_attack_redis_url = ENV.fetch('RACK_ATTACK_REDIS_URL', '').presence
if rack_attack_redis_url
store = ActiveSupport::Cache::RedisCacheStore.new(
url: rack_attack_redis_url,
ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
)
Rack::Attack.cache.store = store
# Verify the cache can be written to
begin
store.write('rack_attack_test', 'ok', expires_in: 1.second)
Rails.logger.info("Rack Attack Redis cache initialized successfully")
rescue => e
Rails.logger.error("Rack Attack Redis cache initialization failed: #{e.class} - #{e.message}")
end
# Otherwise fall back to the Rails.cache. Not recommended in production, but fine everywhere else
else
Rack::Attack.cache.store = Rails.cache
Rails.logger.info("Rack Attack using Rails.cache (consider setting RACK_ATTACK_REDIS_URL in production)")
end

### Safelist MIT IP addresses
# http://kb.mit.edu/confluence/x/F4DCAg
# Main IP range (includes campus, NAT pool, and VPNs)
Expand Down