Skip to content

[REVIEW] api-security: add real-time channel and webhook evidence gates #1443

@NiXouuuu

Description

@NiXouuuu

Skill Being Reviewed

Skill name: api-security
Skill path: skills/appsec/api-security/

False Positive Analysis

Benign code that triggers a false positive:

api_surface:
  style: websocket
  endpoint: wss://api.example.com/realtime
  auth:
    handshake_requires_session: true
    origin_allowlist:
      - https://app.example.com
    csrf_token_required_on_upgrade: true
    session_revalidated_every_minutes: 15
    close_on_logout: true
  message_security:
    schema_validation: true
    per_message_authorization: true
    max_payload_bytes: 65536
    per_user_message_rate_limit: 100/min
    replay_nonce_required: true
  logging:
    connection_events: true
    authz_failures: true
    validation_failures: true
    sensitive_payloads_redacted: true

Why this is a false positive:

The current skill focuses its inventory and output on REST, GraphQL, gRPC, OpenAPI/Swagger paths, GraphQL operations, and gateway configuration. A reviewer could treat an undocumented WebSocket as a shadow API or gateway-bypass finding simply because it is not represented in OpenAPI. That is not always accurate: a real-time channel can be intentionally separate from request/response APIs and still be secure when the review captures explicit evidence for WSS, origin validation, session lifecycle, per-message authorization, message validation, replay controls, rate limits, and logging.

The finding should be about missing evidence or unsafe controls, not about the channel being WebSocket/SSE/webhook by itself.

Coverage Gaps

Missed variant 1: WebSocket/SSE channel only authenticates the initial connection

io.on("connection", (socket) => {
  const user = verifyCookie(socket.handshake.headers.cookie);

  socket.on("delete_project", async ({ projectId }) => {
    // Missing per-message authorization and session revalidation.
    await deleteProject(projectId);
  });
});

Why it should be caught:

Step 1 inventories endpoints and GraphQL subscriptions, but it does not require a real-time channel inventory for WebSocket or Server-Sent Events. The GraphQL section covers resolver authorization, but persistent connections need additional evidence: WSS, Origin allowlist, CSWSH/CSRF protections, session expiry handling, logout disconnect, message-level authorization, input schema validation, replay protection, payload limits, connection limits, and message-rate limits. OWASP's WebSocket guidance explicitly calls out origin validation, session management, message-level authorization, input validation, DoS controls, and monitoring.

Missed variant 2: signed webhook lacks replay and raw-body verification evidence

@app.post("/webhooks/payment")
def webhook(request):
    payload = request.json()
    expected = hmac_sha256(WEBHOOK_SECRET, json.dumps(payload))
    if request.headers["X-Signature"] != expected:
        abort(401)
    process_payment_event(payload)

Why it should be caught:

The skill lists downstream dependencies and API10 unsafe consumption, but it does not require webhook-specific verification fields. A webhook receiver should verify the exact raw request body, sender identity/signature, timestamp freshness, replay nonce/event ID, idempotency, secret rotation, source/event allowlist, and failure handling. Otherwise a receiver can accept replayed events, signature-bypass variants caused by JSON reserialization, or forged callbacks from an untrusted integration while the general API10 checklist still looks complete.

Missed variant 3: asynchronous job/result APIs split authorization across request, status, callback, and download surfaces

POST /exports
Authorization: Bearer user-a

202 Accepted
Location: /exports/jobs/exp_123

GET /exports/jobs/exp_123/result
Authorization: Bearer user-b

Why it should be caught:

Async APIs often create a job, poll status, deliver a callback, and expose a result download URL. The current BOLA/BFLA guidance can catch individual endpoints, but the output does not force reviewers to model the full lifecycle. A secure review should verify owner binding on job IDs, status endpoints, callback registration, callback dispatch, result URLs, pre-signed URL TTL, retry queues, and worker-side authorization context. Otherwise the create endpoint can be safe while the result or callback surface leaks another user's data.

Edge Cases

  • GraphQL subscriptions commonly run over WebSocket, so GraphQL resolver authorization is not enough if the transport accepts cross-site connections or keeps sessions alive after logout.
  • API gateways and HTTP access logs often capture only the WebSocket upgrade request, not later message traffic or authorization failures.
  • SSE is one-way but can still leak sensitive events if subscription filters are not bound to the current user and tenant.
  • Webhook signatures are weaker if the application verifies parsed JSON rather than the raw body that was signed.
  • Replay defenses need both timestamp tolerance and nonce/event-id idempotency; HMAC alone does not prove freshness.
  • Async export/download URLs can remain valid after the initiating user's role changes unless TTL and authorization are rechecked.

