Summary
Current version of the ping messenger has multiple high/critical security gaps.
Impact is not limited to one attacker type: risks exist for active relay/network attackers, malicious peers, passive observers, and local/supply-chain compromise.
Baseline trust assumption (expected for Nostr/public relays)
Relays are transport infrastructure, not trusted security authorities.
In normal Nostr usage, clients should assume relay-delivered data can be malicious, incomplete, replayed, or modified in transit and must enforce authenticity/integrity checks locally.
This project’s default setup already targets public relays (upstream_ping/README.md:49, ping.py:278-287), so strict client-side verification is not an optional hardening feature; it is a baseline requirement.
Given this default model, relay input must be treated as untrusted and verified client-side, and this is expected across any app making these big claims regardless of relay trust.
Threat models affected
- Active relay/network influence: forged events, key substitution, malformed-frame DoS.
- Malicious peer behavior: identity confusion and key poisoning paths.
- Passive relay observation: traffic/relationship metadata correlation.
- Local/supply-chain attacker: although quite rare, addon execution and artifact trust risks still remain.
Findings
- Critical: inbound authenticity is not enforced before handling
- Receive path dispatches events without mandatory
event.id recompute + Schnorr verification.
schnorr_verify exists but is not used on inbound processing (ping.py:211).
- Direct dispatch from relay frame to handlers (
ping.py:4231-4233).
- Critical: no authenticated binding between Nostr identity and X25519 encryption key
- Incoming content can overwrite peer encryption keys (
ping.py:4368-4369, 4453-4455, 4485-4488).
- Decrypt logic trusts payload-supplied sender key material (
ping.py:2833, 3241).
- Enables key-substitution / MITM-style outcomes.
- High: malformed input can terminate message processing (DoS)
- Listener path lacks top-level exception containment around event handling (
ping.py:4211-4222).
- Unvalidated assumptions on frame/event shape (
ping.py:4231-4233, 4263-4265, 4507, 4566).
- Reproduced crash conditions include non-dict
EVENT payload, unhashable event.id, and event.tags=None.
- High: metadata exposure remains significant
- Relay-visible routing tags (
r/p) expose relationship/linkage (ping.py:4003-4004).
- Legacy discovery broadcasts stable identifiers (
username, ping_id, encryption_pubkey) (ping.py:3886-3893, 3910-3915).
- Conflicts with “No metadata” wording (
upstream_ping/README.md:44).
- Medium: addon trust surface is weak
- Addons are auto-loaded from multiple local paths including CWD (
ping.py:513, load at 5292).
- Official addons are downloaded without artifact signature verification (
ping.py:2331-2364).
- Core updater verifies minisign, but addon artifacts do not (
ping.py:2383-2392).
- Medium: replay/freshness controls are weak across sessions
- Replay/dedupe state is in-memory and reset on join/restart (
ping.py:3720, 3793).
- Dedupe relies on unverified
event.id values (ping.py:4263-4265).
- Medium: forward secrecy is limited
- Message encryption relies on long-lived X25519 identity keys and static ECDH-derived keys (
ping.py:2705, 2813-2837, 3203-3214).
- Ephemeral Nostr pubkeys help unlinkability but do not provide full ratcheting PCS/FS semantics.
Passive capture summary (read-only)
- Relevant Ping events:
4124
- Unique pubkeys:
23
- Unique usernames:
17
- Unique room tags (
r): 12
- Unique recipient tags (
p): 17
- Unique sender->recipient pairs:
35
- Duplicate event IDs seen across multiple relays:
727 / 1016 (71.56%)
- metadata-bearing events:
3919 / 4124 (95.03%)
- Events with visible
r tags: 4057 / 4124 (98.38%)
Passive capture responsible disclosure note
- Analysis was passive listening of publicly accessible relays.
- No unauthorized room access, credential brute-forcing, payload decryption bypass, or exploit deployment was performed.
Summary
Current version of the ping messenger has multiple high/critical security gaps.
Impact is not limited to one attacker type: risks exist for active relay/network attackers, malicious peers, passive observers, and local/supply-chain compromise.
Baseline trust assumption (expected for Nostr/public relays)
Relays are transport infrastructure, not trusted security authorities.
In normal Nostr usage, clients should assume relay-delivered data can be malicious, incomplete, replayed, or modified in transit and must enforce authenticity/integrity checks locally.
This project’s default setup already targets public relays (
upstream_ping/README.md:49,ping.py:278-287), so strict client-side verification is not an optional hardening feature; it is a baseline requirement.Given this default model, relay input must be treated as untrusted and verified client-side, and this is expected across any app making these big claims regardless of relay trust.
Threat models affected
Findings
event.idrecompute + Schnorr verification.schnorr_verifyexists but is not used on inbound processing (ping.py:211).ping.py:4231-4233).ping.py:4368-4369,4453-4455,4485-4488).ping.py:2833,3241).ping.py:4211-4222).ping.py:4231-4233,4263-4265,4507,4566).EVENTpayload, unhashableevent.id, andevent.tags=None.r/p) expose relationship/linkage (ping.py:4003-4004).username,ping_id,encryption_pubkey) (ping.py:3886-3893,3910-3915).upstream_ping/README.md:44).ping.py:513, load at5292).ping.py:2331-2364).ping.py:2383-2392).ping.py:3720,3793).event.idvalues (ping.py:4263-4265).ping.py:2705,2813-2837,3203-3214).Passive capture summary (read-only)
41242317r):12p):1735727 / 1016(71.56%)3919 / 4124(95.03%)rtags:4057 / 4124(98.38%)Passive capture responsible disclosure note