chrisvpearse / phpcrypter
PHP源代码加密器。
Requires
- php: ^8.2
- ext-openssl: *
- symfony/console: ^6.3
README
此开源软件包的目标是通过隐蔽性实现安全。
它旨在提供一种替代方案,用于在明文(plaintext)中交付您的闭源项目。相反,您可以选择以密文(加密)的形式交付它们,同时提供一个二进制PHP扩展,该扩展将即时解密它们。
此软件包使用对称加密,因此AES-256密钥(只有您作为开发者知道),可以每个项目/发布都是唯一的。为了避免被十六进制编辑器(例如Hex Fiend)和strings命令检测到,密钥以XOR密码的形式存储在二进制文件中,分成32部分。此外,XOR密钥也分成32部分。然后将所有64个密钥部分与64个随机密钥部分(总共128个部分)一起打乱,以确保AES-256和XOR密钥部分永远不会出现在同一位置两次。
为什么是加密而不是混淆(obfuscation)?
如果您搜索混淆软件包,几乎总是有一个互补的解混淆软件包可用(由其他人编写),这使得原始软件包过时(不幸的是)。另一方面,AES-256加密尚未被破解(尚无)!
话虽如此,我当然会考虑混淆作为加密的补充。如果您的源代码首先(在加密之前)被混淆,然后有人试图通过查看操作码并逐步执行来逆向工程您的项目,那么将更加困难。
通常,混淆侧重于更改源代码的执行流程,并结合对类、方法、函数、变量和字符串字面量的名称进行混淆。由于混淆本质上重写了您的代码,因此不可避免地会带来一些“陷阱”。另一方面,加密则保留了您的代码(与您编写的一模一样)。
要求
macOS/Linux
- PHP ^8.2
phpize
Windows
此软件包考虑到对Windows的支持,但尚未进行测试。
安装
以下假设您目前处于应用程序的根目录。
$ composer require chrisvpearse/phpcrypter --dev
用法
生成密钥
$ ./vendor/bin/phpcrypter generate [--clean] [--] <name> [<payload>]
以下命令将生成一个唯一的AES-256-CBC对称密钥,名为foo
$ ./vendor/bin/phpcrypter generate foo
此外,将在应用程序的根目录中创建一个.phpcrypter/foo
目录,其中包含PHP扩展骨架。对称密钥是骨架的❤️ - 它们都将用于稍后构建同名的二进制PHP扩展(foo.so
)。
一个好的经验法则是每个项目一个密钥(因此一个PHP扩展)。
上述命令的输出将类似于以下内容
Success!
Payload: pAYL0AD==
❗ 请记住将/.phpcrypter
添加到您的.gitignore
文件。
‼️ 此外,保存负载到密码管理器(如1Password或pass)非常重要。
构建PHP扩展
macOS/Linux
$ cd .phpcrypter/foo $ phpize $ ./configure $ make $ make install
上述命令将构建一个名为 foo.so
的 PHP 扩展,并将其复制到您的 PHP 扩展目录中。您可以通过以下命令找到该目录:
$ php -i | grep ^extension_dir
接下来,您应该将以下行添加到您的 php.ini
配置文件中:
extension=foo.so
加载的 php.ini
配置文件的路径可以通过以下命令找到:
$ php -i | grep "Loaded Configuration File"
接下来,验证扩展是否已 加载:
$ php -m | grep foo foo
加密目录和/或文件
$ ./vendor/bin/phpcrypter encrypt <payload> <path>...
以下命令可以一次性加密多个目录和文件。您必须指定之前获得的 payload
作为第一个参数。
$ ./vendor/bin/phpcrypter encrypt "pAYL0AD==" \ "dir-1" \ "dir-2" \ "file-1.php" \ "file-2.php"
❗ 在上述路径中找到的任何 PHP 文件的内容将被覆盖。强烈建议您为这些文件创建一个新的 Git 分支。
$ git checkout -b encrypted
解密
如果您只是进行实验,能够随意加密和解密很有用。以下命令可以解密使用 payload
参数先前加密的任何目录和/或文件。
$ ./vendor/bin/phpcrypter decrypt <payload> <path>...
❗ 再次提醒,上述路径中找到的任何 PHP 文件的内容将被覆盖。
加密文件看起来像什么?
<?php // @foo if (! extension_loaded('foo')) exit('The "foo" extension is not loaded'); #pAYL0AD==
PHP 代码块应该是自解释的,然而,最后一行包含一个 base64 编码的字符串,其中包含 phpcrypter 版本、IV(初始向量)和加密源代码。
它是如何工作的?
默认情况下,当扩展 加载 时,它只是钩入 PHP 的内部结构,即 zend_compile_file()
函数,但它不执行任何操作,除非将 foo.decrypt
配置选项设置为 1
(默认设置为 1
)。
如果您在 php.ini
配置文件中将 foo.decrypt
设置为 0
,建议您在任何包含/要求加密文件的未加密 PHP 文件中使用 ini_set('foo.decrypt', 1)
。例如,如果您想加密一个控制器,您应该在未加密的基本控制器中使用 ini_set()
。您不能在加密的 PHP 文件中使用 ini_set()
,因为 zend_compile_file()
的工作级别更低。
以下是autocannon 基准测试(10 个连接,持续 10 秒):
部署
当您准备部署加密文件时,如果您的工作站平台与目标平台不同(例如,Linux 与 macOS),则应构建适用于该平台的应用程序扩展。
全局安装
如果您需要在同一服务器上安装多个扩展(用于不同的项目),应考虑全局安装 phpcrypter。
$ composer global require chrisvpearse/phpcrypter
使用带有有效载荷的 PHP 扩展骨架
您必须指定之前获得的 payload
作为第二个参数,以便相同的密钥也成为此骨架的 ❤️。
$ cd ~/.composer $ export HISTCONTROL=ignorespace $ ./vendor/bin/phpcrypter generate foo "pAYL0AD==" $ unset HISTCONTROL
💡 使用 HISTCONTROL=ignorespace
可以防止带有前缀空格的任何命令出现在您的 shell 历史记录中。
再次构建 PHP 扩展
您应该参考上一节,遵循适用于该特定平台的适当步骤。
部署
您现在可以部署您的加密 PHP 文件了!🚀
鸣谢
许可证
MIT 许可证(MIT)。