跳到正文
DEV Community· mech.app·· 4 小時前AI 評分48

Anthropic 100 倍超額收費漏洞:國際化失誤揭示 Agent 付款流程的問題

Anthropic's 100 Overcharge Bug: What Internationalization Failures Reveal About Agent Payment Pipelines

AI 導讀

Anthropic 的 API 額度充值流程把哥斯達黎加開發者輸入的「100,00」誤讀為 10000,令原打算充值 $100 的開發者被收取 $10,000。交易沒有確認畫面,伺服器端亦沒有金額驗證。建議採用分層防護:用戶端按語系解析並顯示格式化金額,由 API gateway 正規化輸入,再由伺服器驗證金額上限。

正文

A Costa Rican developer tried to load $100 in API credits on Anthropic's console. The platform charged $10,000 instead. The root cause was a missing decimal separator validation layer that silently interpreted "100,00" (Spanish locale format) as 10000. No confirmation modal. No server-side normalization. Just an instant charge 100 times larger than intended.

This is not a UX annoyance. It is a structural failure in the payment authorization boundary that sits between agent consumption metering and financial settlement. Every agent platform that bills for API calls faces this exact problem: how do you validate currency input across locales without trusting client-side formatting?

The Payment Pipeline Gap

Agent platforms have two distinct flows that must intersect cleanly:

  1. Consumption metering: Agents call APIs, rack up token costs, trigger usage events.
  2. Credit loading: Users authorize payments to fund future agent activity.

The bug occurred in the credit loading flow. Here is what failed:

  • Client-side input: User typed "100,00" (comma as decimal separator, standard in Spanish-speaking regions).
  • Missing sanitization: Platform stripped the comma without locale-aware parsing.
  • Silent interpretation: Backend read "10000" as the intended amount.
  • No confirmation screen: Payment processor executed immediately.
  • No server-side validation: No range check, no locale normalization, no pre-authorization review.

The result: a $10,000 charge with no opportunity to cancel.

Where Locale Normalization Should Happen

There are three places to handle locale-aware currency input. Each has trade-offs.

Layer Approach Failure Mode
Client validation Parse input using user's locale, display formatted preview User can bypass with dev tools; locale detection can be wrong
API gateway Reject ambiguous formats, require ISO 4217 currency codes and decimal points Poor UX; forces users to change system settings
Payment processor integration Normalize at the last mile before Stripe/Adyen call Too late to show confirmation; errors surface after user intent

The correct answer is layered defense:

  1. Client validates and shows a formatted preview ("You are about to load $100.00 USD").
  2. API gateway normalizes to a canonical format (always use period as decimal separator, strip grouping characters).
  3. Server-side authorization checks the parsed amount against reasonable bounds (e.g., max $1,000 per transaction without additional confirmation).
  4. Payment processor receives a pre-validated, locale-agnostic payload.

Anthropic's flow skipped steps 1, 2, and 3.

Authorization Boundaries for Agent Billing

Agent platforms need two types of spending controls:

Pre-authorization limits: Maximum amount a user can load in a single transaction without manual review.

Post-authorization caps: Maximum API spend per hour/day/month to prevent runaway agent loops from draining credits.

The Anthropic incident exposed a missing pre-authorization boundary. Even if the user had intended to load $10,000, a well-designed system would require:

  • Explicit confirmation modal showing the final amount in the user's currency.
  • Step-up authentication (e.g., email verification) for transactions above a threshold.
  • Server-side validation that the parsed amount matches the client's stated intent.

Here is a minimal server-side validation layer:

from decimal import Decimal, InvalidOperation
from typing import Tuple

def parse_currency_input(
    raw_input: str,
    expected_locale: str,
    max_amount: Decimal = Decimal("1000.00")
) -> Tuple[Decimal, list[str]]:
    """
    Parse currency input with locale awareness and validation.
    Returns (parsed_amount, errors).
    """
    errors = []

    # Normalize common locale formats
    if expected_locale.startswith("es"):
        # Spanish: 1.234,56 -> 1234.56
        normalized = raw_input.replace(".", "").replace(",", ".")
    else:
        # English: 1,234.56 -> 1234.56
        normalized = raw_input.replace(",", "")

    try:
        amount = Decimal(normalized)
    except InvalidOperation:
        errors.append(f"Invalid number format: {raw_input}")
        return Decimal("0"), errors

    # Validation checks
    if amount <= 0:
        errors.append("Amount must be positive")
    if amount > max_amount:
        errors.append(f"Amount exceeds maximum: {max_amount}")
    if amount.as_tuple().exponent < -2:
        errors.append("Maximum 2 decimal places allowed")

    return amount, errors

This is not production-ready (you need proper locale libraries like Babel), but it shows the validation surface. The key insight: never trust raw user input for financial amounts. Always parse, normalize, and validate server-side before touching a payment API.

Observability for Payment Failures

When a payment pipeline fails, you need telemetry that answers:

  • What input did the user provide?
  • What amount did the server parse?
  • What amount did the payment processor charge?
  • Did the user see a confirmation screen?

Structured logging for payment events should capture:

{
  "event": "credit_load_initiated",
  "user_id": "usr_abc123",
  "raw_input": "100,00",
  "detected_locale": "es-CR",
  "parsed_amount": 10000.00,
  "currency": "USD",
  "confirmation_shown": false,
  "payment_processor_response": {
    "charge_id": "ch_xyz789",
    "amount_charged": 10000.00
  }
}

This log would have immediately revealed the locale parsing failure. Without it, the incident looks like user error instead of a systemic validation gap.

Likely Failure Modes in Agent Payment Pipelines

Beyond locale parsing, agent billing infrastructure has several high-risk surfaces:

Runaway agent loops: An agent enters an infinite retry loop, burning through credits in minutes. Mitigation: per-minute spend caps, circuit breakers on repeated identical API calls.

Stale credit balances: Agent checks balance, starts a long-running task, balance depletes mid-execution. Mitigation: reserve credits at task start, release on completion or timeout.

Currency conversion drift: User loads credits in EUR, agent API bills in USD, exchange rate changes between authorization and settlement. Mitigation: lock exchange rate at authorization time, show final USD amount before charging.

Chargeback abuse: User loads $1,000, consumes $900 in API calls, initiates chargeback. Mitigation: delayed credit availability for new accounts, fraud scoring on large initial loads.

Refund timing: User requests refund, but credits are already partially consumed. Mitigation: track consumed vs. available credits separately, only refund the unused portion.

Technical Verdict

Use locale-aware payment pipelines when:

  • You bill users in multiple currencies or serve international markets.
  • Payment amounts are user-entered rather than system-calculated.
  • A single transaction can exceed $100 (where 100x errors become catastrophic).

Avoid single-layer validation when:

  • Financial transactions have no confirmation screen.
  • Client-side formatting is the only input normalization step.
  • Server-side validation trusts that the client sent a well-formed number.

The Anthropic incident is a case study in missing defense layers. The fix is not complicated: parse input server-side using the user's declared locale, show a confirmation modal with the final amount, and reject transactions that exceed reasonable bounds. Every agent platform that handles payments needs these three controls. Without them, a misplaced comma becomes a $10,000 mistake.

Source Links

來源:DEV Community · dev.to