Skip to content

Verify notification signatures before actions run - #76

Merged
tim-kos merged 5 commits into
mainfrom
verify-notification-signatures
Oct 19, 2026
Merged

tim-kos merged 5 commits into
mainfrom
verify-notification-signatures

Conversation

@tim-kos

@tim-kos tim-kos commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Why

ParamsDecoder decodes params[:transloadit] without checking params[:signature], and it replaces the raw field that the signature covers. A controller receiving notify_url callbacks could therefore act on a forged Assembly status.

Change

Add Transloadit::Rails::NotificationVerifier, an opt-in concern for the controller that receives notifications:

  • It registers a before action that verifies the raw transloadit form field (request.request_parameters) with the Auth Secret from config/transloadit.yml, through Transloadit#verify_notification! from Add constant-time Assembly notification verification ruby-sdk#85.
  • Missing, malformed or forged notifications get an empty 401 before the action runs.
  • A missing Auth Secret (MissingSecret) and authenticated but invalid JSON propagate as server errors, since they aren't authentication failures.
  • The verified status is exposed as transloadit_notification, with indifferent access.
  • Because it reads the raw form field, it still works when ParamsDecoder decoded params[:transloadit] first.
  • transloadit_notification_client can be overridden when notifications are signed with another Auth Key.

Compatibility: ParamsDecoder keeps decoding without verifying. Browser form submissions carry the request's sha384 signature, not a notification signature, so verifying there would reject every upload form. The README now says its data is unauthenticated and points notification handlers to the verifier.

Dependency: the gemspec still allows transloadit >= 1.1.1, so the legacy Ruby 2.2/2.3 jobs keep resolving. With a transloadit gem older than 4.1.0, the verifier raises NotImplementedError naming the required version instead of skipping verification.

The README covers multiple Auth Keys, CSRF (skip_before_action :verify_authenticity_token, or skip_before_filter on Rails 3), retries and replays, and idempotent handling.

Testing

  • test/lib/test_notification_verifier.rb covers:
    • the registered before action
    • the verified status
    • 401 on forged and missing signatures
    • verification of raw bytes after ParamsDecoder decoded params
    • configuration errors that aren't turned into 401
    • the old-gem error
    • the default client from the Rails configuration
  • A real-HMAC test (valid sha1: signature, tampered payload, missing signature) runs whenever the resolved transloadit gem supports verification.
  • New CI job: Ruby 3.3 with Rails 8.0. It sets REQUIRE_NOTIFICATION_VERIFICATION=1, which fails if the resolved gem cannot verify, so the real-HMAC test cannot silently drop out. Until a released transloadit gem includes Add constant-time Assembly notification verification ruby-sdk#85, the job pins ruby-sdk to #85's head commit (d85aa53); switch it to the released version once that ships.
  • Ruby 2.2/2.3 on Rails 3.1–5.0 and JRuby on Rails 5.0 pass in CI.
  • Locally on Ruby 2.6, the full suite passes against railties 3.1, 3.2, 4.0, 4.1, 4.2 and 5.0 (transloadit 2.0.1, so without the real-HMAC test). Docker was unavailable for running Rails 8 locally.

Closes #73.

🤖 Generated with Claude Code

ParamsDecoder decodes params[:transloadit] without authenticating it, and
replaces the raw field that a signature covers. Add an opt-in
NotificationVerifier for notify_url controllers. It verifies the raw form
field with the configured Auth Secret through Transloadit#verify_notification!
and answers invalid notifications with 401 before any action runs.

ParamsDecoder stays decoding-only, because browser form submissions carry no
notification signature, and the README now says its data is unauthenticated.

Closes #73.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@tim-kos tim-kos self-assigned this Oct 18, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T17:19:33.732214Z 72bf3c4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Ruby 2.2 and 2.3 reject ensure inside do blocks, and Rails 5.0 on JRuby
passes quirks_mode to a json gem that no longer accepts it, so the
ordering test now stubs ParamsDecoder's decoding. Skip LFS smudging when
Bundler checks out ruby-sdk for the Rails 8.0 job.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72bf3c491c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread README.md Outdated
```ruby
class TransloaditNotificationsController < ApplicationController
include Transloadit::Rails::NotificationVerifier
skip_forgery_protection

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Show a CSRF skip that works on supported Rails releases

For applications using the Rails 3.x or 4.x versions still included in this repository's CI matrix, copying this example raises NoMethodError while defining the controller because those releases do not provide skip_forgery_protection. Consequently, users on supported versions cannot use the documented notification endpoint as written; document the appropriate version-specific skip_before_filter/skip_before_action :verify_authenticity_token alternative.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: the example now uses skip_before_action :verify_authenticity_token (Rails 4.0+) and names skip_before_filter for Rails 3.

tim-kos and others added 3 commits October 18, 2026 19:20
skip_forgery_protection only exists from Rails 5.2, so the README now uses
skip_before_action and names skip_before_filter for Rails 3. Pin the Rails
8.0 job to the ruby-sdk commit that adds verify_notification! until a
released transloadit gem includes it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@tim-kos
tim-kos merged commit 0aa02e4 into main Oct 19, 2026
14 checks passed
@tim-kos
tim-kos deleted the verify-notification-signatures branch October 19, 2026 03:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Verify notification signatures before ParamsDecoder parses callback data

1 participant