Skip to content

Detect timing-unsafe secret comparisons via taint analysis (CWE-208)#576

Draft
alies-dev wants to merge 4 commits intomasterfrom
worktree-570-timing-unsafe-comparison
Draft

Detect timing-unsafe secret comparisons via taint analysis (CWE-208)#576
alies-dev wants to merge 4 commits intomasterfrom
worktree-570-timing-unsafe-comparison

Conversation

@alies-dev
Copy link
Copy Markdown
Collaborator

@alies-dev alies-dev commented Mar 26, 2026

Blocked by vimeo/psalm#11762

What does this PR do?

Adds TimingUnsafeComparisonHandler that detects timing-unsafe string comparisons involving secrets. When values tainted with user_secret or system_secret flow into ===, ==, !==, !=, strcmp(), or strcasecmp(), Psalm emits TaintedUserSecret or TaintedSystemSecret.

The handler works by adding taint sinks at comparison operators during expression analysis. Only activates during --taint-analysis runs; near-zero overhead in normal analysis.

Use hash_equals() for constant-time comparison instead.

Closes #570

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new taint-analysis rule to detect timing-unsafe secret comparisons (CWE-208) by introducing taint sinks at ==/===/!=/!== and strcmp()/strcasecmp() usage, plus PHPT coverage to validate secret vs non-secret behavior.

Changes:

  • Register a new TimingUnsafeComparisonHandler hook in the plugin.
  • Implement sink insertion for comparison operators and timing-unsafe string compare functions when --taint-analysis is active.
  • Add 11 taint-analysis type tests covering user/system secret flows, operand position, indirect flow, and safe scenarios (hash_equals, non-secret comparisons).

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Plugin.php Registers the new rule handler with Psalm’s plugin hook system.
src/Handlers/Rules/TimingUnsafeComparisonHandler.php Implements taint sink creation for timing-unsafe comparisons involving user_secret/system_secret.
tests/Type/tests/TaintAnalysis/TaintedUserSecretTimingComparison.phpt Asserts == comparison against a user_secret triggers TaintedUserSecret.
tests/Type/tests/TaintAnalysis/TaintedUserSecretStrcasecmp.phpt Asserts strcasecmp() against a user_secret triggers TaintedUserSecret.
tests/Type/tests/TaintAnalysis/TaintedUserSecretNotEqualComparison.phpt Asserts != comparison against a user_secret triggers TaintedUserSecret.
tests/Type/tests/TaintAnalysis/TaintedUserSecretLeftOperand.phpt Asserts detection works when the secret is on the left operand.
tests/Type/tests/TaintAnalysis/TaintedSystemSecretTimingComparison.phpt Asserts === comparison against a system_secret triggers TaintedSystemSecret.
tests/Type/tests/TaintAnalysis/TaintedSystemSecretStrcmp.phpt Asserts strcmp() against a system_secret triggers TaintedSystemSecret.
tests/Type/tests/TaintAnalysis/TaintedSystemSecretNotIdenticalComparison.phpt Asserts !== comparison against a system_secret triggers TaintedSystemSecret.
tests/Type/tests/TaintAnalysis/TaintedSystemSecretIndirectComparison.phpt Asserts secret taint propagates through variables before comparison.
tests/Type/tests/TaintAnalysis/SafeNonSecretStrcmp.phpt Ensures strcmp() with non-secret input does not trigger secret issues.
tests/Type/tests/TaintAnalysis/SafeNonSecretComparison.phpt Ensures === with non-secret input does not trigger secret issues.
tests/Type/tests/TaintAnalysis/SafeHashEqualsSecret.phpt Ensures hash_equals() with a secret does not trigger timing-unsafe comparison issues.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Add TimingUnsafeComparisonHandler that creates taint sinks at ===, ==,
!==, !=, strcmp(), and strcasecmp() for USER_SECRET and SYSTEM_SECRET
taint types. When a secret-tainted value flows into a timing-unsafe
comparison, Psalm emits TaintedUserSecret or TaintedSystemSecret.

Closes #570
Reduces taint graph size and avoids duplicate reports when an operand
has multiple parent nodes (unions/merged values). One sink is created
per comparison side, with all parent nodes connected to it via addPath.
Better DX — the list is scannable and easy to extend.
@alies-dev alies-dev force-pushed the worktree-570-timing-unsafe-comparison branch from 29f278f to 6c0cf7f Compare April 5, 2026 20:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:feature for PRs only (used by release-drafter)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Detect timing attack vulnerability: === instead of hash_equals() for secrets (CWE-208)

3 participants