fix: add widget conversation throttle by ip + website_token
Replaces the per-IP 6-per-12h throttle (disabled in cloud as too aggressive for shared NAT IPs) with a per-(IP, website_token) 30-per-minute burst throttle. Distinct widgets behind a shared NAT IP get separate buckets so the original NAT pain is preserved while a single source can no longer create unbounded conversations against one widget. Adds ENABLE_RACK_ATTACK_WIDGET_CONVERSATIONS as an independent kill switch so this throttle can be toggled without re-enabling the legacy widget throttles. RATE_LIMIT_WIDGET_CONVERSATIONS controls the per-minute cap.
This commit is contained in:
@@ -170,15 +170,25 @@ class Rack::Attack
|
||||
###-----------Widget API Throttling---------------###
|
||||
###-----------------------------------------------###
|
||||
|
||||
## Burst protection on widget conversation creation. Keyed on (IP, website_token)
|
||||
## so distinct widgets behind a shared NAT IP get separate buckets. Independent
|
||||
## kill switch so it can be toggled without touching the legacy ENABLE_RACK_ATTACK_WIDGET_API.
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_RACK_ATTACK_WIDGET_CONVERSATIONS', true))
|
||||
throttle('api/v1/widget/conversations',
|
||||
limit: ENV.fetch('RATE_LIMIT_WIDGET_CONVERSATIONS', '30').to_i,
|
||||
period: 1.minute) do |req|
|
||||
next unless req.path_without_extentions == '/api/v1/widget/conversations' && req.post?
|
||||
|
||||
token = req.params['website_token'].presence ||
|
||||
ActionDispatch::Request.new(req.env).params['website_token'].presence
|
||||
"#{req.ip}:#{token}" if token
|
||||
end
|
||||
end
|
||||
|
||||
# Rack attack on widget APIs can be disabled by setting ENABLE_RACK_ATTACK_WIDGET_API to false
|
||||
# For clients using the widgets in specific conditions like inside and iframe
|
||||
# TODO: Deprecate this feature in future after finding a better solution
|
||||
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_RACK_ATTACK_WIDGET_API', true))
|
||||
## Prevent Conversation Bombing on Widget APIs ###
|
||||
throttle('api/v1/widget/conversations', limit: 6, period: 12.hours) do |req|
|
||||
req.ip if req.path_without_extentions == '/api/v1/widget/conversations' && req.post?
|
||||
end
|
||||
|
||||
## Prevent Contact update Bombing in Widget API ###
|
||||
throttle('api/v1/widget/contacts', limit: 60, period: 1.hour) do |req|
|
||||
req.ip if req.path_without_extentions == '/api/v1/widget/contacts' && (req.patch? || req.put?)
|
||||
|
||||
Reference in New Issue
Block a user