Affected Versions
| Affected | not applicable (first-party code) |
| Fixed in | not applicable (first-party code) — see PR for commit |
| Ecosystem | not applicable (first-party code) |
| CVE / GHSA | not assigned |
| CWE | unknown |
Introduction
A critical authentication flaw in Hybridauth's Telegram OAuth provider allowed attackers to forge authentication tokens through precise timing analysis. The authenticateCheckError() method—which validates Telegram's callback data using HMAC-SHA256—relied on PHP's strcmp() function to compare computed and received hash values. This function returns immediately on the first mismatched byte, creating measurable timing differences that leak information about the correct hash prefix.
The vulnerability is particularly insidious because the cryptographic implementation appears correct at first glance: the code properly computes an HMAC using hash_hmac(), uses a SHA-256 derived key, and concatenates parameters in Telegram's specified format. Yet a single comparison operation undermined the entire authentication guarantee.
The Vulnerability Explained
The vulnerable code in authenticateCheckError() constructed a data verification string from Telegram's callback parameters, computed an expected HMAC, then validated it:
$secret_key = hash('sha256', $this->botSecret, true);
$hash = hash_hmac('sha256', $data_check_string, $secret_key);
if (strcmp($hash, $check_hash) !== 0) {
throw new InvalidAuthorizationCodeException(
sprintf('Provider returned an error: %s', 'Data is NOT from Telegram')
);
}
The strcmp() function compares strings lexicographically, returning as soon as it finds a difference. For two 64-character hex strings, a comparison that fails at position 5 executes faster than one that fails at position 60. An attacker sending forged callbacks with systematically varied hash values can measure response times to determine how many leading characters match the valid hash.
Attack scenario: An attacker intercepts or constructs a Telegram callback with manipulated user data (claiming to be an admin user, for instance). They send guesses for the $check_hash parameter, starting with 000...0 through fff...f. When a guess with prefix a7f takes measurably longer than a7e, they've confirmed the first three characters. Repeating this process 64 times yields a complete valid hash, allowing complete authentication bypass.
The real-world impact is severe: any service using this provider for Telegram login could have attacker-controlled accounts elevated to arbitrary identities, including administrative accounts if the OAuth flow supports role assignment.
The Fix
The fix replaces strcmp() with PHP's hash_equals() function, which performs constant-time comparison regardless of where strings differ:
$secret_key = hash('sha256', $this->botSecret, true);
$hash = hash_hmac('sha256', $data_check_string, $secret_key);
if (!hash_equals($hash, $check_hash)) {
throw new InvalidAuthorizationCodeException(
sprintf('Provider returned an error: %s', 'Data is NOT from Telegram')
);
}
hash_equals() was introduced in PHP 5.6.0 specifically to address this class of vulnerability. It compares all bytes of both strings before returning, and uses operations whose execution time does not depend on the data content. The logic change is minimal—strcmp() !== 0 becomes !hash_equals()—but the security property transformation is fundamental.
The surrounding code including $secret_key derivation, $data_check_string construction from Telegram's auth_date, id, and other parameters, and the exception throwing logic remain unchanged. This surgical fix preserves all existing functionality while eliminating the side-channel.
Key Takeaways
-
strcmp()and===on sensitive values leak timing information—always usehash_equals()for HMAC, password hash, or cryptographic signature comparison in PHP, even when the values are hex-encoded strings. -
Correct cryptography adjacent to vulnerable code is still vulnerable—the HMAC computation itself was flawless, but the comparison operation invalidated the security guarantee. Review authentication logic end-to-end, not just the cryptographic primitives.
-
OAuth provider implementations are high-value targets—as centralized authentication gateways, flaws here compromise every downstream service. Third-party authentication libraries warrant security audits comparable to primary application code.
-
Timing attacks are practical over network distances—modern statistical techniques and repeated measurements can extract timing differences in the nanosecond range even across internet paths. Never assume network jitter protects against these attacks.
How Orbis AppSec Detected This
Orbis AppSec identified this vulnerability through static analysis of the authentication flow in the Telegram OAuth provider implementation.
- Source: The
$check_hashparameter from Telegram's OAuth callback data, received via HTTP request - Sink: The
strcmp()function comparing computed and received HMAC values inauthenticateCheckError() - Missing control: Constant-time comparison primitive;
strcmp()was used wherehash_equals()is required for cryptographic verification - CWE: unknown
- Fix: Replace
strcmp($hash, $check_hash)withhash_equals($hash, $check_hash)to eliminate timing side-channel
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability demonstrates how a single function choice—strcmp() versus hash_equals()—can undermine otherwise sound cryptography. The Telegram OAuth provider's HMAC-SHA256 implementation was algorithmically correct, yet the timing side-channel in signature comparison rendered it exploitable. For developers integrating OAuth providers or implementing any cryptographic verification, this case underscores that security requires correct primitives at every step: generation, computation, encoding, and comparison. The fix is now available; services using this provider should apply the update immediately.