Skip to content

[Security] Bulk send does not verify phone number ownership (missing user_id filter) #42

@eltociear

Description

@eltociear

Summary

src/app/api/messages/bulk-send/route.ts fetches the phone number without filtering by user_id. An authenticated user can send bulk SMS from any phone number in the system, not just their own.

Location

// src/app/api/messages/bulk-send/route.ts
const { data: phoneNumber, error: phoneError } = await supabase
  .from("phone_numbers")
  .select("*")
  .eq("id", phoneNumberId)
  .single();  // No .eq("user_id", user.id) filter!

Compare with campaigns route which correctly verifies ownership:

// src/app/api/campaigns/route.ts (correct)
const { data: phoneNumber } = await supabase
  .from("phone_numbers")
  .select("id")
  .eq("id", phone_number_id)
  .eq("user_id", user.id)  // Ownership verified
  .single();

Impact

  • Any authenticated user can send bulk SMS from another user's phone number
  • The messages would be billed to the phone number owner's provider account
  • Could be used for impersonation or phishing
  • Note: single send (/api/messages/send/route.ts) has the same issue — no user_id filter on phone_numbers select

Suggested Fix

Add user_id filter:

const { data: phoneNumber, error: phoneError } = await supabase
  .from("phone_numbers")
  .select("*")
  .eq("id", phoneNumberId)
  .eq("user_id", user.id)  // Add ownership check
  .single();

Severity

High — Cross-user resource access, potential impersonation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions