diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index 902feef683..b61a569860 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -64,6 +64,9 @@ module SignatureVerification return (@signed_request_actor = actor) if signed_request.verified?(actor) fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri}" + rescue Mastodon::MalformedHeaderError => e + @signature_verification_failure_code = 400 + fail_with! e.message rescue Mastodon::SignatureVerificationError => e fail_with! e.message rescue *Mastodon::HTTP_CONNECTION_ERRORS => e diff --git a/app/lib/signed_request.rb b/app/lib/signed_request.rb index 066411c680..0ee47ddae1 100644 --- a/app/lib/signed_request.rb +++ b/app/lib/signed_request.rb @@ -196,6 +196,8 @@ class SignedRequest return if body_digest == received_digest raise Mastodon::SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{received_digest}" + rescue Starry::ParseError + raise Mastodon::MalformedHeaderError, 'Content-Digest could not be parsed. It does not contain a valid RFC8941 dictionary.' end def created_time diff --git a/lib/exceptions.rb b/lib/exceptions.rb index 93fcc38dce..c8c8198382 100644 --- a/lib/exceptions.rb +++ b/lib/exceptions.rb @@ -13,6 +13,7 @@ module Mastodon class SyntaxError < Error; end class InvalidParameterError < Error; end class SignatureVerificationError < Error; end + class MalformedHeaderError < Error; end class UnexpectedResponseError < Error attr_reader :response diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb index edcc2b673d..eccb2babc9 100644 --- a/spec/requests/signature_verification_spec.rb +++ b/spec/requests/signature_verification_spec.rb @@ -621,6 +621,30 @@ RSpec.describe 'signature verification concern' do ) end end + + context 'with a malformed `Content-Digest` header' do + let(:digest_header) { 'SHA-256=:ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=:' } + let(:signature_input) do + 'sig1=("@method" "@target-uri" "content-digest");created=1703066400;keyid="https://remote.domain/users/bob#main-key"' + end + let(:signature_header) do + 'sig1=:aXua24cIlBi8akNXg/Vc5pU8fNGXo0f4U2qQk42iWoIaCcH3G+z2edPMQTNM/aZmD0bULqvb/yi6ZXgRls1ereq3OqnvA4JBLKx15O/jLayS/FhR4d/2vaeXuBOYXM7EGXItKkFxEXn3J+FCQPb5wY31GlbljrESjsiZ6gtrSmwryBluQCwMJ59LACzocxbWo42Kv3cpSig2aCu9CYXKC4sCH3eSKjwPtjdlpmX1VkYX5ge+JaZMn7A218ZgZOc9xpPawESOuIF9axcKW5PDEhOwmswFd2G65c8H9kJY6zEnqbArP9lRQMmjuAb011NILClaaRZOOupz2HZUdm+91Q==:' # rubocop:disable Layout/LineLength + end + + it 'returns `400` (Bad Request)', :aggregate_failures do + post '/activitypub/signature_required', params: 'Hello world', headers: { + 'Host' => 'www.example.com', + 'Content-Digest' => digest_header, + 'Signature-Input' => signature_input, + 'Signature' => signature_header, + } + + expect(response).to have_http_status(400) + expect(response.parsed_body).to match( + error: 'Content-Digest could not be parsed. It does not contain a valid RFC8941 dictionary.' + ) + end + end end context 'with an inaccessible key' do