资讯专栏INFORMATION COLUMN

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

frolc / 1249人阅读

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

问题描述

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

加密框架drupal

Drupal是使用PHP语言编写的开源内容管理框架,下载完后需要进行相关的配置和安装才能进行使用。

在加密后的影响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则不会保存注释,如果设置为0,在2.1.3版本swoole_loader.so扩展中新增加的函数naloinwenraswwww也无法获取到类中use的相关信息,具体函数使用在后面会详细说明。

    $ref = new ReflectionClass($this->className);

    $parent_ref = $ref->getParentClass();

    ......

    if (is_file($fileName)) {
        $php_file_info = unserialize(naloinwenraswwww(realpath($fileName)));
        foreach ($php_file_info as $key => $info) {
            if ($key == "swoole_namespaces" || $key == "swoole_class_name") {
                continue;
            }
            $this->useStatements[$key] = $info;
        }
    }

    $this->parentClassName = $parent_ref->getName();

    if (strpos($this->parentClassName, "")!==0) {
        $this->parentClassName = "".$this->parentClassName;
    }

    $static_properties = [];

    $properties = $ref->getProperties();

    $parent_properties = $this->createNewArrKey($parent_ref->getProperties());

    ......

    $static_methods = [];

    $methods = $ref->getMethods();

    ......

1.通过类名来获取反射类ReflectionClass类的对象。
2.因为此反射类包含了所有父类中的属性和方法,但源码中只要获取本类中的属性和方法,所以还要获取父类的反射类然后通过对比来剔除父类中的属性和方法,可以使用ReflectionClass类提供的getParentClass()方法获取父类的反射类,此方法返回父类的ReflectionClass对象。
3.通过ReflectionClass类提供的getProperties()方法分别获取本类和父类中的属性,然后进行对比剔除父类的属性,保留本类的属性,此方法返回的是一个ReflectionProperty类对象。
4.通过ReflectionProperty类提供的getDocComment()方法就可以拿到属性的注释。
5.同上通过ReflectionClass类提供的getMethods()方法可以拿到本类和父类中的方法,然后进行对比剔除父类的方法,保留本类的方法,此方法返回的是一个ReflectionMethod类对象。
6.通过ReflectionMethod对象提供的getDocComment()方法就可以拿到方法的注释。
7.通过ReflectionClass提供的getName()方法可以拿到类名。

ps:因为反射无法获取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中引入了Symfony框架,此框架中部分代码也是通过file_get_contents和token_get_all来获取php文件的类名,但目前未对druapl运行产生影响,可能并未用到其中方法

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

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

解决方案:同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_contents和token_get_all来获取setting中的信息,然后合并用户在页面输入的信息,重新存回文件,因为整个过程涉及到读取文件,更改文件信息,在存入文件,所以swoole compiler在此处暂时没有更好的解决方案,需要在加密的时候选择不加密setting文件。

代码路径:drupal/vendor/symfony/class-loader/ClassCollectionLoader.php:126

此类中是symfony读取php文件然后作相应处理后缓存到文件中,存在和上面代码同样的问题,暂未找到更好的解决方案

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

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

相关文章

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

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

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

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

    beita 评论0 收藏0
  • PHP多版本下安装Swoole引起问题

    摘要:问题首先你电脑上,系统是是安装了很多版本的,其次,你的引用改了之后有多个引起多个版本扩展共存的问题即如在我本地为目录下然后在目录下会是这样这种情况下使用进行安装将会出现的情况,这样你使用会一起报这个问题,如果不是这个问题就不用往下看了。 问题 首先,你电脑上,系统是Ubuntu是安装了很多版本的PHP,其次,你的PHP引用改了之后有多个引起多个版本扩展共存的问题即如在我本地为/etc/...

    Flands 评论0 收藏0

发表评论

0条评论

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