fix(0005b): drop unsigned frames in SignMsgs rooms (close sig-nil spoof)
client.processFrame verified a frame's signature only when one was present
(`info.Policy.SignMsgs && f.Sig != nil`). In a room whose policy REQUIRES
per-message signatures, an attacker with data-plane access could publish a raw
frame with Sig==nil and a forged Sender, and the receiver accepted it as
authentic because the verification block was skipped (audit N3, report 0006).
On a signed-but-cleartext room any peer that knows the subject could thus
impersonate any sender.
Fix: in a SignMsgs room a missing signature is itself a rejection. processFrame
now drops any frame with Sig==nil before attempting verification:
if info.Policy.SignMsgs {
if f.Sig == nil { return } // signature required but absent: drop
// verify ...
}
Non-signed rooms (ModeNATS) are unaffected: unsigned frames there are still
delivered, so the plain-NATS path is unchanged.
Verification (pkg/client/sig_nil_spoof_test.go, TestReaudit_SigNilSpoof):
- golden: a properly signed frame from a member is delivered.
- error : an unsigned frame with a forged Sender in a SignMsgs room is dropped
(the test fails with "SIG-NIL SPOOF: receiver accepted ..." when the fix is
reverted, confirming it is a real regression guard).
- edge : a non-signed room still delivers an unsigned frame.
- CGO_ENABLED=0 go build ./... && go vet ./... && go test -count=1 ./... green.
Refs: report 0006 N3, issue 0005b.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
-1
@@ -799,7 +799,17 @@ func (c *Client) processFrame(roomID string, info roomView, data []byte, handler
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if info.Policy.SignMsgs && f.Sig != nil {
|
||||
// A room with SignMsgs REQUIRES a signature, so an unsigned frame is
|
||||
// unauthenticated and must be dropped — not silently accepted. The previous
|
||||
// `&& f.Sig != nil` guard verified the signature only when one was present, so
|
||||
// an attacker with data-plane access could publish a frame with Sig==nil and a
|
||||
// forged Sender and have the receiver accept it as authentic in a room that
|
||||
// demands signatures (audit N3, report 0006). Requiring the signature first
|
||||
// closes that spoof.
|
||||
if info.Policy.SignMsgs {
|
||||
if f.Sig == nil {
|
||||
return // signature required by room policy but absent: drop
|
||||
}
|
||||
pub, err := c.signerPub(roomID, f.Sender)
|
||||
if err != nil || !cs.VerifyEd25519(pub, f.SigningBytes(), f.Sig) {
|
||||
return // unauthenticated frame: drop
|
||||
|
||||
Reference in New Issue
Block a user