GuardAPI Logo
GuardAPI
Automated Security Protocol

How to fix Business Logic Errors
in Phoenix

Executive Summary

Business Logic Errors (BLE) in Phoenix are the silent killers of Elixir backends. While Phoenix's immutability mitigates some memory safety issues, logic flaws—like price manipulation, unauthorized state transitions, or ID-based resource harvesting—thrive in the gap between the Controller and the Ecto Context. Hackers exploit these by tampering with parameters that should be server-controlled. Stop trusting the client; start enforcing state integrity at the database layer.

The Vulnerable Pattern

VULNERABLE CODE
def create_order(conn, %{"item_id" => id, "price" => user_provided_price}) do
  # VULNERABLE: Direct trust in client-supplied price parameter
  # An attacker can intercept the request and change price from 100.00 to 0.01
  case Orders.create_order(%{item_id: id, total_price: user_provided_price, user_id: conn.assigns.user.id}) do
    {:ok, order} -> 
      render(conn, "show.json", order: order)
    {:error, _changeset} -> 
      conn |> put_status(400) |> json(%{error: "Order failed"})
  end
end

The Secure Implementation

The vulnerability stems from 'Parameter Injection'—allowing the client to define the value of a sensitive field (the price). The fix implements the 'Source of Truth' principle: the application fetches the price directly from the database based on a verified ID, completely ignoring user-supplied financial data. Furthermore, wrapping the logic in Ecto.Multi ensures that the check-and-insert operation is atomic, preventing race conditions (TOCTOU) where the product state might change between fetching and ordering.

SECURE CODE
def create_order(conn, %{"item_id" => id}) do
  # SECURE: Ignore client-side pricing. Fetch canonical price from the DB.
  # Use Ecto.Multi for atomicity to prevent race conditions.
  alias Ecto.Multi

result = Multi.new() |> Multi.run(:product, fn repo, _ -> case repo.get(Product, id) do nil -> {:error, :not_found} product -> {:ok, product} end end) |> Multi.insert(:order, fn %{product: product} -> %Order{item_id: id, total_price: product.price, user_id: conn.assigns.user.id} end) |> Repo.transaction()

case result do {:ok, %{order: order}} -> render(conn, “show.json”, order: order) {:error, _, _reason} -> conn |> put_status(422) |> json(%{error: “Invalid transaction”}) end end

System Alert • ID: 5348
Target: Phoenix API
Potential Vulnerability

Your Phoenix API might be exposed to Business Logic Errors

74% of Phoenix apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

Free Tier • No Credit Card • Instant Report

Verified by Ghost Labs Security Team

This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.