Skip to content

Potential paywall bypass: weak payment proof can unlock paid resources #6

Description

@chenshj73

Potential paywall bypass: weak payment proof can unlock paid resources

Hi, I noticed a possible payment-flow issue while reviewing the current repository state. This is a conservative report based on the visible code path, and I may be missing deployment-specific guards outside this repository.

Reviewed HEAD: c7212544f347

What I observed

In the visible payment path, I observed:

Paid blob gate accepts any non-empty paymentProof; TODO notes JWT verification missing, so ?paymentProof=anything bypasses 402

Relevant code locations:

  • worker/src/index.ts
  • docs/api.md
  • docs/adr/004-payment-protocol.md
  • docs/scaling-revenue.md

Relevant code excerpts

worker/src/index.ts:394-418

 394             'X-Payment-Currency': 'USD',
 395             'X-Payment-Methods': 'lightning,stripe',
 396             ...corsHeaders,
 397           },
 398         }
 399       );
 400     }
 401 
 402     // TODO: Validate payment proof (JWT verification)
 403     // For now, accept any non-empty proof for testing
 404   }
 405 
 406   // Fetch from R2
 407   const object = await env.VNSH_STORE.get(id);
 408 
 409   if (!object) {
 410     // R2 object missing but metadata exists - inconsistent state
 411     await env.VNSH_META.delete(`blob:${id}`);
 412     return errorResponse('NOT_FOUND', 'Blob not found', 404, request);
 413   }
 414 
 415   // Track read analytics
 416   const source = getClientSource(request);
 417   trackStat(env, 'reads', ctx);
 418   trackStat(env, `source:${source}:reads`, ctx);

docs/api.md:54-78

  54 ```http
  55 GET /api/blob/a1b2c3d4-e5f6-7890-abcd-ef1234567890 HTTP/1.1
  56 ```
  57 
  58 **Query Parameters:**
  59 
  60 | Parameter | Type | Description |
  61 |-----------|------|-------------|
  62 | `paymentProof` | string | JWT token proving payment (for paid blobs) |
  63 
  64 **Response (200 OK):**
  65 
  66 ```http
  67 HTTP/1.1 200 OK
  68 Content-Type: application/octet-stream
  69 Content-Length: 1234
  70 Cache-Control: private, no-store, no-cache
  71 X-Content-Type-Options: nosniff
  72 X-Opaque-Expires: 2024-01-25T12:00:00.000Z
  73 
  74 <binary encrypted data>
  75 ```
  76 
  77 **Errors:**
  78 

docs/adr/004-payment-protocol.md:27-51

  27 **Cons:**
  28 - Requires account setup
  29 - High fees for micropayments
  30 - Server must store payment state
  31 
  32 #### Lightning Network (Bitcoin L2)
  33 
  34 **Pros:**
  35 - Instant settlement
  36 - Low fees (< 1%)
  37 - No account needed
  38 - Proof of payment (preimage)
  39 
  40 **Cons:**
  41 - Requires Lightning wallet
  42 - Volatile exchange rates
  43 - Learning curve
  44 
  45 #### x402 Protocol Standard
  46 
  47 **Pros:**
  48 - HTTP-native (402 status code)
  49 - Method-agnostic
  50 - Emerging standard
  51 - Self-describing payments

docs/scaling-revenue.md:118-142

 118 This is what makes vnsh different from every other pastebin. AI agents can pay for content programmatically via the HTTP 402 Payment Required standard.
 119 
 120 See also: [architecture-x402-payment-proposal.md](architecture-x402-payment-proposal.md), [ADR 004](adr/004-payment-protocol.md)
 121 
 122 ### 3.1 Complete x402 Payment Flow
 123 
 124 The scaffolding exists in `worker/src/index.ts`. The upload endpoint accepts `?price=X` and the read endpoint returns 402 with payment headers. What's missing:
 125 
 126 1. **JWT verification**: The current code accepts any non-empty `paymentProof` string. Implement HS256 JWT verification with `JWT_SECRET` env var.
 127 2. **Stripe Checkout for per-blob payments**: New endpoint `POST /api/pay/:id` creates a one-time Stripe Checkout session.
 128 3. **Split URL model**: Upload with `?price=1.00` returns both:
 129    - `payUrl`: `vnsh.dev/pay/{id}` (no encryption key, shareable publicly)
 130    - `secretUrl`: `vnsh.dev/v/{id}#secret...` (full key, kept private by creator)
 131    - Buyer visits `payUrl` → pays → receives signed JWT → redirect to `secretUrl`
 132 4. **Revenue split**: Platform 10%, Stripe 2.9% + $0.30, creator ~87%.
 133 
 134 ### 3.2 MCP `vnsh_pay` Tool
 135 
 136 New tool in the MCP server for programmatic agent payments:
 137 
 138 ```
 139 Tool: vnsh_pay
 140 Description: Pay for and retrieve content behind a vnsh paywall
 141 Input: { url: string, max_price: number }
 142 Behavior: detect 402 → create payment → submit proof → return decrypted content

Why this may matter

A weak or simulated payment proof can become an access token for a paid capability, especially when the proof is not cryptographically tied to the request.

Suggested check

Consider making the paid-resource path depend on server-trusted payment requirements and a completed payment state. In particular, re-check recipient/payTo, amount, asset, network, nonce/idempotency, resource binding, and settlement result at the exact point where the protected API/tool/content is released.

Conservative caveat

I only reviewed the code visible in this repository at the HEAD above. If deployment-specific middleware or an upstream service enforces the missing binding/settlement invariant, this may already be mitigated there.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions