Skip to content

fix(server): prevent open redirect via forged Origin header on login#41995

Open
subrata71 wants to merge 2 commits into
releasefrom
fix/origin-header-open-redirect-app-15347
Open

fix(server): prevent open redirect via forged Origin header on login#41995
subrata71 wants to merge 2 commits into
releasefrom
fix/origin-header-open-redirect-app-15347

Conversation

@subrata71

@subrata71 subrata71 commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes an open redirect vulnerability (APP-15347) where an attacker could forge the Origin HTTP header on POST /api/v1/login to redirect authenticated users to an external domain.

Root cause: isSafeRedirectUrl() used the client-supplied Origin header as both the redirect URL builder and the trust anchor. A forged Origin: https://evil.com passed validation because the redirect host trivially matched the Origin host (evil.com == evil.com).

Fix: Introduces getTrustedOrigin() which cross-checks the Origin header against the request's Host / X-Forwarded-Host (set by the reverse proxy, not the client). All four call sites that previously read Origin directly now use this helper:

  • isSafeRedirectUrl() — no longer uses forged Origin as the validation baseline
  • fulfillRedirectUrl() — no longer prepends forged Origin to relative redirect paths
  • sanitizeRedirectUrl() — no longer uses forged Origin in the fallback URL
  • getRedirectUrl() (fork-app path) — no longer uses forged Origin for URL construction

When no Host / X-Forwarded-Host is available (unusual environments without proxy headers), Origin is still trusted to preserve backward compatibility.

Test plan

  • Added unit test testForgedOriginHeaderIsBlockedWhenHostDiffers — confirms forged Origin is rejected and sanitized URL does not contain the attacker domain
  • All 69 existing RedirectHelperOpenRedirectTest tests pass (0 failures, 0 errors)

Linear ticket

https://linear.app/appsmith/issue/APP-15347/security-low-open-redirect-via-improper-validation-of-origin-header-on

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes
    • Improved redirect security by validating request origins against the incoming host.
    • Blocked forged Origin headers from enabling unsafe redirects, including in absolute-URL handling.
    • Updated fallback behavior for rejected redirects to avoid leaking forged origin data.
  • Tests
    • Added regression coverage for forged Origin scenarios and IPv6+port origin matching.

Automation

/ok-to-test tags="@tag.All"

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/29364494919
Commit: 2a35649
Cypress dashboard.
Tags: @tag.All
Spec:


Tue, 14 Jul 2026 21:45:29 UTC

The Origin header was used as both the redirect URL builder and the
trust anchor in isSafeRedirectUrl(), allowing an attacker who forges
the Origin to bypass validation (evil.com == evil.com always passes).

Introduce getTrustedOrigin() which cross-checks the Origin header
against the request's Host / X-Forwarded-Host before trusting it.
All four call sites that read Origin now use this helper.

APP-15347
@subrata71 subrata71 requested a review from a team as a code owner July 14, 2026 19:53
@linear-code

linear-code Bot commented Jul 14, 2026

Copy link
Copy Markdown

APP-15347

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Redirect handling now validates the Origin header against the inbound request host before using it for fork redirects, URL normalization, safety checks, and sanitization. Regression tests cover forged origins and matching IPv6 origins.

Changes

Redirect hardening

Layer / File(s) Summary
Trusted-origin redirect flow
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/RedirectHelper.java, app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java
RedirectHelper validates Origin against X-Forwarded-Host or Host, uses the trusted value across redirect flows, and tests forged-origin rejection plus matching IPv6 acceptance.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: Bug

Poem

A forged origin knocks at the gate,
The trusted host decides its fate.
Safe redirects follow the chart,
Evil domains depart.
Headers align, and paths stay bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly states the main change: blocking open redirects caused by forged Origin headers on login.
Description check ✅ Passed The PR description covers the vulnerability, fix, test plan, issue link, and automation/test results, though the Communication section is missing.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/origin-header-open-redirect-app-15347

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java (1)

506-521: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test to verify IPv6 Origin validation against the request host.

The existing testIPv6LocalhostMatch does not set a Host or X-Forwarded-Host header, meaning it bypasses the new host-comparison logic in getTrustedOrigin. Consider adding a test that provides both an IPv6 Origin and an IPv6 Host header to ensure the bracket stripping logic works correctly and prevents future regressions.

