Edited at

Rustの依存関係の信頼性を検証する (crev)


cargo-crevとは

最近、複数のRubyライブラリにバックドアが発見されました。このような事件はnode/npmPython/PyPIなどでも発生しています。これらは正当な作者のアカウントがハックされてアップロードされているケースや、はじめから悪意のある作者が公開しているケースなどがありますが、いずれのケースでも公開されているライブラリバージョンの安全性が検証されていないことが問題であるといえます。

これに対してRustのcrates.ioに関してもコミュニティーから懸念が表明されています (URLO18860, URLO29989) が、その中でdpc氏が提案しているのが分散コードレビューによる信頼性の担保です。これを実際に実装したのがcargo-crevというツールです。

(なお、RustSecチームがcargo-auditというツールを公開していますが、これは既知の脆弱性データベースに基づいて脆弱性を調査するもので、悪意あるライブラリを事前に検出する能力はないと思います。もちろん、すでに発覚している悪意あるバックドアは脆弱性データベースに含まれるかもしれません。)

本ツールはコミュニティーに十分な数のレビュアーがいることが前提のエコシステムであり、うまくいくかは現時点では定かではありません。しかし試してみる価値はあると思います。

以下はcargo-crevのgetting_started.md (最新版はここ)をもとに試してみたものです。


cargo-crevのインストール

cargo install cargo-crev でも入りますが、コンパイル済みバイナリを自前で入れるのが手っ取り早いです。

# 記事執筆時点での最新版 (v0.8.0) のx86_64-linux-musl用バイナリ

wget https://github.com/dpc/crev/releases/download/cargo-crev-v0.8.0/cargo-crev-v0.8.0-x86_64-unknown-linux-musl.tar.gz
tar xvf cargo-crev-v0.8.0-x86_64-unknown-linux-musl.tar.gz
mv cargo-crev-v0.8.0-x86_64-unknown-linux-musl/cargo-crev ~/.cargo/bin/
rm -r cargo-crev-v0.8.0-x86_64-unknown-linux-musl*


既存レビューに対して検証する

cargo-crevは分散レビューシステムです。あなたの判断で、誰のレビューを信頼するか決める必要があります。Google等でcrev-proofsと検索すると、公開されているレビューデータを探すことができます。また、crevのWikiに誰でも追加できるレビュアー一覧ページがあります。

ここではcargo-crev作者自身によるレビューを入れてみます。

$ cargo crev fetch url https://github.com/dpc/crev-proofs

複数のレビューによって検証されることが前提のシステムですが、dpc氏は他の何人かのレビュアーを推移的に信頼しているので、これである程度のレビューが得られます。 (とはいえ、現実のコードの信頼性を担保するには数は全く足りません)

ここで、依存関係を検証したいプログラムのリポジトリに移動して以下のように実行します。

$ cd path/to/your/repo

$ cargo crev verify
status reviews downloads own. issues lines geiger flgs crate version latest_t
none 0 0 409422 3256532 1/2 0/0 14384 329 rayon 1.1.0
none 0 0 5548530 10466852 2/6 0/0 514 0 unicode-xid 0.1.0
none 0 0 205077 3346217 1/2 0/0 341 0 CB rand_chacha 0.2.0
none 0 0 4531202 4836081 0/1 0/0 1040 0 semver-parser 0.7.0
none 0 0 1960554 3463301 0/1 0/0 1481 187 lock_api 0.1.5
none 0 0 73428 118720 0/2 0/0 70 0 actix-threadpool 0.1.1
none 0 0 495748 495809 0/3 0/0 90 0 http-body 0.1.0
none 0 2 87273 889625 0/1 0/0 14112 0 trust-dns-proto 0.7.4
none 0 0 1439379 2840204 1/1 0/0 1464 154 miow 0.2.1
none 0 0 2518557 4449315 3/3 0/0 1570 359 net2 0.2.33
none 0 0 381246 4810599 0/1 0/0 2845 349 CB parking_lot 0.8.0
none 0 0 41920 430748 0/3 0/0 568 0 dotenv 0.14.1
none 0 0 125348 573958 0/4 0/0 7196 0 aster 0.41.0
none 0 0 413269 1003049 0/3 0/0 278 0 lru-cache 0.1.2
none 0 0 3225725 3830376 0/1 0/0 275 75 iovec 0.1.2
none 0 0 249256 637805 0/1 0/0 850 59 cexpr 0.2.3
none 0 0 1232739 2134119 0/1 0/0 218 0 block-buffer 0.3.3
(略)
none 0 0 486448 1748896 0/2 0/0 796 2 CB core-foundation-sys 0.6.2
none 0 0 203174 1875924 0/4 0/0 27793 37 syntex_syntax 0.58.1
none 0 0 2318532 8372160 2/3 0/0 516 41 rand_core 0.3.1
none 0 0 71090 751351 0/3 0/0 6721 4 deflate 0.7.20
none 1 1 2485664 4939200 0/1 0/0 126 11 nodrop 0.1.13
none 0 0 3205654 15956485 3/3 0/0 643 0 bitflags 0.9.1

各列は以下のような意味のようです。


  • digest: 検証対象クレートのハッシュ値。 (-v 指定時)

  • status: 検証結果。none=十分なレビューがない・warn=注意あり・pass=十分なレビューがあった

  • reviews:


    • 左: このバージョンに対するレビューの個数。

    • 右: このクレートの全てのバージョンに対するレビューの個数。



  • downloads:


    • 左: このバージョンのダウンロード数

    • 右: このクレートの全てのバージョンのダウンロード数



  • own.:


    • 左: 信頼できるオーナーの数

    • 右: オーナーの数



  • issues:


    • 左: 信頼できるレビュアーからの注意情報の個数

    • 右: 注意情報の個数



  • lines: クレートの行数 (CLOC)

  • geiger: クレート中のunsafeコードの行数

  • flags: 特殊なパッケージである場合


    • "CB": カスタムビルドスクリプト (build.rs) が使われている



  • crate: クレートの名前

  • version: クレートのバージョン

  • latest_t (latest_trusted_version): 一番直近で、十分なレビューがあるバージョン

左端のステータスを全て "pass" にするのが我々の目標ということになります。これをpassにするための基準 (信頼度の算出方法) はある程度カスタマイズできるようなので、気になる人は cargo crev verify --help を見てみてください。)


自分でレビューする

信頼できるレビューがなければ、自分でレビューすればよいわけです。それを公開することで、コミュニティーへの貢献にもなります。


リポジトリ作成

GitHubに crev-proofs リポジトリを作ります。masterブランチを作っておきます。

$ mkdir crev-proofs && cd crev-proofs

$ git init
Initialized empty Git repository in .../crev-proofs/.git/
$ git commit --allow-empty -m "Initial commit"
[master (root-commit) 4f6e0bd] Initial commit
$ git push -u origin master
Counting objects: 2, done.
Writing objects: 100% (2/2), 169 bytes | 169.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To github.com:qnighy/crev-proofs.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
$ hub create
Updating origin
created repository: qnighy/crev-proofs

以下、cargo-crevはこのリポジトリの複製を $HOME/.config/crev/proofs 以下に作って自動的にそこで作業します。今作った crev-proofs ディレクトリは消してしまって大丈夫です。


CrevID作成

cargo crev new id --url コマンドでCrevIDを使えます。慣習にそったリポジトリ名の場合は --github-username オプションが使えます。

$ cargo crev new id --github-username qnighy

https://github.com/qnighy/crev-proofs cloned to $HOME/.config/crev/proofs/******
CrevID will be protected by a passphrase.
There's no way to recover your CrevID if you forget your passphrase.
Enter new passphrase: ******
Enter new passphrase again: ******
Your CrevID was created and will be printed below in an encrypted form.
Make sure to back it up on another device, to prevent loosing it.

