GHSA-g7m4-839x-ch6v The `digits` parameter parsed from a provisioning URI is validated only with a lower bound (`$value > 0`) and has no upper bound

Not scored Published

An attacker can cause a denial of service in the OTP verification path by supplying a provisioning URI with an excessively large digits parameter, triggering an uncaught error.

Exploited in the wild
Not known
Login needed
Not needed
Attackable
Over the internet
Victim action needed
Not needed
Fix
Available

What is GHSA-g7m4-839x-ch6v?

GHSA-g7m4-839x-ch6v is a denial of service vulnerability in spomky-labs/otphp, published on 18 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.

Official description

Summary

The digits parameter parsed from a provisioning URI is validated only with a lower bound ($value > 0) and has no upper bound (src/OTP.php:353-357). OTP generation computes $code % (10 ** $this->getDigits()) (src/OTP.php:283). When digits is large enough that 10 ** digits overflows PHP's integer range and the (int) cast yields 0 (around digits >= 40 on 64-bit PHP 8.x), the modulo operand becomes 0 and PHP raises a DivisionByZeroError.

Impact

OTPHP\Factory::loadFromProvisioningUri() forwards the attacker-controlled digits query value to setParameter('digits', $value), so a hostile URI such as otpauth://totp/Alice?secret=JBSWY3DPEHPK3PXP&digits=50 produces an OTP object whose at(), now(), and verify() all throw DivisionByZeroError. Because DivisionByZeroError extends Error (not Exception), callers that guard OTP generation with a catch (\Exception) do not catch it, turning a malformed URI into an unhandled fatal error (denial of service of the verification path).

Measured threshold on PHP 8.3: digits = 30 works, digits >= 40 throws DivisionByZeroError: Modulo by zero.

Affected component

  • src/OTP.php:353-357 — digits parameter callback (no upper bound)
  • src/OTP.php:283 — $code % (10 ** $this->getDigits())

Proof of concept

use OTPHP\Factory;
use OTPHP\InternalClock;

$otp = Factory::loadFromProvisioningUri(
    'otpauth://totp/Alice?secret=JBSWY3DPEHPK3PXP&digits=50',
    new InternalClock()
);
$otp->at(0); // DivisionByZeroError: Modulo by zero (escapes catch (\Exception))

Remediation

Enforce a sane upper bound on digits in the parameter validation callback (e.g. reject values above 8–10, the practical range for OTPs) so that an out-of-range value is rejected with a documented exception instead of producing an object that fails later with an uncatchable Error.

Which versions are affected by GHSA-g7m4-839x-ch6v?

2 open source packages ship an affected version, including spomky-labs/otphp.

PackageAffectedFixed in
spomky-labs/otphp PackagistAll versions11.4.3
spomky-labs/otphp PackagistAll versions11.4.3

How do I fix GHSA-g7m4-839x-ch6v?

Upgrade spomky-labs/otphp to 11.4.3 or later.

Upgrade, then put limits on request size, rate and nesting depth so one client cannot exhaust the service. More about Denial of service.

Is GHSA-g7m4-839x-ch6v being exploited?

There is no known exploitation of GHSA-g7m4-839x-ch6v in the wild and no public exploit code.

Sources

Is GHSA-g7m4-839x-ch6v in your software?

Upload an SBOM and sbomify checks it against every vulnerability here, every day.

Check your SBOMs for free