🧪 Proposed test
    `@Test`
    void testIPv6OriginMatchesRequestHost() {
        HttpHeaders headers = new HttpHeaders();
        headers.setOrigin("http://[::1]:8080");
        headers.set("X-Forwarded-Host", "[::1]:8080");
        assertTrue(RedirectHelper.isSafeRedirectUrl("http://[::1]:8080/applications", headers));
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java`
around lines 506 - 521, Add a test alongside testIPv6LocalhostMatch that sets an
IPv6 Origin with brackets and port and provides the matching IPv6
X-Forwarded-Host header, then assert isSafeRedirectUrl accepts the corresponding
redirect. Ensure the test exercises host comparison and IPv6 bracket stripping.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/RedirectHelper.java`:
- Around line 138-141: Replace the removed StringUtils.isEmpty checks with
StringUtils.hasText in RedirectHelper at
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/RedirectHelper.java
lines 138-141, 202-203, and 440-441. Apply the change to trustedOrigin at the
anchor and final site, and to origin at the sibling site, preserving the
surrounding redirect logic.
- Around line 324-328: Update the origin host comparison in RedirectHelper to
remove surrounding IPv6 brackets from originHost before comparing it with
requestHost, matching the normalization used by isSafeRedirectUrl. Preserve the
existing null check and return origin only when the normalized hosts match
case-insensitively.

---

Nitpick comments:
In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java`:
- Around line 506-521: Add a test alongside testIPv6LocalhostMatch that sets an
IPv6 Origin with brackets and port and provides the matching IPv6
X-Forwarded-Host header, then assert isSafeRedirectUrl accepts the corresponding
redirect. Ensure the test exercises host comparison and IPv6 bracket stripping.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d653edc-6bf8-42f7-921a-09d38a2fffad

📥 Commits

Reviewing files that changed from the base of the PR and between 03d35e8 and 04b2ed6.

📒 Files selected for processing (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/RedirectHelper.java
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java

@subrata71 subrata71 self-assigned this Jul 14, 2026
- Strip IPv6 brackets from originHost in getTrustedOrigin() before
  comparing against requestHost (which already strips them), matching
  the normalization in isSafeRedirectUrl's fallback path.
- Replace deprecated StringUtils.isEmpty() with StringUtils.hasText()
  at the three call sites introduced in the prior commit.
- Add IPv6 test with X-Forwarded-Host to exercise the bracket
  stripping path.

APP-15347
@subrata71 subrata71 added the ok-to-test Required label for CI label Jul 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java`:
- Around line 523-531: The IPv6 redirect validation lacks a same-host,
different-port rejection case and currently may trust an Origin with an
incorrect port. Extend RedirectHelper.isSafeRedirectUrl validation and its
getTrustedOrigin flow to compare the effective origin port as well as the host,
then add a negative test using X-Forwarded-Host [::1]:8080 and Origin/redirect
port 9090 that asserts rejection while preserving the existing matching-port
acceptance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1c4c0330-3253-4ca6-a486-ce2df694b026

📥 Commits

Reviewing files that changed from the base of the PR and between 04b2ed6 and 2a35649.

📒 Files selected for processing (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/RedirectHelper.java
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/RedirectHelper.java

Comment on lines +523 to +531
@Test
void testIPv6OriginMatchesRequestHost() {
HttpHeaders headers = new HttpHeaders();
headers.setOrigin("http://[::1]:8080");
headers.set("X-Forwarded-Host", "[::1]:8080");
assertTrue(
RedirectHelper.isSafeRedirectUrl("http://[::1]:8080/applications", headers),
"IPv6 Origin matching X-Forwarded-Host must be accepted");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Add a same-host, different-port rejection test.

This only verifies the positive case. getTrustedOrigin() strips the request port and compares hosts only, then isSafeRedirectUrl() trusts the retained client-supplied Origin. With X-Forwarded-Host: [::1]:8080 and Origin: http://[::1]:9090, a redirect to port 9090 may be accepted. Add a negative test and make trusted-origin validation include effective port matching.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/RedirectHelperOpenRedirectTest.java`
around lines 523 - 531, The IPv6 redirect validation lacks a same-host,
different-port rejection case and currently may trust an Origin with an
incorrect port. Extend RedirectHelper.isSafeRedirectUrl validation and its
getTrustedOrigin flow to compare the effective origin port as well as the host,
then add a negative test using X-Forwarded-Host [::1]:8080 and Origin/redirect
port 9090 that asserts rejection while preserving the existing matching-port
acceptance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Required label for CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant