nowakowskir / php-jwt
PHP 的 JSON Web Token 实现。
Requires
- php: >=7.2.1
Requires (Dev)
- phpunit/phpunit: ^7.5|^8.0
README
JWT
在此了解更多关于 JWT 的信息
许可
请在使用前检查 BSD-3 Clause 许可条款。
支持算法
- HS256
- HS384
- HS512
- RS256
- RS384
- RS512
安装
您可以通过运行 composer 命令将此包添加到您的项目中
composer require nowakowskir/php-jwt
确保您的 vendor 自动加载文件已正确加载,并且以下类被使用。
use Nowakowskir\JWT\JWT;
use Nowakowskir\JWT\TokenDecoded;
use Nowakowskir\JWT\TokenEncoded;
元素
当使用此包时,您将主要使用两个类: TokenEncoded
和 TokenDecoded
。
您可以将这些类的对象转换为以下形式
TokenEncoded => TokenDecoded
TokenDecoded => TokenEncoded
TokenDecoded
此类表示一个解码后的令牌。它由一个头和一个有效载荷组成。这两个元素都是数组。
由 TokenDecoded
类的对象表示的令牌允许您访问和修改其任何部分。
TokenEncoded
此类表示一个编码后的令牌。
用法
构建新的 JWT
您可以将 payload 和 header 作为可选参数传递给 TokenDecode
构造函数。
$tokenDecoded = new TokenDecoded(['payload_key' => 'value'], ['header_key' => 'value']);
$tokenEncoded = $tokenDecoded->encode($privateKey, JWT::ALGORITHM_RS256);
echo 'Your token is: ' . $tokenEncoded->toString();
请参阅 安全最佳实践 部分,了解为什么在编码令牌时提供算法是强制性的!
实例化现有令牌
$tokenEncoded = new TokenEncoded('Existing JSON Web Token');
获取令牌的头
$tokenEncoded = new TokenEncoded('Existing JSON Web Token');
$header = $tokenEncoded->decode()->getHeader();
获取令牌的有效载荷
$tokenEncoded = new TokenEncoded('Existing JSON Web Token');
$payload = $tokenEncoded->decode()->getPayload();
请注意,提供密钥不是解码令牌所必需的,因为其头和有效载荷是公开的。您应特别注意不要在令牌的头和有效载荷中传递任何机密信息。JWT 只允许您验证包含给定有效载荷的令牌是否由受信任方签发。它不会保护您在有效载荷中传递的数据!请注意,任何人都可以访问您的令牌有效载荷!
验证令牌
为了使用解码后的有效载荷,请确保您的令牌首先通过了验证过程。否则,无法假设有效载荷是可信的!
try {
$tokenEncoded->validate($publicKey, JWT::ALGORITHM_RS256);
} catch(Exception $e) {
// Token validation failed.
}
请参阅 安全最佳实践 部分,了解为什么在验证令牌时提供算法是强制性的!
如果您需要更多关于为什么您的验证过程失败的信息,您可以捕获几个异常类
带有过期日期(exp)的新 JWT 构建
如果您希望令牌在某日期过期,您可以使用 exp
标志。
$tokenDecoded = new TokenDecoded(['exp' => time() + 1000]);
$tokenEncoded = $tokenDecoded->encode($key, JWT::ALGORITHM_RS256);
带有不早于日期(nbf)的新 JWT 构建
如果您希望令牌在达到某个日期之前不活跃,您可以使用 nbf
标志。
$tokenDecoded = new TokenDecoded(['nbf' => time() + 1000]);
$tokenEncoded = $tokenDecoded->encode($key, JWT::ALGORITHM_RS256);
解决服务器之间的时钟差异问题(exp、nbf)
由于服务器之间的时钟可能不同,您可以使用所谓的 leeway
来解决这个问题。这是一种时间范围,在验证令牌(exp、nbf)时将考虑它。
$leeway = 500;
$tokenEncoded = new TokenEncoded('Existing JSON Web Token');
$tokenEncoded->validate($key, JWT::ALGORITHM_RS256, $leeway);
安全最佳实践
不要在令牌的有效载荷中传递机密数据
请注意,提供密钥不是解码令牌所必需的,因为其头和有效载荷是公开的。您应特别注意不要在令牌的头和有效载荷中传递任何机密信息。JWT 只允许您验证包含给定有效载荷的令牌是否由受信任方签发。它不会保护您在有效载荷中传递的数据!请注意,任何人都可以访问您的令牌有效载荷!
在验证令牌之前不要信任您的有效载荷
确保令牌有效的唯一方法是使用 TokenEncoded::validate()
方法。请记住,TokenDecoded::decode()
方法只解码令牌。它允许您访问其有效载荷,而无需任何验证!
它允许您在不进行任何验证的情况下获取令牌的payload的原因是
- JWT 的本质是令牌的payload未加密,并且未受密钥保护,因此您甚至不应有它受到保护的幻想,
- 您可能需要在验证令牌之前使用令牌有效负载的一些部分。
在编码和验证令牌时强制使用算法
因为在某些情况下,令牌头部中定义的算法可能会被攻击者修改,因此强烈建议不要依赖于令牌头部中包含的算法。
出于安全原因,您应该尽可能选择一种算法,并在发行者和验证者应用程序中坚持使用该算法。
为了提高令牌的安全性,此包要求在编码和验证令牌时提供一个算法。
以下您可以看到正确编码和解码令牌的方法
// Issuer
$tokenDecoded = new TokenDecoded();
$tokenEncoded = $tokenDecoded->encode($privateKey, JWT::ALGORITHM_RS256);
// Consumer
$tokenEncoded->validate($publicKey, JWT::ALGORITHM_RS256);
如您所见,两者使用相同的算法。
如果您选择的用于验证令牌的算法与令牌头部中指定的算法不同,此包将抛出Nowakowskir\JWT\Exceptions\AlgorithmMismatchException
。
这可以保护您的令牌免受篡改后被成功验证的风险。
您可能会想尝试一些变通方法,并使用令牌头部中包含的算法进行验证,尽管这强烈不推荐!
// Don't use algorithm defined in token's header like here!
$header = $tokenEncoded->decode()->getHeader();
$tokenEncoded->validate($publicKey, $header['alg']);
使用不安全的令牌
由于安全原因,创建不安全的令牌是不可能的。
此包不允许您创建使用none
算法或空签名的令牌。
尝试这样做将导致抛出Nowakowskir\JWT\Exceptions\InsecureTokenException
异常。
try {
$tokenEncoded = new TokenEncoded('Existing JSON Web Token with none algorithm or missing signature');
} catch (InsecureTokenException $e) {
// Insecure token
}
try {
$tokenDecoded = new TokenDecoded();
$tokenDeoded->encode($privateKey, 'none');
} catch (InsecureTokenException $e) {
// Insecure token
}
如果不定义算法,也无法解析令牌。
try {
$tokenEncoded = new TokenEncoded('Existing JSON Web Token without an algorithm');
} catch (UndefinedAlgorithmException $e) {
// Algorithm not provided
}
生成强大的私钥
首先,您需要生成一个私钥。
ssh-keygen -t rsa -b 4096 -m PEM -f private.key
chmod 600 private.key
然后,您需要根据私钥生成一个公钥。
openssl rsa -in private.key -pubout -outform PEM -out public.pub
定期轮换您的公钥/私钥对
为了最大限度地降低未经授权实体获取您的公钥/私钥的风险,请定期轮换。
保护您的私钥
确保您的私钥受到保护,并且不被任何未经授权的实体访问。特别要注意文件权限。在大多数情况下,您应该将私钥文件的权限设置为600
,这意味着只有文件的所有者才能访问。
保护您的公钥
即使它被称为公钥,也只有在真正需要的时候才分享这个密钥。此外,文件权限应尽可能严格。不要在请求之间传递公钥或将它们暴露给公众。
不要在URL中传递令牌
它们将被存储在服务器日志、浏览器历史记录等中。
使用令牌的过期日期
尽可能使用令牌的过期日期,这样令牌的有效期就越短。
检查更新
定期检查此包的更新。