Skip to content

Commit 47cb53d

Browse files
committed
Use activesupport implementation and add tests
1 parent 455306f commit 47cb53d

File tree

3 files changed

+76
-10
lines changed

3 files changed

+76
-10
lines changed

lib/slack/events/request.rb

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22

3-
require 'openssl'
3+
require 'slack/utils/security'
44

55
module Slack
66
module Events
@@ -62,7 +62,7 @@ def valid?
6262
signature_basestring = [version, timestamp, body].join(':')
6363
hex_hash = OpenSSL::HMAC.hexdigest(digest, signing_secret, signature_basestring)
6464
computed_signature = [version, hex_hash].join('=')
65-
secure_compare(computed_signature, signature)
65+
Utils::Security.secure_compare(computed_signature, signature)
6666
end
6767

6868
# Validates the request signature and its expiration.
@@ -72,14 +72,6 @@ def verify!
7272

7373
true
7474
end
75-
76-
private
77-
78-
def secure_compare(computed_signature, signature)
79-
return false if computed_signature.bytesize != signature.bytesize
80-
81-
OpenSSL.fixed_length_secure_compare(computed_signature, signature)
82-
end
8375
end
8476
end
8577
end

lib/slack/utils/security.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# frozen_string_literal: true
2+
require 'openssl'
3+
4+
# This module is copied from activesupport
5+
# https://github.com/rails/rails/blob/3235827585d87661942c91bc81f64f56d710f0b2/activesupport/lib/active_support/security_utils.rb
6+
module Slack
7+
module Utils
8+
# rubocop:disable Naming/MethodParameterName
9+
module Security
10+
# Constant time string comparison, for fixed length strings.
11+
#
12+
# The values compared should be of fixed length, such as strings
13+
# that have already been processed by HMAC. Raises in case of length mismatch.
14+
15+
if defined?(OpenSSL.fixed_length_secure_compare)
16+
def fixed_length_secure_compare(a, b)
17+
OpenSSL.fixed_length_secure_compare(a, b)
18+
end
19+
else
20+
def fixed_length_secure_compare(a, b)
21+
raise ArgumentError, 'string length mismatch.' unless a.bytesize == b.bytesize
22+
23+
l = a.unpack "C#{a.bytesize}"
24+
25+
res = 0
26+
b.each_byte { |byte| res |= byte ^ l.shift }
27+
res.zero?
28+
end
29+
end
30+
module_function :fixed_length_secure_compare
31+
32+
# Secure string comparison for strings of variable length.
33+
#
34+
# While a timing attack would not be able to discern the content of
35+
# a secret compared via secure_compare, it is possible to determine
36+
# the secret length. This should be considered when using secure_compare
37+
# to compare weak, short secrets to user input.
38+
def secure_compare(a, b)
39+
a.bytesize == b.bytesize && fixed_length_secure_compare(a, b)
40+
end
41+
module_function :secure_compare
42+
end
43+
# rubocop:enable Naming/MethodParameterName
44+
end
45+
end

spec/slack/utils/security_spec.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
require 'slack/utils/security'
4+
5+
RSpec.describe Slack::Utils::Security do
6+
describe '.secure_compare' do
7+
it 'performs string comparison correctly' do
8+
expect(described_class.secure_compare('a', 'a')).to be true
9+
expect(described_class.secure_compare('a', 'b')).to be false
10+
end
11+
12+
it 'returns false on bytesize mismatch' do
13+
expect(described_class.secure_compare('a', "\u{ff41}")).to be false
14+
end
15+
end
16+
17+
describe '.fixed_length_secure_compare' do
18+
it 'performs string comparison correctly' do
19+
expect(described_class.fixed_length_secure_compare('a', 'a')).to be true
20+
expect(described_class.fixed_length_secure_compare('a', 'b')).to be false
21+
end
22+
23+
it 'raises ArgumentError on length mismatch' do
24+
expect do
25+
described_class.fixed_length_secure_compare('a', 'ab')
26+
end.to raise_error(ArgumentError, 'inputs must be of equal length')
27+
end
28+
end
29+
end

0 commit comments

Comments
 (0)