---
version: -1
url: "https://github.com/qnighy/crev-proofs"
public-key: ******
sealed-secret-key: ******
seal-nonce: ******
pass:
version: 19
variant: argon2id
iterations: 192
memory-size: 4096
lanes: 4
salt: ******

これで、自分のレビューを入れるためのバケットのようなものが作られました。このID (CrevID) は以下のようにして確認できます。

$ cargo crev query id current

fkkJ3Z6xSDEZwFlyKgIDxYS2x6mS7b2cxbWBeEWgfXs https://github.com/qnighy/crev-proofs


推移的な信頼の表明

他のレビュアーに対する信頼を表明することができます。たとえば以下のようにするとdpc氏を信頼できます。

$ cargo crev trust FYlr8YoYGVvDwHQxqEIs89reKKDy-oWisoO0qXXEfHE

「どれくらい信頼するか」「信頼する理由等のコメント」などの情報を記入して保存します。(信頼度は、たとえば推移的な信頼をどこまで辿るかの判定に使われます。)

cargo crev query id trusted で、この信頼をもとに推移的に信頼できるユーザーのリストを出すことができます。これらのユーザーのレビューを、 cargo crev fetch trusted で取得できます。


レビュー作業の結果を公開する

まだ具体的なレビューはしていませんが、少し作業をしたので保存方法を確認しておきます。

自分のレビュー作業の結果は以下のようにしてpushできます。これは cargo crev git push HEAD の省略コマンドで、さらに言うとこれは $HOME/.config/crev/proofs/${CREV_ID} 以下に移動して git を実行するのと同じことです。

$ cargo crev push


実際にレビューしてみる

もう一度、Rustコードのあるリポジトリで cargo crev verify をしてみます。「passになっていない」かつ「行数の少ないコード」を選んでレビューしてみましょう。今回ぼくは、以前パッチを投げたことがあるsignal-hook-registryを対象にレビューしてみたいと思います。

cargo crev goto または cargo crev open を使うことで、当該クレートのソースを閲覧することができます。前者はシェルが立ち上がり、後者は指定したプログラムが立ち上がります。

# VSCodeでsignal-hook-registryのソースを見る

$ cargo crev open signal-hook-registry --cmd "code --wait -n" --cmd-save

(ただ、IDEで開いた場合、 $HOME/.cargo/registry 以下をrlsの出力で汚すことになります。これが果たしてOKなのかどうかはよくわからない…… ←おそらく終了後に .crate ファイルから再展開するか何かしているので大丈夫そうです。)

クレートを一通りチェックしたら、以下のコマンドでレビューを作成します。

# signal-hook-registryに対するレビューを作成する

$ cargo crev review signal-hook-registry

レビューファイルには以下のような属性が含められます。もちろん、すべて自己申告です。


  • レビューの網羅性 (どれくらいしっかりソースを見たか)

  • 理解度 (プログラムの動作をどれくらい理解してレビューしたか)

  • レーティング

  • アドバイザリ (当該バージョンには、今後修正される問題がある)

  • イシュー (より一般的に何かの問題について記録する場所)

  • コメント

レビューを書いたら、再び cargo crev push によって作業結果を公開できます。


何をレビューするべきか

cargo-crevは「何をレビューするべきか」については意図的に各レビュアーにある程度の判断を委ねているようです。crevの主な動機から考えると、第一には悪意あるコードが混入されていないかを判断した上で、悪意によらないセキュリティー上の問題などをついでに検証するのがよいのではないかと思います。


まとめ

分散レビューのエコシステムがうまくいくかは現時点では定かではありませんが、少なくともそのベースとなるツール群は(計画の初期段階としては)思いのほか良くできていました。もちろん、まだ使い勝手に改善の余地はあるので、その部分は今後に期待します。

この試みによって、Rustがより広い意味での「安全な言語」に近づくことができるのではないかと思います。このエコシステムが成功するにはより多くの人の強力が必要なので、志あるRustプログラマーの皆さんにも、ぜひ一度は試してみてほしいと思いました。