diff --git a/lib/line/bot/v2/webhook_parser.rb b/lib/line/bot/v2/webhook_parser.rb index 4587d506..64238596 100644 --- a/lib/line/bot/v2/webhook_parser.rb +++ b/lib/line/bot/v2/webhook_parser.rb @@ -41,7 +41,23 @@ def parse(body, signature) def verify_signature(body:, signature:) hash = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), @channel_secret, body) - signature == Base64.strict_encode64(hash.to_s) + expected = Base64.strict_encode64(hash) + variable_secure_compare(signature, expected) + end + + # To avoid timing attacks + def variable_secure_compare(a, b) + secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) + end + + def secure_compare(a, b) + return false unless a.bytesize == b.bytesize + + l = a.unpack("C#{a.bytesize}") + + res = 0 + b.each_byte { |byte| res |= byte ^ l.shift } + res == 0 end def create_instance(klass, attributes) diff --git a/sig/line/bot/v2/webhook_parser.rbs b/sig/line/bot/v2/webhook_parser.rbs index 88ec56f1..658d3f5c 100644 --- a/sig/line/bot/v2/webhook_parser.rbs +++ b/sig/line/bot/v2/webhook_parser.rbs @@ -8,6 +8,10 @@ module Line private + def variable_secure_compare: (a: String, b: String) -> bool + + def secure_compare: (a: String, b: String) -> bool + def verify_signature: (body: String, signature: String) -> bool def create_instance: (untyped klass, Hash[Symbol, untyped] attributes) -> untyped