## Description * Added Meta webhook HMAC validation in meta_token_verify_concern.rb. * Wired it into instagram_controller.rb and whatsapp_controller.rb. * WhatsApp now verifies X-Hub-Signature-256 with WHATSAPP_APP_SECRET. * Instagram now verifies with either FB_APP_SECRET or INSTAGRAM_APP_SECRET. * Updated request specs so missing/invalid signatures return 401 and valid signatures still enqueue jobs. Fixes # (issue): [CW-6786](https://linear.app/chatwoot/issue/CW-6786/ghsa-7rw7-pc8v-mrr3-unauthenticated-message-injection-via-missing) ## Type of change Please delete options that are not relevant. - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? * Updated the controller specs and ran them successfully. * The original issue is no longer reproducible. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
72 lines
2.1 KiB
Ruby
72 lines
2.1 KiB
Ruby
# services from Meta (Prev: Facebook) needs a token verification step for webhook subscriptions,
|
|
# This concern handles the token verification step.
|
|
|
|
module MetaTokenVerifyConcern
|
|
CHANNEL_APP_SECRET_KEYS = %w[app_secret app_secret_key client_secret api_secret].freeze
|
|
META_SIGNATURE_HEADER = 'X-Hub-Signature-256'.freeze
|
|
META_SIGNATURE_PREFIX = 'sha256='.freeze
|
|
|
|
def verify
|
|
service = is_a?(Webhooks::WhatsappController) ? 'whatsapp' : 'instagram'
|
|
if valid_token?(params['hub.verify_token'])
|
|
Rails.logger.info("#{service.capitalize} webhook verified")
|
|
render json: params['hub.challenge']
|
|
else
|
|
render status: :unauthorized, json: { error: 'Error; wrong verify token' }
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def verify_meta_signature!
|
|
return unless meta_signature_verification_required?
|
|
return if valid_meta_signature?
|
|
|
|
head :unauthorized
|
|
end
|
|
|
|
def valid_meta_signature?
|
|
signature = request.headers[META_SIGNATURE_HEADER]
|
|
return false unless signature&.start_with?(META_SIGNATURE_PREFIX)
|
|
|
|
meta_app_secrets.any? do |secret|
|
|
next false if secret.blank?
|
|
|
|
expected_signature = "#{META_SIGNATURE_PREFIX}#{OpenSSL::HMAC.hexdigest('SHA256', secret, meta_request_body)}"
|
|
ActiveSupport::SecurityUtils.secure_compare(expected_signature, signature)
|
|
end
|
|
end
|
|
|
|
def meta_request_body
|
|
@meta_request_body ||= request.raw_post
|
|
end
|
|
|
|
def meta_app_secrets
|
|
raise 'Overwrite this method in your controller'
|
|
end
|
|
|
|
def meta_signature_verification_required?
|
|
true
|
|
end
|
|
|
|
def channel_meta_app_secrets(channel)
|
|
return [] if channel.blank?
|
|
|
|
secrets = []
|
|
secrets << channel.app_secret if channel.respond_to?(:app_secret)
|
|
secrets.concat(provider_config_meta_app_secrets(channel))
|
|
secrets.compact_blank.uniq
|
|
end
|
|
|
|
def provider_config_meta_app_secrets(channel)
|
|
return [] unless channel.respond_to?(:provider_config)
|
|
|
|
provider_config = channel.provider_config.to_h.with_indifferent_access
|
|
CHANNEL_APP_SECRET_KEYS.filter_map { |key| provider_config[key].presence }
|
|
end
|
|
|
|
def valid_token?(_token)
|
|
raise 'Overwrite this method your controller'
|
|
end
|
|
end
|