pockestrap

Programmer's memo

Re: 脆弱性を見つけるコードレビュー(OSコマンドインジェクション)

脆弱性を見つけるコードレビュー(OSコマンドインジェクション) - ククログ(2017-04-27)

上記記事の内容が、ミスリーディングを誘う可能性があると思ったので、勝手に補足します。

2017/05/04 追記

元の記事の方に修正をいただきました。 そのため、以下の内容は元の記事を読んでいれば読む必要がない内容となっています。

TL;DR

外部コマンドに外部入力値を渡す際には、エスケープではなく、シェルを経由しない外部コマンド呼び出しを利用しましょう。

元記事の問題点

元記事の “3.OSのコマンドを実行する関数に与える引数が適切にエスケープされているかどうかをチェック” では、外部コマンドに引数を与える際、エスケープされているかをチェックする、と書かれています。
ですが、この方法は安全とは言い切りづらいです。

元記事でも

シェルの文字列解釈の仕様は複雑なため、各言語で用意しているエスケープの方法にもバグが存在することがあり、正しくエスケープされない可能性があります。

とある通り、この方法でOSコマンドインジェクションを完全に防げる、と断言することは難しいでしょう。
そのため、この記事にある「エスケープする」という方法では不安が残ります。 後述する通り、より安全なコマンドの実行方法があるため、この記述は不親切です。

安全な外部コマンドの実行方法

多くの言語ではエスケープに頼らず、より安全に外部コマンドを実行する方法が用意されています。
それは、「コマンドの実行をシェル経由ではなく実行する」というものです。

OSコマンドインジェクションとは

そもそもOSコマンドインジェクションはどのように行われるのでしょうか?
この脆弱性は、シェルの機能に依存しています。
例えば、以下のようにcurlコマンドを実行する場合を考えてみます。

# Ruby
url = gets         # ユーザーがURLを入力
system("curl #{url}") # ユーザーが入力した URL に GET リクエストを発行

ここで、ユーザーがhttp://example.com; rm -rf /と入力した場合を考えてみましょう。
この場合system()に渡される文字列は、curl http://example.com; rm -rf /となります。

では、この文字列中にある(スペース)や;セミコロンは誰が解釈するのでしょうか?
答えはシェルです。スペースなどがシェルによって解釈された後、curlなどのコマンドが実行されます。
あなたが普段インタラクティブなシェルに対して上記の文字列を入力した場合を想像すると、理解がしやすいと思います。

そしてシェルがスペースとセミコロンを解釈した結果、上記の場合ではcurl http://example.comrm -rf / の二つのコマンドが実行されてしまいます。

シェルを経由しない外部コマンドの実行

以上のことから「シェルが文字列を解釈する」ことでOSコマンドインジェクションが発生することが理解できたと思います。
例えば Ruby では、以下のようにすることでシェルを経由せず外部コマンドを実行することが可能です。

url = gets
system('curl', url)

上記のコードにhttp://example.com; rm -rf /を入力したとしても、curlhttp://example.com; rm -rf /をURLとして解釈しようとし、失敗します。
セミコロンをコマンドの区切り目だとは解釈しなくなり、安全になります。

PHP

PHP にはシェルを経由せず外部コマンドを実行する関数が実質的にないらしいです。 詳しくは参考リンクを参照下さい。

参考