composer.lock 如何保护您的项目免受恶意依赖

How does composer.lock protects your project of malicious dependencies

在我的项目中,我在 github 上签入 composer.lock 文件。 假设我需要 composer.json 中的健康依赖,例如:

"require": {
    "foo/bar": "v3.0"
  },

在我调用 composer install 后,创建了一个 composer.lock 文件。

"packages": [
        {
            "name": "foo/bar",
            "version": "v3.0",
            "source": {
                "type": "git",
                "url": "https://github.com/foo/bar.git",
                "reference": "bbafb0edb791b23220563d113d00371ea42aedaa"
            },
            "type": "project",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Mr.Foo",
                    "email": "mr.foo@bar.de"
                }
            ],
            "time": "2019-09-30T12:13:55+00:00"
        }

假设拥有 foo/bar 存储库的攻击者会删除 v3.0 标签。攻击者会为 v3.0 命名一个不同的提交。 有人可以确认 composer install 将始终检查 composer.lock 安装依赖项吗? 如果我 运行 composer install 没有 composer.lock 文件,composer 将创建一个带有新引用的新 .lock 文件(提交编号)。如果我 运行 composer install 使用 composer.lock 文件 composer 将坚持提交 ID ("reference" : "bbafb0edb791b23220563d113d00371ea42aedaa" , 旧 v3.0)。 Composer 不会加载恶意的假 v3.0。 v3.0 指向 github 上的新提交 ID。

有人可以确认 composer.lock 的 reference 标签比 version 标签有一个 "higher priority" 吗? composer 是否完全保护我的项目免受此类攻击?

TL;DR;

当然,您问题的答案是:

Yes, composer will protect you

要么它会根据你的 composer.lock 中声明的提交哈希安装包,如果它存在于存储库中,只是忽略提交和版本之间的不匹配,要么它会失败并显示一个漂亮的直接说出原因:"history was rewritten?"


这个问题确实激起了我的好奇心:我会说是的,否则,将提交哈希锁定在锁定文件中将毫无用处,但为了正确性,我不得不对其进行测试。

这就是我所做的:

  1. 在 "obvious" 场景中,攻击者真的会重写历史并删除标签与您的 composer.json 约束相匹配的提交
  2. 再想想,还有一种情况是攻击者会保留标记为您的约束的提交,但会添加一个更新的提交并使用与您的约束匹配的标记重新标记新提交

第一种情况:

我安装了一个基本包,到一些特定的版本(不是最新的,只是为了有一个版本限制):

$ composer require psr/log:1.0.0

这让我得到了这个非常简单的结果 composer.json

{
    "require": {
        "psr/log": "1.0.0"
    }
}

还有这个composer.lock

{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "2865f724e23cffb23b3afd3a968e0359",
    "packages": [
        {
            "name": "psr/log",
            "version": "1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/log.git",
                "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
                "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
                "shasum": ""
            },
            "type": "library",
            "autoload": {
                "psr-0": {
                    "Psr\Log\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "http://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging libraries",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "time": "2012-12-21T11:40:51+00:00"
        }
    ],
    "packages-dev": [],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": [],
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": [],
    "platform-dev": []
}

然后为了测试它,我只是将提交散列 fe0936ee26643249e916849d48e3a51d5f5e278bcomposer.lock 中找到的所有位置更改了一个字符:fe0936ee26643249e916849d48e3a51d5f5e278c(最后一个 b变成了 c);以此结尾 composer.lock:

{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "2865f724e23cffb23b3afd3a968e0359",
    "packages": [
        {
            "name": "psr/log",
            "version": "1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/log.git",
                "reference": "fe0936ee26643249e916849d48e3a51d5f5e278c"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278c",
                "reference": "fe0936ee26643249e916849d48e3a51d5f5e278c",
                "shasum": ""
            },
            "type": "library",
            "autoload": {
                "psr-0": {
                    "Psr\Log\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "http://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging libraries",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "time": "2012-12-21T11:40:51+00:00"
        }
    ],
    "packages-dev": [],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": [],
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": [],
    "platform-dev": []
}

请注意: 如果您在浏览器中尝试此操作,因为 composer 稍后会为您执行此操作,您最终会得到一个 404 页面:https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278c

我删除了我的 vendor 文件夹,为此:

$ rm -Rf vendor

然后,重新运行一个依赖安装,以这个输出结束:

$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 1 install, 0 updates, 0 removals
  - Installing psr/log (1.0.0): Downloading (0%)    Failed to download psr/log from dist: The "https://codeload.github.com/php-fig/log/legacy.zip/fe0936ee26643249e916849d48e3a51d5f5e278c" file could not be downloaded (HTTP/1.1 404 Not Found)
    Now trying to download from source
  - Installing psr/log (1.0.0): Cloning fe0936ee26 from cache
    fe0936ee26643249e916849d48e3a51d5f5e278c is gone (history was rewritten?)


  [RuntimeException]                                                                                                                  
  Failed to execute git checkout 'fe0936ee26643249e916849d48e3a51d5f5e278c' -- && git reset --hard 'fe0936ee26643249e916849d48e3a51d  
  5f5e278c' --                                                                                                                        

  fatal: reference is not a tree: fe0936ee26643249e916849d48e3a51d5f5e278c                                                            


install [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--no-suggest] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--ignore-platform-reqs] [--] [<packages>]...

如果您只有一行可读出此输出,则为:

fe0936ee26643249e916849d48e3a51d5f5e278c is gone (history was rewritten?)

第二种情况:

这一次,我在 php-fig/log to find the initial commit of the repository: https://github.com/php-fig/log/commit/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0

的存储库中做了一些挖掘

并且,以同样的方式,我编辑了我的 composer.lock,但这次伪造了一个事实,即回购的初始提交是标记为 1.0.0 的事实,而它是 [=44] =].

