mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
Merge remote-tracking branch 'origin/support/3.2' into develop
This commit is contained in:
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## [7.0.3](https://github.com/firebase/php-jwt/compare/v7.0.2...v7.0.3) (2026-02-18)
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* add environment for Release Please job ([#619](https://github.com/firebase/php-jwt/issues/619)) ([300fd02](https://github.com/firebase/php-jwt/commit/300fd02c883f096c9067df652dbd23f62cb5e2a7))
|
||||
|
||||
## [7.0.2](https://github.com/firebase/php-jwt/compare/v7.0.1...v7.0.2) (2025-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add key length validation for ec keys ([#615](https://github.com/firebase/php-jwt/issues/615)) ([7044f9a](https://github.com/firebase/php-jwt/commit/7044f9ae7e7d175d28cca71714feb236f1c0e252))
|
||||
|
||||
## [7.0.0](https://github.com/firebase/php-jwt/compare/v6.11.1...v7.0.0) (2025-12-15)
|
||||
|
||||
|
||||
### ⚠️ ⚠️ ⚠️ Security Fixes ⚠️ ⚠️ ⚠️
|
||||
* add key size validation ([#613](https://github.com/firebase/php-jwt/issues/613)) ([6b80341](https://github.com/firebase/php-jwt/commit/6b80341bf57838ea2d011487917337901cd71576))
|
||||
**NOTE**: This fix will cause keys with a size below the minimally allowed size to break.
|
||||
|
||||
### Features
|
||||
|
||||
* add SensitiveParameter attribute to security-critical parameters ([#603](https://github.com/firebase/php-jwt/issues/603)) ([4dbfac0](https://github.com/firebase/php-jwt/commit/4dbfac0260eeb0e9e643063c99998e3219cc539b))
|
||||
* store timestamp in `ExpiredException` ([#604](https://github.com/firebase/php-jwt/issues/604)) ([f174826](https://github.com/firebase/php-jwt/commit/f1748260d218a856b6a0c23715ac7fae1d7ca95b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* validate iat and nbf on payload ([#568](https://github.com/firebase/php-jwt/issues/568)) ([953b2c8](https://github.com/firebase/php-jwt/commit/953b2c88bb445b7e3bb82a5141928f13d7343afd))
|
||||
|
||||
## [6.11.1](https://github.com/firebase/php-jwt/compare/v6.11.0...v6.11.1) (2025-04-09)
|
||||
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ $passphrase = '[YOUR_PASSPHRASE]';
|
||||
// Can be generated with "ssh-keygen -t rsa -m pem"
|
||||
$privateKeyFile = '/path/to/key-with-passphrase.pem';
|
||||
|
||||
// Create a private key of type "resource"
|
||||
/** @var OpenSSLAsymmetricKey $privateKey */
|
||||
$privateKey = openssl_pkey_get_private(
|
||||
file_get_contents($privateKeyFile),
|
||||
$passphrase
|
||||
|
||||
@@ -6,6 +6,8 @@ class ExpiredException extends \UnexpectedValueException implements JWTException
|
||||
{
|
||||
private object $payload;
|
||||
|
||||
private ?int $timestamp = null;
|
||||
|
||||
public function setPayload(object $payload): void
|
||||
{
|
||||
$this->payload = $payload;
|
||||
@@ -15,4 +17,14 @@ class ExpiredException extends \UnexpectedValueException implements JWTException
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
public function setTimestamp(int $timestamp): void
|
||||
{
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
|
||||
public function getTimestamp(): ?int
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class JWK
|
||||
*
|
||||
* @uses parseKey
|
||||
*/
|
||||
public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array
|
||||
public static function parseKeySet(#[\SensitiveParameter] array $jwks, ?string $defaultAlg = null): array
|
||||
{
|
||||
$keys = [];
|
||||
|
||||
@@ -93,7 +93,7 @@ class JWK
|
||||
*
|
||||
* @uses createPemFromModulusAndExponent
|
||||
*/
|
||||
public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key
|
||||
public static function parseKey(#[\SensitiveParameter] array $jwk, ?string $defaultAlg = null): ?Key
|
||||
{
|
||||
if (empty($jwk)) {
|
||||
throw new InvalidArgumentException('JWK must not be empty');
|
||||
|
||||
@@ -31,6 +31,8 @@ class JWT
|
||||
private const ASN1_SEQUENCE = 0x10;
|
||||
private const ASN1_BIT_STRING = 0x03;
|
||||
|
||||
private const RSA_KEY_MIN_LENGTH=2048;
|
||||
|
||||
/**
|
||||
* When checking nbf, iat or expiration times,
|
||||
* we want to provide some extra leeway time to
|
||||
@@ -95,7 +97,7 @@ class JWT
|
||||
*/
|
||||
public static function decode(
|
||||
string $jwt,
|
||||
$keyOrKeyArray,
|
||||
#[\SensitiveParameter] $keyOrKeyArray,
|
||||
?stdClass &$headers = null
|
||||
): stdClass {
|
||||
// Validate JWT
|
||||
@@ -127,6 +129,16 @@ class JWT
|
||||
if (!$payload instanceof stdClass) {
|
||||
throw new UnexpectedValueException('Payload must be a JSON object');
|
||||
}
|
||||
if (isset($payload->iat) && !\is_numeric($payload->iat)) {
|
||||
throw new UnexpectedValueException('Payload iat must be a number');
|
||||
}
|
||||
if (isset($payload->nbf) && !\is_numeric($payload->nbf)) {
|
||||
throw new UnexpectedValueException('Payload nbf must be a number');
|
||||
}
|
||||
if (isset($payload->exp) && !\is_numeric($payload->exp)) {
|
||||
throw new UnexpectedValueException('Payload exp must be a number');
|
||||
}
|
||||
|
||||
$sig = static::urlsafeB64Decode($cryptob64);
|
||||
if (empty($header->alg)) {
|
||||
throw new UnexpectedValueException('Empty algorithm');
|
||||
@@ -154,7 +166,7 @@ class JWT
|
||||
// token can actually be used. If it's not yet that time, abort.
|
||||
if (isset($payload->nbf) && floor($payload->nbf) > ($timestamp + static::$leeway)) {
|
||||
$ex = new BeforeValidException(
|
||||
'Cannot handle token with nbf prior to ' . \date(DateTime::ISO8601, (int) floor($payload->nbf))
|
||||
'Cannot handle token with nbf prior to ' . \date(DateTime::ATOM, (int) floor($payload->nbf))
|
||||
);
|
||||
$ex->setPayload($payload);
|
||||
throw $ex;
|
||||
@@ -165,7 +177,7 @@ class JWT
|
||||
// correctly used the nbf claim).
|
||||
if (!isset($payload->nbf) && isset($payload->iat) && floor($payload->iat) > ($timestamp + static::$leeway)) {
|
||||
$ex = new BeforeValidException(
|
||||
'Cannot handle token with iat prior to ' . \date(DateTime::ISO8601, (int) floor($payload->iat))
|
||||
'Cannot handle token with iat prior to ' . \date(DateTime::ATOM, (int) floor($payload->iat))
|
||||
);
|
||||
$ex->setPayload($payload);
|
||||
throw $ex;
|
||||
@@ -175,6 +187,7 @@ class JWT
|
||||
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
|
||||
$ex = new ExpiredException('Expired token');
|
||||
$ex->setPayload($payload);
|
||||
$ex->setTimestamp($timestamp);
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
@@ -185,11 +198,11 @@ class JWT
|
||||
* Converts and signs a PHP array into a JWT string.
|
||||
*
|
||||
* @param array<mixed> $payload PHP array
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
|
||||
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
* @param string $keyId
|
||||
* @param array<string, string> $head An array with header elements to attach
|
||||
* @param array<string, string|string[]> $head An array with header elements to attach
|
||||
*
|
||||
* @return string A signed JWT
|
||||
*
|
||||
@@ -198,7 +211,7 @@ class JWT
|
||||
*/
|
||||
public static function encode(
|
||||
array $payload,
|
||||
$key,
|
||||
#[\SensitiveParameter] $key,
|
||||
string $alg,
|
||||
?string $keyId = null,
|
||||
?array $head = null
|
||||
@@ -226,7 +239,7 @@ class JWT
|
||||
* Sign a string with a given key and algorithm.
|
||||
*
|
||||
* @param string $msg The message to sign
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
|
||||
* @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256',
|
||||
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||
*
|
||||
@@ -236,7 +249,7 @@ class JWT
|
||||
*/
|
||||
public static function sign(
|
||||
string $msg,
|
||||
$key,
|
||||
#[\SensitiveParameter] $key,
|
||||
string $alg
|
||||
): string {
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
@@ -248,13 +261,19 @@ class JWT
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||
}
|
||||
self::validateHmacKeyLength($key, $algorithm);
|
||||
return \hash_hmac($algorithm, $msg, $key, true);
|
||||
case 'openssl':
|
||||
$signature = '';
|
||||
if (!\is_resource($key) && !openssl_pkey_get_private($key)) {
|
||||
if (!$key = openssl_pkey_get_private($key)) {
|
||||
throw new DomainException('OpenSSL unable to validate key');
|
||||
}
|
||||
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
|
||||
if (str_starts_with($alg, 'RS')) {
|
||||
self::validateRsaKeyLength($key);
|
||||
} elseif (str_starts_with($alg, 'ES')) {
|
||||
self::validateEcKeyLength($key, $alg);
|
||||
}
|
||||
$success = \openssl_sign($msg, $signature, $key, $algorithm);
|
||||
if (!$success) {
|
||||
throw new DomainException('OpenSSL unable to sign data');
|
||||
}
|
||||
@@ -293,7 +312,7 @@ class JWT
|
||||
*
|
||||
* @param string $msg The original message (header and body)
|
||||
* @param string $signature The original signature
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
|
||||
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
|
||||
* @param string $alg The algorithm
|
||||
*
|
||||
* @return bool
|
||||
@@ -303,7 +322,7 @@ class JWT
|
||||
private static function verify(
|
||||
string $msg,
|
||||
string $signature,
|
||||
$keyMaterial,
|
||||
#[\SensitiveParameter] $keyMaterial,
|
||||
string $alg
|
||||
): bool {
|
||||
if (empty(static::$supported_algs[$alg])) {
|
||||
@@ -313,7 +332,15 @@ class JWT
|
||||
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||
switch ($function) {
|
||||
case 'openssl':
|
||||
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
|
||||
if (!$key = openssl_pkey_get_public($keyMaterial)) {
|
||||
throw new DomainException('OpenSSL unable to validate key');
|
||||
}
|
||||
if (str_starts_with($alg, 'RS')) {
|
||||
self::validateRsaKeyLength($key);
|
||||
} elseif (str_starts_with($alg, 'ES')) {
|
||||
self::validateEcKeyLength($key, $alg);
|
||||
}
|
||||
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm);
|
||||
if ($success === 1) {
|
||||
return true;
|
||||
}
|
||||
@@ -350,6 +377,7 @@ class JWT
|
||||
if (!\is_string($keyMaterial)) {
|
||||
throw new InvalidArgumentException('key must be a string when using hmac');
|
||||
}
|
||||
self::validateHmacKeyLength($keyMaterial, $algorithm);
|
||||
$hash = \hash_hmac($algorithm, $msg, $keyMaterial, true);
|
||||
return self::constantTimeEquals($hash, $signature);
|
||||
}
|
||||
@@ -457,7 +485,7 @@ class JWT
|
||||
* @return Key
|
||||
*/
|
||||
private static function getKey(
|
||||
$keyOrKeyArray,
|
||||
#[\SensitiveParameter] $keyOrKeyArray,
|
||||
?string $kid
|
||||
): Key {
|
||||
if ($keyOrKeyArray instanceof Key) {
|
||||
@@ -664,4 +692,57 @@ class JWT
|
||||
|
||||
return [$pos, $data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate HMAC key length
|
||||
*
|
||||
* @param string $key HMAC key material
|
||||
* @param string $algorithm The algorithm
|
||||
*
|
||||
* @throws DomainException Provided key is too short
|
||||
*/
|
||||
private static function validateHmacKeyLength(string $key, string $algorithm): void
|
||||
{
|
||||
$keyLength = \strlen($key) * 8;
|
||||
$minKeyLength = (int) \str_replace('SHA', '', $algorithm);
|
||||
if ($keyLength < $minKeyLength) {
|
||||
throw new DomainException('Provided key is too short');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate RSA key length
|
||||
*
|
||||
* @param OpenSSLAsymmetricKey $key RSA key material
|
||||
* @throws DomainException Provided key is too short
|
||||
*/
|
||||
private static function validateRsaKeyLength(#[\SensitiveParameter] OpenSSLAsymmetricKey $key): void
|
||||
{
|
||||
if (!$keyDetails = openssl_pkey_get_details($key)) {
|
||||
throw new DomainException('Unable to validate key');
|
||||
}
|
||||
if ($keyDetails['bits'] < self::RSA_KEY_MIN_LENGTH) {
|
||||
throw new DomainException('Provided key is too short');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate RSA key length
|
||||
*
|
||||
* @param OpenSSLAsymmetricKey $key RSA key material
|
||||
* @param string $algorithm The algorithm
|
||||
* @throws DomainException Provided key is too short
|
||||
*/
|
||||
private static function validateEcKeyLength(
|
||||
#[\SensitiveParameter] OpenSSLAsymmetricKey $key,
|
||||
string $algorithm
|
||||
): void {
|
||||
if (!$keyDetails = openssl_pkey_get_details($key)) {
|
||||
throw new DomainException('Unable to validate key');
|
||||
}
|
||||
$minKeyLength = (int) \str_replace('ES', '', $algorithm);
|
||||
if ($keyDetails['bits'] < $minKeyLength) {
|
||||
throw new DomainException('Provided key is too short');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,20 +10,19 @@ use TypeError;
|
||||
class Key
|
||||
{
|
||||
/**
|
||||
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
|
||||
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
|
||||
* @param string $algorithm
|
||||
*/
|
||||
public function __construct(
|
||||
private $keyMaterial,
|
||||
#[\SensitiveParameter] private $keyMaterial,
|
||||
private string $algorithm
|
||||
) {
|
||||
if (
|
||||
!\is_string($keyMaterial)
|
||||
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
|
||||
&& !$keyMaterial instanceof OpenSSLCertificate
|
||||
&& !\is_resource($keyMaterial)
|
||||
) {
|
||||
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
|
||||
throw new TypeError('Key material must be a string, OpenSSLCertificate, or OpenSSLAsymmetricKey');
|
||||
}
|
||||
|
||||
if (empty($keyMaterial)) {
|
||||
@@ -46,7 +45,7 @@ class Key
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
|
||||
* @return string|OpenSSLAsymmetricKey|OpenSSLCertificate
|
||||
*/
|
||||
public function getKeyMaterial()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user