资讯专栏INFORMATION COLUMN

Swoole Compiler 加密 Drupal 产生的一些问题

source / 2259人阅读

摘要:第行通过类提供的方法分别获取本类和父类中的属性,然后进行对比剔除父类的属性,保留本类的属性,此方法返回的是一个类对象。

前言

上个星期碰到个客户使用Swoole Compiler加密Drupal导致Drupal项目无法运行的问题,逐步排查后总结问题是Drupal中有部分代码直接通过file_get_contents获取PHP源码导致的,因为项目代码是加密过后的,所以直接获取PHP源码解析是获取不到想要的内容的。
注:

Swoole Compiler:https://www.swoole-cloud.com/compiler.html

Drupal是使用PHP语言编写的开源内容管理框架(CMF),它由内容管理系统(CMS)和PHP开发框架(Framework)共同构成。

加密后的影响Drupal运行的主要代码 代码路径

drupal/vendor/doctrine/common/lib/Doctrine/Common/Reflection/StaticReflectionParser.php:126

    //代码内容
    protected function parse()
    {
        if ($this->parsed || !$fileName = $this->finder->findFile($this->className)) {
            return;
        }
        $this->parsed = true;
        $contents = file_get_contents($fileName);
        if ($this->classAnnotationOptimize) {
            if (preg_match("/A.*^s*((abstract|final)s+)?classs+{$this->shortClassName}s+/sm", $contents, $matches)) {
                $contents = $matches[0];
            }
        }
        $tokenParser = new TokenParser($contents);
        ......
    }