Remediation Quality

  • Fix resolves the vulnerability
  • Fix doesn't introduce new security issues
  • Fix doesn't break functionality
  • Issues found: Add an API surface evidence gate for real-time channels, webhooks/callbacks, and async job/result flows. Cross-map findings to API1, API2, API4, API5, API8, API9, and API10 where appropriate.

Recommended evidence fields:

Field Purpose
Non-request/response surface WebSocket, SSE, webhook receiver, outbound callback, async job, export/result URL, GraphQL subscription.
Inventory source Route table, gateway config, message broker, webhook registry, GraphQL schema, worker queue, callback docs.
Auth/session evidence Handshake auth, Origin/CSRF checks, session expiry, logout disconnect, token refresh, tenant binding.
Per-message/per-event authz Message action, event type, job ID, callback target, result URL, object owner, tenant, role check.
Integrity/freshness Raw-body signature verification, timestamp window, nonce/event ID, replay cache, idempotency key.
Resource controls Connection limits, message size, message rate, queue limits, job TTL, result URL TTL, backpressure.
Logging/monitoring Upgrade/connect/disconnect, authz failures, validation failures, replay rejects, redacted payload handling.
Decision Secure, vulnerable, partial, or not evaluable with residual risk.

Comparison to Other Tools

Tool Catches this? Notes
OWASP API Security Top 10:2023 Partial Covers BOLA, BFLA, resource consumption, inventory, and unsafe API consumption, but the skill needs evidence fields for persistent channels and callbacks.
OWASP WebSocket Security Cheat Sheet Yes Explicitly covers WSS, Origin validation, session handling, message-level authorization, validation, DoS controls, replay protection, and monitoring.
OWASP GraphQL Cheat Sheet Partial Covers GraphQL access control, batching, DoS, and secure configuration, but subscriptions over WebSocket still need transport/session evidence.
OpenAPI/Swagger tooling No OpenAPI often misses WebSocket/SSE, webhook runtime behavior, background jobs, and callback result URLs unless separately modeled.
API gateways/WAFs Partial Can enforce handshake or edge limits, but usually cannot prove message-level authorization, raw-body webhook verification, or worker-side result authorization.

Overall Assessment

Strengths:

  • Strong OWASP API Top 10 mapping and finding output structure.
  • Good REST and GraphQL coverage for BOLA, BFLA, property-level authorization, resource consumption, SSRF, inventory, and unsafe upstream consumption.
  • Useful GraphQL-specific guidance for introspection, depth/complexity limits, resolver authorization, and alias abuse.

Needs improvement:

  • The inventory gate does not explicitly include WebSocket, SSE, webhook receivers, outbound callbacks, async job endpoints, export result URLs, or GraphQL subscription transport controls.
  • API10 unsafe consumption is too generic for webhook receivers; it should require signature/freshness/idempotency evidence.
  • Persistent channels need session lifecycle and message-level authorization evidence, not only the initial HTTP upgrade/authentication.
  • Async job flows need lifecycle modeling from create -> status -> worker -> callback -> result download.

Priority recommendations:

  1. Add a real_time_and_async_surface_inventory gate to Step 1.
  2. Add WebSocket/SSE evidence checks for Origin allowlist, session expiry, logout disconnect, per-message authorization, message validation, replay protection, and connection/message rate limits.
  3. Add webhook/callback evidence checks for raw-body signatures, timestamp tolerance, nonce/event replay cache, event allowlists, idempotency, and secret rotation.
  4. Add async job/result flow checks for job ownership, status/result authorization, callback target authorization, result URL TTL, and worker-side auth context.
  5. Extend output API Style values beyond REST/GraphQL/gRPC/General to include WebSocket, SSE, Webhook, Callback, and Async Job.

Sources Checked

Bounty Info

  • I have read and agree to the CONTRIBUTING.md bounty terms
  • Preferred payment method: PayPal or GitHub Sponsors; details can be provided privately after maintainer acceptance.

Note: this was created through the GitHub connector, which could not apply repository labels. Please tag with review and bounty if eligible under CONTRIBUTING.md.

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