mczentechnologies / php-jwt
PHP的JSON Web Token实现。从https://github.com/nowakowskir/php-jwt分支而来
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
您可以向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()
方法仅解码令牌。它允许您访问其负载,而不进行任何验证!
它允许您在不进行任何验证的情况下获取令牌负载的原因是
- JWT的本质是令牌的负载未加密且未受密钥保护,因此您甚至不应该有它受到保护的错觉,
- 在验证令牌之前,您可能需要使用令牌的有效负载的某些部分。
在编码和验证令牌时强制算法
因为在某些情况下,令牌头中定义的算法可能被攻击者修改,强烈建议不要依赖于令牌头中包含的算法。
出于安全原因,您应尽可能选择一个算法,并在发行者和验证者应用程序中坚持使用该算法。
为了提高您的令牌安全性,此包要求在编码和验证令牌时提供算法。
下面您可以找到编码和解码令牌的正确方法
// 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中传递令牌
它们将被存储在服务器日志、浏览器历史记录中等。
使用令牌的过期日期
尽可能使用令牌的过期日期,使令牌的有效期尽可能短。
检查更新
定期检查此包的更新。