---
id: w6/m9/t002
title: "Constant-time comparison hardening: webhook either-key OR, internal tenant-API bearer subtle.ConstantTimeCompare"
worker: worker6
status: done
estimate: 30m
depends_on: []
---

## Objective

Close two non-exploitable but timing-observable comparison paths identified in `w6/004`.

## Context

- `lego/backend/internal/apps/webhook.go:62-69` — each key's MAC is compared with `hmac.Equal` (constant-time), but the loop returns on the first valid signature, letting a timing analysis distinguish which of two keys matched.
- `lego/backend/internal/store/api.go:351-357` — internal tenant-API bearer check is a plain string compare (`!=`) on `BEX_CP_TOKEN`.

## Steps

1. In `webhook.go`: compute both keys' MACs unconditionally and OR the `hmac.Equal` results with a constant-time OR (don't short-circuit on the first match).
2. In `store/api.go`: replace the `!=` string compare with `subtle.ConstantTimeCompare([]byte(got), []byte(want)) != 1`.

## Files

- `lego/backend/internal/apps/webhook.go`
- `lego/backend/internal/store/api.go`

## Acceptance criteria

- [ ] Webhook verification still accepts either valid key and rejects invalid ones; no early-return timing signal remains.
- [ ] Internal tenant-API auth uses a constant-time compare.

## Out of scope

- Any other secret-comparison sites — these two are the audit's findings.
