From 1e7218d43923814e9f096b99542fc0865d9fc4ce Mon Sep 17 00:00:00 2001 From: Vishnu Narayanan Date: Tue, 7 Jul 2026 17:37:22 +0530 Subject: [PATCH] fix: compare HMAC identity hashes securely (#14945) This switches widget and public inbox HMAC identity checks from direct string equality to constant-time comparison with a length guard. Fixes [https://linear.app/chatwoot/issue/CW-6937](https://linear.app/chatwoot/issue/CW-6937) Fixes [https://linear.app/chatwoot/issue/CW-7464](https://linear.app/chatwoot/issue/CW-7464) Fixes [https://linear.app/chatwoot/issue/CW-7227](https://linear.app/chatwoot/issue/CW-7227) --- app/controllers/api/v1/widget/contacts_controller.rb | 6 +++++- .../public/api/v1/inboxes/contacts_controller.rb | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/widget/contacts_controller.rb b/app/controllers/api/v1/widget/contacts_controller.rb index 4094ceb24..9a7d5193a 100644 --- a/app/controllers/api/v1/widget/contacts_controller.rb +++ b/app/controllers/api/v1/widget/contacts_controller.rb @@ -73,11 +73,15 @@ class Api::V1::Widget::ContactsController < Api::V1::Widget::BaseController end def valid_hmac? - params[:identifier_hash] == OpenSSL::HMAC.hexdigest( + expected_hash = OpenSSL::HMAC.hexdigest( 'sha256', @web_widget.hmac_token, params[:identifier].to_s ) + identifier_hash = params[:identifier_hash].to_s + return false unless identifier_hash.bytesize == expected_hash.bytesize + + ActiveSupport::SecurityUtils.secure_compare(identifier_hash, expected_hash) end def permitted_params diff --git a/app/controllers/public/api/v1/inboxes/contacts_controller.rb b/app/controllers/public/api/v1/inboxes/contacts_controller.rb index 835c2596b..838b10951 100644 --- a/app/controllers/public/api/v1/inboxes/contacts_controller.rb +++ b/app/controllers/public/api/v1/inboxes/contacts_controller.rb @@ -35,11 +35,15 @@ class Public::Api::V1::Inboxes::ContactsController < Public::Api::V1::InboxesCon end def valid_hmac? - params[:identifier_hash] == OpenSSL::HMAC.hexdigest( + expected_hash = OpenSSL::HMAC.hexdigest( 'sha256', @inbox_channel.hmac_token, params[:identifier].to_s ) + identifier_hash = params[:identifier_hash].to_s + return false unless identifier_hash.bytesize == expected_hash.bytesize + + ActiveSupport::SecurityUtils.secure_compare(identifier_hash, expected_hash) end def permitted_params