
Cloud Vulnerability DB
A community-led vulnerabilities database
TOTP::expiresIn() computes period - ((now - epoch) % period) without checking that the clock has reached the epoch (src/TOTP.php:99-104). Both epoch and period can come from a provisioning URI. When the epoch is later than the current time, PHP's % returns a negative remainder, so the result is larger than the period. When the period is at or near PHP_INT_MAX (including any value the (int) cast saturates to it, such as 1e30), the sum overflows to a float, and the int return type raises a TypeError.
Factory::loadFromProvisioningUri() accepts such a URI, for example otpauth://totp/Alice?secret=JBSWY3DPEHPK3PXP&epoch=9223372036854775807&period=9223372036854775807. Calling expiresIn() on the returned object throws a TypeError. That class extends Error, not Exception, and does not implement OTPExceptionInterface, so the handling recommended in doc/Exceptions.md does not catch it. The request ends with an unhandled error, every time the stored URI is used. With the default period, the same kind of URI only produces a wrong countdown: up to 2 * period - 1 seconds, for example 46 seconds for a 30-second period.
Only the party that supplies the URI is affected, and only if the application calls expiresIn() before now() or verify(), or separately from them. For the same object, those two methods already throw the documented InvalidParameterException. No code is accepted and nothing is disclosed.
Line numbers are those of 11.4.3 and 11.5.0.
src/TOTP.php:99-104: TOTP::expiresIn()
The epoch subtraction was introduced in 11.4.0 (commit 1236f0e, "Fix totp expires in (#237)"). Earlier versions ignored the epoch in expiresIn() and are not affected.use OTPHP\Factory;
use OTPHP\InternalClock;
$totp = Factory::loadFromProvisioningUri(
'otpauth://totp/Alice?secret=JBSWY3DPEHPK3PXP&epoch=9223372036854775807&period=9223372036854775807',
new InternalClock()
);
$totp->expiresIn(); // TypeError: OTPHP\TOTP::expiresIn(): Return value must be of type int, float returnedFixed in 11.4.4 and 11.5.1. If the current time is before the epoch, expiresIn() now throws InvalidParameterException ("The timestamp must be greater than or equal to the epoch."), as now() already does. From the epoch on, the result is always between 1 and the period, and the computation cannot overflow.
Call now() or verify() before expiresIn() and handle OTPExceptionInterface, or reject provisioning URIs whose epoch is in the future before you store them.
Reported by Anthropic's OSS Scanner (finding ANT-2026-LXJKQ7KL).
Source: NVD
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."