chrisvpearse/code-encrypter-laravel

用于 Laravel 框架的代码加密器。

v0.1.0 2023-12-20 19:01 UTC

This package is auto-updated.

Last update: 2024-09-12 14:02:47 UTC


README

此开源包的目标是 通过隐秘性实现安全

它旨在为将闭源项目以 纯文本 形式交付提供替代方案。相反,您可以选择加密您的文件,并附带一个二进制 PHP 扩展,该扩展将在运行时动态解密它们。

此包使用对称加密,这意味着密钥本身必须嵌入到二进制 PHP 扩展中。然而,该扩展的代码(用于存储密钥和处理解密)是开源的(并包含在此包中)。

密钥由您生成,并且只有您(作为开发者)知道。它可以针对每个项目或客户都是唯一的,这为后续添加更多功能开辟了可能性,例如设置过期日期(免费试用)和/或通过 IP/MAC 地址进行白名单。

要求

  1. macOS
  2. PHP 8.1
  3. Laravel 10
  4. 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源代码》。