其中部分代码如上,通过class名获取文件路径,然后通过file_get_contents获取PHP文件的内容,其中TokenParser类中构造函数如下

    public function __construct($contents)
    {
         $this->tokens = token_get_all($contents);
         token_get_all("numTokens = count($this->tokens);
    }

传入获取到的源码通过token_get_all进行解析,然后后续分析代码获取PHP文件的类、属性、方法的注释 ,父类的命名空间 和class名 ,本类的use信息等,因为文件已经加密,所以file_get_contents获取到的内容是加密后的内容,token_get_all就解析不到正确的信息,从而导致程序无法运行。

解决方案
本次使用的2.1.1版本的加密器,通过Swoole Compiler加密器加密的代码,在配置文件中save_doc配置选项必须设置为1,如果设置为0则不会保存注释,并且在2.1.3版本swoole_loader.so扩展中新增加的函数naloinwenraswwww也无法获取到类中use的相关信息,具体函数使用在后面会详细说明。
  1    $ref = new ReflectionClass($this->className);
  2
  3    $parent_ref = $ref->getParentClass();
  4
  5    ......
  6
  7    if (is_file($fileName)) {
  8        $php_file_info = unserialize(naloinwenraswwww(realpath($fileName)));
  9        foreach ($php_file_info as $key => $info) {
  10           if ($key == "swoole_namespaces" || $key == "swoole_class_name") {
  11               continue;
  12           }
  13           $this->useStatements[$key] = $info;
  14       }
  15   }
  16
  17   $this->parentClassName = $parent_ref->getName();
  18
  19   if (strpos($this->parentClassName, "")!==0) {
  20       $this->parentClassName = "".$this->parentClassName;
  21   }
  22
  23   $static_properties = [];
  24
  25   $properties = $ref->getProperties();
  26
  27   $parent_properties = $this->createNewArrKey($parent_ref->getProperties());
  28
  29   ......
  30
  31   $static_methods = [];
  32
  33   $methods = $ref->getMethods();
  34
  35   ......

第1行通过类名来获取反射类ReflectionClass类的对象。

因为此反射类包含了所有父类中的属性和方法,但源码中只要获取本类中的属性和方法,所以还要获取父类的反射类然后通过对比来剔除父类中的属性和方法,第3行使用ReflectionClass类提供的getParentClass方法获取父类的反射类,此方法返回父类的ReflectionClass对象。

第25行通过ReflectionClass类提供的getProperties方法分别获取本类和父类中的属性,然后进行对比剔除父类的属性,保留本类的属性,此方法返回的是一个ReflectionProperty类对象。

通过ReflectionProperty类提供的getDocComment方法就可以拿到属性的注释。

同上第33行通过ReflectionClass类提供的getMethods方法可以拿到本类和父类中的方法,然后进行对比剔除父类的方法,保留本类的方法,此方法返回的是一个ReflectionMethod类对象。

通过ReflectionMethod对象提供的getDocComment方法就可以拿到方法的注释。

通过第17行ReflectionClass提供的getName方法可以拿到类名。

因为反射无法获取use类的信息,所以在2.1.3版本中的swoole_loader.so扩展中添加函数naloinwenraswwww,此函数传入一个PHP文件的绝对路径,返回传入文件的相关信息的序列化数组,反序列化后数组如下
    [
        "swoole_namespaces" => "DrupalCoreDatetimeElement",
        "swoole_class_name" => "DrupalCoreDatetimeElementDateElementBase",
        "nestedarray" => "DrupalComponentUtilityNestedArray",
        "drupaldatetime" => "DrupalCoreDatetimeDrupalDateTime",
        "formelement"=> "DrupalCoreRenderElementFormElement"
    ]

其中swoole_namespaces为文件的命名空间,swoole_class_name为文件的命名空间加类名,其他为use信息,键为use类的类名小写字母,如存在别名则为别名的小写字母,值为use类的命名空间加类名,通过该函数和反射函数可以兼容StaticReflectionParser中加密后出现的无法获取正确信息的问题

在加密后的未影响Drupal运行的潜在问题:

代码路径:drupal/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php:39

代码路径:drupal/vendor/symfony/class-loader/ClassMapGenerator.php:91

代码路径:drupal/vendor/symfony/routing/Loader/AnnotationFileLoader.php:90

Drupal中引入了Symfony框架,此框架中部分代码也是通过file_get_contentstoken_get_all来获取PHP文件的类名,但目前未对Druapl运行产生影响,可能并未用到其中方法
解决方案:

StaticReflectionParser类的解决方案一样通过2.1.3版本中的swoole_loader.so扩展中添加函数naloinwenraswwww来获取加密后文件的命名空间和类名

尚未有更好方案的问题:

代码路径:drupal/core/includes/install.inc:220

    function drupal_rewrite_settings($settings = [], $settings_file = NULL)
    {
        if (!isset($settings_file)) {
            $settings_file = Drupal::service("site.path") . "/settings.php";
        }
        // Build list of setting names and insert the values into the global namespace.
        $variable_names = [];
        $settings_settings = [];
        foreach ($settings as $setting => $data) {
            if ($setting != "settings") {
                _drupal_rewrite_settings_global($GLOBALS[$setting], $data);
            } else {
                _drupal_rewrite_settings_global($settings_settings, $data);
            }
            $variable_names["$" . $setting] = $setting;
        }
        $contents = file_get_contents($settings_file);
        if ($contents !== FALSE) {
            // Initialize the contents for the settings.php file if it is empty.
            if (trim($contents) === "") {
                $contents = " $setting) {
                $buffer .= _drupal_rewrite_settings_dump($setting, "$" . $name);
            }

            // Write the new settings file.
            if (file_put_contents($settings_file, $buffer) === FALSE) {
                throw new Exception(t("Failed to modify %settings. Verify the file permissions.", ["%settings" => $settings_file]));
            } else {
                // In case any $settings variables were written, import them into the
                // Settings singleton.
                if (!empty($settings_settings)) {
                    $old_settings = Settings::getAll();
                    new Settings($settings_settings + $old_settings);
                }
                // The existing settings.php file might have been included already. In
                // case an opcode cache is enabled, the rewritten contents of the file
                // will not be reflected in this process. Ensure to invalidate the file
                // in case an opcode cache is enabled.
                OpCodeCache::invalidate(DRUPAL_ROOT . "/" . $settings_file);
            }
        } else {
            throw new Exception(t("Failed to open %settings. Verify the file permissions.", ["%settings" => $settings_file]));
        }
    }

Drupal安装过程中有个配置文件default.setting.php,里面存放了默认配置数组,在安装的过程中会让用户在安装界面输入一些配置比如Mysql的信息,输入过后此方法通过file_get_contentstoken_get_all来获取setting中的信息,然后合并用户在页面输入的信息,重新存回文件,因为整个过程涉及到读取文件,更改文件信息,在存入文件,所以Swoole Compiler在此处暂时没有更好的解决方案,需要在加密的时候选择不加密setting文件。

代码路径:drupal/vendor/symfony/class-loader/ClassCollectionLoader.php:126
此类中是Symfony读取PHP文件然后作相应处理后缓存到文件中,存在和上面代码同样的问题,暂未找到更好的解决方案

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31592.html

相关文章

  • swoole compiler加密drupal产生一些问题

    摘要:通过类提供的方法分别获取本类和父类中的属性,然后进行对比剔除父类的属性,保留本类的属性,此方法返回的是一个类对象。 问题描述 上个星期碰到个客户使用swoole compiler加密drupal导致drupal项目无法运行的问题,逐步排查后总结问题是drupal中有部分代码直接通过file_get_contents获取php源码导致的,因为项目代码是加密过后的,所以直接获取php源码解...

    frolc 评论0 收藏0
  • 4 个常用 HTTP 安全头部

    摘要:偏巧的是重定向的过程会给黑客提供中间人攻击。在上例的情况下,从发送头部到得到响应,有效性可保持年。它认为这个头部可以预防伪造跨站请求。总结使用以上头部可帮你快速容易地预防攻击点击挟持攻击嗅探和中间人攻击。请确保用户的安全性。 它曾是世界性图书馆梦的开始,现在它是全球知识的聚集地,它是目前最流行的,人们将应用都部署之上的万维网。 它是敏捷的代表,它不是单一的实体,它由客户端和服务端组成...

    beita 评论0 收藏0
  • Swoole 源码分析——Server模块之OpenSSL (上)

    摘要:另一方比如小明得到公钥之后,双方就可以通信。然而,中间人还是可能截获公钥,然后自己弄一对秘钥,然后告诉小明说是小红的公钥。这样,小亮在签署小红的身份证的时候,可以在小红身份证后面附上自己的身份证。一般来说,自签名的根身份证用于公司内部使用。 前言 自从 Lets Encrypt 上线之后,HTTPS 网站数量占比越来越高,相信不久的未来就可以实现全网 HTTPS,大部分主流浏览器也对 ...

    ky0ncheng 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<