kbs1 / laravel-encrypted-api
在 Laravel 应用程序之间加密 API 请求和响应。
Requires
- php: >=7.0
- laravel/framework: >=5.4
This package is not auto-updated.
Last update: 2023-04-26 17:49:26 UTC
README
轻松创建 Laravel 应用程序之间的加密 API 通信。请求和响应数据使用双向加密安全传输,所有数据都经过校验和以确保不会被修改(防止中间人攻击)。
此包旨在同时用于客户端和服务器应用程序。因为它处理接收请求(验证和解密)以及发送请求/响应(加密和签名),整个实现无缝且仅基于中间件。
中间件透明地修改传入的请求,并用解密后的值替换请求数据,因此您可以使用自己的 FormRequest
、验证或其他您通常与标准请求一起使用的代码(例如在控制器中使用 $request->input('foo')
等)。
您可以根据需要扩展中间件以满足特定需求(例如多个客户端安全通信,每个客户端都有自己的共享密钥集)。
应使用 HTTPS 为调用的 API 路由提供额外的安全性,但这不是必需的。
此包会验证调用客户端,因为没有其他调用者知道共享密钥。这确保您的 API 只能从您或批准的第三方控制的应用程序安全调用,即使 API 路由本身对互联网公开。
安装
composer require kbs1/laravel-encrypted-api
包已安装。如果您正在使用 laravel 版本 < 5.5,请在您的 config/app.php
提供商部分添加以下行
Kbs1\EncryptedApi\Providers\EncryptedApiServiceProvider::class,
配置
默认情况下,该包支持与恰好一个客户端的加密通信,使用一对共享密钥。首先使用 php artisan vendor:publish --tag=encrypted-api
发布配置并设置适当的 secret1
和 secret2
值(长度至少为 32 字节,对于 secret1
,仅使用前 32 字节)。在您的其他应用程序中也进行相同的操作,然后您就可以开始了!
为了方便,包含 php artisan encrypted-api:secrets:generate
命令以生成合适的共享密钥。通过传递 --save
选项自动发布配置(如果尚未发布),并在配置文件中就地修改新的共享密钥。使用 --save
选项执行命令后,您可以通过打开 config/encrypted_api.php
来查看生成的密钥。在任何情况下,将共享密钥复制到您的其他应用程序,配置即可完成!
使用
包安装后,会自动注册 kbs1.encryptedApi
中间件别名。您可以在任何希望使用此包进行安全保护的路由中使用此别名。
接收请求(服务器应用程序)
Route::group(['prefix' => '/api', 'middleware' => ['kbs1.encryptedApi']], function () {
Route::post('/users/disable', 'App\Http\Controllers\Api\UsersController@disable')->name('myApp.api.users.disable');
...
});
上述示例自动使用此包保护了路由组,现在对组的任何调用都必须仅使用经过验证的客户端应用程序发送。默认中间件实现使用在 config/encrypted_api.php
中定义的共享密钥。
您可以通过在数据库中存储秘密(例如)轻松地支持多个调用客户端。扩展 Kbs1\EncryptedApi\Http\Middleware\EncryptedApi
类,并实现自己的 getSharedSecrets()
方法
class ClientApi extends \Kbs1\EncryptedApi\Http\Middleware\EncryptedApi
{
protected function getSharedSecrets($request)
{
$client = \App\Clients\ClientRepository::findByUuid($request->route('clientUuid'));
return ['secret1' => $client->secret1, 'secret2' => $client->secret2];
}
}
在上面的例子中,路由组可能看起来像这样
Route::group(['prefix' => '/api/{clientUuid}', 'middleware' => ['clientApi']], function () {
Route::post('/users/disable', 'App\Http\Controllers\Api\Clients\UsersController@disable')->name('myApp.api.clients.users.disable');
...
});
发送请求(调用应用程序)
可以通过以下方式调用加密API服务
$call = new \Kbs1\EncryptedApi\Http\ApiCall("https://server-application.dev/api/$ourUuid/users/disable", 'POST', [
'user_uuid' => '...',
'parameter1' => true,
'parameter2' => 'foo',
...
], $secret1, $secret2);
try {
$response = $call->execute(); // will execute the call each time invoked
} catch (\Kbs1\EncryptedApi\Exceptions\EncryptedApiException $ex) {
...
}
// retrieve service response later if desired
$response = $call->response();
$http_status_code = $call->httpStatus();
$response_headers = $call->headers();
$response
将包含服务发送的任何响应。这可能是一个JSON或其他任何您实现的响应。此包保护的全部服务响应在发送之前都经过适当签名和加密,即使在发生异常(无效的请求数据、服务崩溃等)的情况下也是如此。这意味着没有知道所需共享秘密的人在任何情况下都无法读取服务响应。
ApiCall
构造函数可以接受集合或数组作为第三个可选数据参数。第四和第五个参数(secret1
和 secret2
)也是可选的,如果省略,则从 config/encrypted_api.php
文件中加载共享秘密。
对于 GET
请求,该包将发送请求体。这确保了请求也必须被适当签名,并且除授权调用者外,没有人可以调用该路由。
关于查询字符串和路由参数的说明
建议仅使用 ApiCall
类的第三个(数据)参数发送每个API服务参数(即使是GET请求)。尽管该包在服务器端验证被调用的确切URL(包括查询字符串和HTTP方法),但作为查询参数或路由段传递的敏感数据仍可能被捕获,例如在服务器的访问日志中。
安全传递的参数(第三个数据参数)始终会覆盖查询字符串参数,使用Laravel的 $request->merge()
方法。
唯一建议作为查询字符串参数或路由段传递的参数是 clientUuid
参数,如果您有多个调用客户端。因为这个参数用于为特定客户端加载共享秘密,所以它不能被加密传递。
IP白名单
如果您想确保来自特定客户端的API调用仅来自白名单IPv4地址,您可以在 config/encrypted_api.php
中设置适当的 ipv4_whitelist
数组。为了提供基于 clientUuid
或任何其他客户端标识符(当您有多个调用客户端)的自己的白名单,请覆盖您自己的路由中间件类中的 getAllowedIps
方法
class ClientApi extends \Kbs1\EncryptedApi\Http\Middleware\EncryptedApi
{
protected function getAllowedIps($request)
{
$client = \App\Clients\ClientRepository::findByUuid($request->route('clientUuid'));
return [$client->ipv4];
}
}
重放攻击
此包通过简单的重放攻击进行保护,因为每个已签名的请求和响应都有一个唯一的标识符,并且仅在10秒内有效。实现自动在服务器端存储过去10秒内接收到的每个标识符,并在遇到已处理的请求标识符时丢弃任何处理。