Skip to content

fix(wecom): use constant-time comparison for the callback signature - #39049

Open
zashraf1337 wants to merge 1 commit into
NousResearch:mainfrom
zashraf1337:zashraf1337-wecom-constant-time-compare
Open

fix(wecom): use constant-time comparison for the callback signature#39049
zashraf1337 wants to merge 1 commit into
NousResearch:mainfrom
zashraf1337:zashraf1337-wecom-constant-time-compare

Conversation

@zashraf1337

Copy link
Copy Markdown

WXBizMsgCrypt.decrypt() checks the callback signature with expected != msg_signature. String != returns at the first byte that differs, so the time it takes leaks how much of the signature matched. By replaying a chosen (timestamp, nonce, encrypt) an attacker can recover the expected signature a byte at a time and forge a callback without knowing the token.

This switches to hmac.compare_digest, which webhook.py and api_server.py already use for the same checks. Low severity since the digest still mixes in the secret token and the attack needs a lot of timed requests, but there is no reason to leave the side channel in.

WXBizMsgCrypt.decrypt() compares the expected SHA-1 signature with !=,
which returns as soon as two bytes differ. That timing difference is an
oracle: by replaying a chosen (timestamp, nonce, encrypt) an attacker can
recover the expected signature a byte at a time and forge a callback
without ever knowing the token. Use hmac.compare_digest instead, which is
already what webhook.py and api_server.py use for the same checks.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening platform/wecom WeCom / WeChat Work adapter P2 Medium — degraded but workaround exists labels Jun 4, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused security hardening. The premise is still valid on current main: WXBizMsgCrypt.decrypt() uses expected != msg_signature at plugins/platforms/wecom/wecom_crypto.py:90.

Problems

  • The PR edits gateway/platforms/wecom_crypto.py, but commit 5600105478ffde29d7566b45421b100eaa29c4ef moved that implementation to plugins/platforms/wecom/wecom_crypto.py. The current callback adapter imports and calls the relocated module at plugins/platforms/wecom/callback_adapter.py:357, so this diff does not reach the live check.
  • No regression test accompanies the change. Existing coverage at tests/gateway/test_wecom_callback.py:32-54 exercises accept/reject behavior but not the constant-time comparison primitive.

Suggested changes

  • Retarget the import and comparison to plugins/platforms/wecom/wecom_crypto.py:9-13,88-91.
  • Add a focused test that verifies hmac.compare_digest is invoked by decrypt().

Automated hermes-sweeper review.

def decrypt(self, msg_signature: str, timestamp: str, nonce: str, encrypt: str) -> bytes:
expected = _sha1_signature(self.token, timestamp, nonce, encrypt)
if expected != msg_signature:
# Constant-time comparison to avoid a timing side-channel on the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current main no longer tracks this path: commit 5600105478ffde29d7566b45421b100eaa29c4ef moved the active implementation to plugins/platforms/wecom/wecom_crypto.py, where the unsafe comparison still exists. Please retarget this change there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Medium — degraded but workaround exists platform/wecom WeCom / WeChat Work adapter sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants