Skip to content

[Security] Slack Media Fetch: SSRF via Unvalidated Redirect Following #13145

Description

@secredoai

Severity: MEDIUM

Summary

fetchWithSlackAuth() in src/slack/monitor/media.ts validates the initial URL is a Slack domain (*.slack.com), but follows the redirect to any HTTPS URL without SSRF validation.

Affected Code

File: src/slack/monitor/media.ts:109-115

// Only follow safe protocols (we do NOT include Authorization on redirects).
if (resolvedUrl.protocol !== "https:") {
    return initialRes;
}

// Follow the redirect without the Authorization header
return fetch(resolvedUrl.toString(), { redirect: "follow" });

The redirect target is checked for HTTPS protocol but not validated against private/internal IP ranges. An open redirect on Slack infrastructure (or a compromised Slack workspace API response) could redirect to an internal IP.

Attack Scenario

  1. Attacker controls a Slack workspace or finds an open redirect on *.slack.com
  2. A file URL resolves to https://files.slack.com/... which redirects to http://169.254.169.254/latest/meta-data/ (blocked by HTTPS check) or https://internal-service.corp/...
  3. The fetch() call with redirect: "follow" will follow further redirects from the target without any SSRF check

Contrast with Safe Path

The createSlackMediaFetch() closure (same file, line 60-77) is used through fetchRemoteMedia()fetchWithSsrFGuard(), which correctly validates each redirect hop against private IP ranges with DNS pinning. Only fetchWithSlackAuth() bypasses this.

Recommended Fix

Route the redirect through fetchWithSsrFGuard instead of bare fetch(), or validate the redirect target hostname against isPrivateIpAddress before following.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity documentation

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions