GHSA-g7m4-839x-ch6v

Suggest an improvement
Source
https://github.com/advisories/GHSA-g7m4-839x-ch6v
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-g7m4-839x-ch6v/GHSA-g7m4-839x-ch6v.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-g7m4-839x-ch6v
Published
2026-06-18T20:45:47Z
Modified
2026-09-10T03:50:49Z
Severity
  • 8.7 (High) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N CVSS Calculator
Summary
spomky-labs/otphp: Unbounded digits parameter in a provisioning URI triggers an uncaught DivisionByZeroError in OTP generation
Details

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-357digits 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.

Database specific
{
    "cwe_ids":  [
        "CWE-1284",
        "CWE-369"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-06-18T20:45:47Z",
    "nvd_published_at":  null,
    "severity":  "HIGH"
}
References

Affected packages

Packagist / spomky-labs/otphp

Package

Name
spomky-labs/otphp
Purl
pkg:composer/spomky-labs/otphp

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
11.4.3

Affected versions

v1.*
v1.0.0-stable
1.*
1.0.1-stable
v2.*
v2.0.0-stable
v2.0.1-stable
v2.0.2-stable
v3.*
v3.0.0-stable
v3.0.1-stable
v3.1.0
v3.1.1
v4.*
v4.0.0
v4.0.1
v4.0.2
v4.0.3
v4.0.4
5.*
5.0.0
v5.*
v5.0.1
v6.*
v6.0.0
v6.0.1
v6.0.2
v6.0.3
v6.0.4
v7.*
v7.0.0
v7.0.1
v7.0.2
v7.0.3
v7.0.4
v8.*
v8.0.0
v8.1.0
v8.2.0
v8.3.0
v8.3.1
v8.3.2
v8.3.3
v9.*
v9.0.0-alpha1
v9.0.0
v9.0.1
v9.0.2
v9.0.3
v9.1.0
v9.1.1
v9.1.2
v9.1.3
v9.1.4
v10.*
v10.0.0
v10.0.1
v10.0.2
v10.0.3
v11.*
v11.0.0
v11.0.1
v11.0.2
11.*
11.0.3
11.1.0
11.1.1
11.2.0
11.2.1
11.2.2
11.3.0
11.4.0
11.4.1
11.4.2

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-g7m4-839x-ch6v/GHSA-g7m4-839x-ch6v.json"