这让我得到了这个 composer.lock

{
    "_readme": [
        "This file locks the dependencies of your project to a known state",
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "2865f724e23cffb23b3afd3a968e0359",
    "packages": [
        {
            "name": "psr/log",
            "version": "1.0.0",
            "source": {
                "type": "git",
                "url": "https://github.com/php-fig/log.git",
                "reference": "a7ab552fdb2efb80aeca09da3bbd9335fc945ff0"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/php-fig/log/zipball/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0",
                "reference": "a7ab552fdb2efb80aeca09da3bbd9335fc945ff0",
                "shasum": ""
            },
            "type": "library",
            "autoload": {
                "psr-0": {
                    "Psr\Log\": ""
                }
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage": "http://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging libraries",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "time": "2012-12-21T11:40:51+00:00"
        }
    ],
    "packages-dev": [],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": [],
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": [],
    "platform-dev": []
}

请注意: 这次尝试,将下载包含初始提交时的存储库状态的 zip:https://api.github.com/repos/php-fig/log/zipball/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0

重复上述删除vendor文件夹

$ rm -Rf vendor

已清除作曲家缓存,另外,这次,因为,剧透警报,安装成功:

$ composer clearcache && rm -Rf vendor
Clearing cache (cache-vcs-dir): /tmp/cache/vcs
Clearing cache (cache-repo-dir): /tmp/cache/repo
Clearing cache (cache-files-dir): /tmp/cache/files
Clearing cache (cache-dir): /tmp/cache
All caches cleared.

然后,重新运行一个依赖安装,以这个输出结束:

$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 1 install, 0 updates, 0 removals
  - Installing psr/log (1.0.0): Downloading (100%)         
Generating autoload files

对我的比喻安装进行得太顺利感到好奇,我重新运行这个过程,更详细地说,想知道作曲家到底在做什么:

$ rm -Rf vendor/ && composer clearcache && composer install -vvv
Cache directory does not exist (cache-vcs-dir): 
Clearing cache (cache-repo-dir): /tmp/cache/repo
Clearing cache (cache-files-dir): /tmp/cache/files
Clearing cache (cache-dir): /tmp/cache
All caches cleared.
Reading ./composer.json
Loading config file ./composer.json
Checked CA file /etc/ssl/certs/ca-certificates.crt: valid
Executing command (/app): git branch --no-color --no-abbrev -v
Executing command (/app): git describe --exact-match --tags
Executing command (/app): git log --pretty="%H" -n1 HEAD
Executing command (/app): hg branch
Executing command (/app): fossil branch list
Executing command (/app): fossil tag list
Executing command (/app): svn info --xml
Failed to initialize global composer: Composer could not find the config file: /tmp/composer.json
To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section
Running 1.8.6 (2019-06-11 15:03:05) with PHP 7.3.8 on Linux / 4.9.184-linuxkit
Reading ./composer.lock
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Reading ./composer.lock
Resolving dependencies through SAT
Looking at all rules.

Dependency resolution completed in 0.000 seconds
Analyzed 43 packages to resolve dependencies
Analyzed 43 rules to resolve dependencies
Package operations: 1 install, 0 updates, 0 removals
Installs: psr/log:1.0.0
  - Installing psr/log (1.0.0): Downloading https://api.github.com/repos/php-fig/log/zipball/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0
Downloading (connecting...)
Following redirect (2) https://codeload.github.com/php-fig/log/legacy.zip/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0
Downloading https://codeload.github.com/php-fig/log/legacy.zip/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0
Downloading (100%)Writing /tmp/cache/files/psr/log/6e79f232da13c50e0fd07e74eb2d58c350e71a60.zip into cache from /app/vendor/psr/log/4ff496e542e24af2efd56eaf051e132b

 Extracting archiveExecuting command (CWD): unzip -qq  '/app/vendor/psr/log/4ff496e542e24af2efd56eaf051e132b' -d '/app/vendor/composer/9c2feb29'
    REASON: Required by the root package: Install command rule (install psr/log 1.0.0)

Generating autoload files

您可以看到它在提交哈希 a7ab552fdb2efb80aeca09da3bbd9335fc945ff0 处安装了库,请相信 composer.lock 的说明。

- Installing psr/log (1.0.0): Downloading https://api.github.com/repos/php-fig/log/zipball/a7ab552fdb2efb80aeca09da3bbd9335fc945ff0

事实上有一个陷阱,因为锁定文件说这个提交 版本 1.0.0 它提示我它安装了那个版本的包, 但这是一个小问题。