# Channel authorization re-validated on subscribe, not only at initial handshake

- **Pattern:** `ab-000739` (`community-realtime.connection-management.channel-auth-revalidation`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000739
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000739)

## Why it matters

A handshake-only authorization model is susceptible to time-of-check/time-of-use attacks: a user whose channel access is revoked after connecting can continue subscribing to restricted channels until the connection drops. CWE-285 (Improper Authorization) and OWASP A01 (Broken Access Control) both describe this failure — authorization decisions must be re-evaluated at the point of access, not cached from an earlier check. In a community app, this means a banned user or a user whose subscription lapses can still join private channels by issuing subscribe events on an existing connection.

## Severity rationale

Critical because a user whose access was revoked post-handshake can subscribe to restricted channels until the TCP connection drops, bypassing OWASP A01 access controls.

## Remediation

Re-check channel permissions on every `subscribe` event, not only at connection time. Emit an error and skip `socket.join()` if the check fails.

```ts
socket.on('subscribe', async (channel: string) => {
  const allowed = await checkChannelAccess(socket.userId, channel);
  if (!allowed) {
    socket.emit('error', { code: 403, message: 'Forbidden', channel });
    return;
  }
  socket.join(channel);
  socket.emit('subscribed', { channel });
});
```

Never trust client-side authorization state. The server must be the sole authority on channel membership.

## Detection

- **ID:** `channel-auth-revalidation`
- **Severity:** `critical`
- **What to look for:** Count all subscribe event handlers. For each, classify whether user permission is re-validated before emitting messages. Enumerate the authorization checks present per handler.
- **Pass criteria:** The subscribe handler validates that the user has permission to access the target channel before emitting any messages. 100% of subscribe handlers must re-validate permissions. Authorization must not rely solely on the initial handshake.
- **Fail criteria:** Channel access is only checked at initial handshake, or no per-channel authorization is implemented.
- **Do NOT pass when:** Authorization is checked only via client-side logic that the server trusts without verification — client-side-only auth is NOT a pass.
- **Skip (N/A) when:** Never — channel-level authorization is critical for multi-channel systems.
- **Cross-reference:** For authorization patterns and permission models, the Auth & Session Audit covers role-based access control.
- **Detail on fail:** `"Subscribe handler does not re-validate channel permissions. User granted permission at handshake could subscribe to restricted channels later."`
- **Remediation:** Validate channel access on every subscribe request:

  ```ts
  socket.on('subscribe', async (channel: string) => {
    const userId = socket.userId;
    const hasAccess = await checkChannelAccess(userId, channel);
    if (!hasAccess) {
      socket.emit('error', { message: 'Forbidden', channel });
      return;
    }
    socket.join(channel);
    socket.emit('subscribed', { channel });
  });
  ```

---

## External references

- cwe CWE-287
- cwe CWE-285
- owasp A01

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-000739
