chrisvpearse / code-encrypter-laravel
用于 Laravel 框架的代码加密器。
Requires
- php: ^8.1
- illuminate/contracts: ^10.0
- spatie/laravel-package-tools: ^1.14.0
Requires (Dev)
- laravel/pint: ^1.0
README
此开源包的目标是 通过隐秘性实现安全。
它旨在为将闭源项目以 纯文本 形式交付提供替代方案。相反,您可以选择加密您的文件,并附带一个二进制 PHP 扩展,该扩展将在运行时动态解密它们。
此包使用对称加密,这意味着密钥本身必须嵌入到二进制 PHP 扩展中。然而,该扩展的代码(用于存储密钥和处理解密)是开源的(并包含在此包中)。
密钥由您生成,并且只有您(作为开发者)知道。它可以针对每个项目或客户都是唯一的,这为后续添加更多功能开辟了可能性,例如设置过期日期(免费试用)和/或通过 IP/MAC 地址进行白名单。
要求
- macOS
- PHP 8.1
- Laravel 10
- Zephir
安装
以下假设您目前位于应用程序的根目录中。
$ composer require chrisvpearse/code-encrypter-laravel
发布配置文件
$ php artisan vendor:publish --tag=code-encrypter-config
Zephir 快速入门
您可以通过从 GitHub 下载 最新发布 PHAR 开始使用 Zephir,然后执行以下命令
$ cd ~ && pecl install zephir_parser
$ cd ~/Downloads && mv zephir.phar /usr/local/bin/zephir && chmod +x /usr/local/bin/zephir
“Hello, World!” 演示
以下假设您目前位于应用程序的根目录中。
创建可调用控制器
$ php artisan make:controller HelloWorld --invokable
📄 ./app/Http/Controllers/HelloWorld.php
<?php namespace App\Http\Controllers; class HelloWorld extends Controller { public function __invoke() { return 'Hello, World!'; } }
更新 Web 路由文件
📄 ./routes/web.php
<?php use App\Http\Controllers\HelloWorld; use Illuminate\Support\Facades\Route; Route::get('/', HelloWorld::class);
更新配置文件
以下显示默认配置文件。
*
通配符匹配指定目录中的所有文件**
通配符是递归的,匹配指定目录及其子目录中的所有文件
📄 ./config/code-encrypter.php
<?php return [ 'paths' => [ app_path('Http/Controllers/Foo/*'), app_path('Http/Controllers/Bar/**'), app_path('Http/Controllers/HelloWorld.php'), ], 'cipher' => config('app.cipher') ?: 'AES-256-CBC', 'minify' => false, ];
满足以下条件时,PHP 文件被认为是有效的
- 扩展名为: .php
- 它包含一个 PHP 开启标签:
<?php
- 它不包含 PHP 结束标签:
?>
首次运行
您应该在运行以下命令后确认可以看到 "Hello, World!"
$ php artisan serve
运行加密命令
$ php artisan code:encrypt
上述命令的输出将类似于以下内容
Encrypted ....................................................... app/Http/Controllers/HelloWorld.php
[ INFO ] Command successfully completed.
Key ............................................. base64:8dsiWW4Ue016d1TBalTlJAE46vfO91y5/Xu9UFZzhmc=
Cipher .................................................................................. AES-256-CBC
Zephir File .......................................... tmp/code-encrypter/zephir/zephir/encrypter.zep
Zephir Temp Directory (Must Delete) .............................................. tmp/code-encrypter
❗ 请务必记下您的 密钥。
在此次运行中,已成功加密一个文件
📄 ./app/Http/Controllers/HelloWorld.php
<?php \Zephir\Encrypter::decrypt("c3DHmSPBpjBUQcZ...8+SePRyCXaKbg==", "hj54ztR3KB+7cWKVJFP0QA=="); // base64:eyJpdiI6ImhqNTR6dFIzS0IrN2NXS1...UwZGVhYTY2MDRhIiwidGFnIjoiIn0=
\Zephir\Encrypter
中的 decrypt()
静态方法从 \Illuminate\Encryption\Encrypter
中的 encryptString()
方法返回的有效负载中获取 值 和 iv(初始化向量)。
注释掉的 base64 编码字符串包含 \Illuminate\Encryption\Encrypter
中的 encryptString()
方法返回的整个 JSON 有效负载,并在解密过程中使用。
“工具”片段
为了您的方便,我已经发布了一个 GitHub Gist,其中包含一些将在本文档的其余部分使用的实用工具。
构建 PHP 扩展
在此阶段,您必须构建PHP扩展,因为Laravel将无法找到\App\Http\Controllers\HelloWorld
(这将导致错误)。
您应该切换到上一次命令输出的详细说明中的Zephir临时目录
$ cd tmp/code-encrypter
从那里,您应该初始化Zephir到已经为您创建的zephir目录
$ zephir init zephir
最后,您应该进入zephir目录并从那里构建扩展
$ cd zephir && zephir build
一旦构建了扩展,您应该会提示将extension=zephir.so
添加到您的php.ini文件中。为此,我将使用GitHub Gist公开的ini实用工具
$ php ~/Downloads/utils.php ini
接下来,我们可以看看Zephir代码本身
📄 ./tmp/code-encrypter/zephir/zephir/encrypter.zep
namespace Zephir;
class Encrypter
{
public static function decrypt(var gLIEokrXlBB6s, var LxARS8923aokLv)
{
return eval(self::ssjgKzuSCrh9Zjhom1P(gLIEokrXlBB6s, LxARS8923aokLv));
}
...
protected static function ssjgKzuSCrh9Zjhom1P(var gLIEokrXlBB6s, var LxARS8923aokLv)
{
var ZURmZSyHXCWAwv;
let ZURmZSyHXCWAwv = ["f1","db","22","59","6e","14",..."bd","50","56","73","86","67"];
return openssl_decrypt(
gLIEokrXlBB6s,
"AES-256-CBC",
hex2bin(implode(ZURmZSyHXCWAwv)),
0,
base64_decode(LxARS8923aokLv)
);
}
...
}
上述代码未经压缩,可以在./config/code-encrypter.php中更改(可以也应该更改)。在压缩过程中,会随机添加空格以确保密钥永远不会在相同的位置两次(在二进制PHP扩展中)。
为了进一步随机化二进制文件,使用了\Illuminate\Support\Str::password()
辅助函数来命名变量和方法。此外,包含密钥的方法与4-8个未使用的方法(每个方法都包含自己的密钥)进行洗牌。每个密钥都存储在其自己的数组中,以避免被strings命令检测
$ strings /path/to/zephir.so
现在,让我们使用GitHub Gist公开的tmp实用工具来删除Zephir临时目录
$ php ~/Downloads/utils.php tmp "/path/to/app/root/"
第二次运行
您应该确认在运行以下命令后仍然能够看到"Hello, World!"
$ cd ../../../ && php artisan serve
恭喜 🎉 您加密的文件现在正在即时解密!
运行解密命令
为了解密您的代码,您必须将密钥作为第一个参数提供
$ php artisan code:decrypt base64:8dsiWW4Ue016d1TBalTlJAE46vfO91y5/Xu9UFZzhmc=
上述命令的输出将类似于以下内容
Decrypted ....................................................... app/Http/Controllers/HelloWorld.php
[ INFO ] Command successfully completed.
在此次运行中,一个文件已成功解密
📄 ./app/Http/Controllers/HelloWorld.php
<?php namespace App\Http\Controllers; class HelloWorld extends Controller { public function __invoke() { return 'Hello, World!'; } }
再次运行加密命令
一旦使用Zephir构建了PHP扩展,您就可以多次解密和加密您的代码而无需重新构建扩展(前提是您使用相同的密钥)。密钥应通过命令选项提供
$ php artisan code:encrypt --key=base64:8dsiWW4Ue016d1TBalTlJAE46vfO91y5/Xu9UFZzhmc=
注意。在加密代码时,./tmp/code-encrypter目录将始终为您生成。
NativePHP
在撰写本文时,NativePHP附带了一个静态PHP二进制文件,因此无法安装其他扩展。
然而,作为一个PoC,我们可以用本地PHP的二进制文件替换NativePHP的二进制文件(NativePHP必须已经安装)。为此,我将使用GitHub Gist公开的bin实用工具
$ php ~/Downloads/utils.php bin "/path/to/app/root/"
第三次运行
您应该确认在运行以下命令后仍然能够看到"Hello, World!"
$ php artisan native:serve
恭喜,再次 🎊 您加密的文件现在在NativePHP中即时解密!
致谢
- Aleksandar Jevremović,等人(2013)。《使用密码学模型保护PHP源代码》。