OSコマンド・インジェクションについて

# 概要
Webサーバーにおいてシェル経由でOSコマンドを呼び出す場合、OSコマンド・インジェクション脆弱性が発生する場合があります。
このOSコマンド・インジェクションによって具体的には以下のような悪用が可能になります。
- Webサーバー内のファイル閲覧・変更・削除
- 外部へのメール送信
- 別のサーバーを攻撃
- 暗号通貨の採掘

いずれも被害が大きく、開発者はこの脆弱性を作らないような実装が求められます。

# 具体例

以下のようなお問い合わせフォームがあるとします。
![スクリーンショット 2022-08-25 1.08.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2644746/2cf8f84f-0dfe-130c-8a74-4695ac5faab0.png)
送信ボタンを押したときに、入力したメールアドレス宛に自動送信メールを送る機能が以下のように作られているとしましょう。
```html:contact.html
<body>
    <form action="sendMail.php" method="POST">
        <h2>お問い合わせ</h2>
        <p>メールアドレス</p>
        <input type="text" name="mail">
        <p>お問い合わせ内容</p>
        <textarea name="inqu" cols="20" rows="3"/></textarea>
        <input type="submit" value="送信">
    </form>
</body>
```
```php:sendMail.php
<?php
  $mail = filter_input(INPUT_POST, 'mail');
  system("/usr/sbin/sendmail -i <template.txt $mail");
  (略)
?>
<body>
  お問い合わせを受け付けました
</body>
```
```text:template.txt
From: test@example.com
Subject: =?UTF-8?B?5Y+X44GR5LuY44GR5LuY44GR44G+44GX44GF?=
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 8bit
お問い合わせを受け付けました
```
このとき、お問い合わせフォームのメールアドレス欄に以下のような入力を行います。
```
waruihito@example.com;cat /etc/passwd
```
このとき、ユーザーの画面には/etc/passwdが表示されてしまいます。
このように、「 ; 」を使って、コマンドを複文にすることによって、ユーザーは任意のシェルコマンドをアプリケーションの権限で実行できることがわかります。

# 対策
OSコマンド・インジェクション脆弱性を防ぐためには以下の対策が考えられます。
- OSコマンド呼び出しを使わない実装方法を選択する
    今回のケースではシェルのコマンドを使ってメール送信機能を作成していましたが、PHPのライブラリを使ってメール送信機能を作成すれば、OSコマンドを使う必要はなくなります。OSコマンドを使用することなく実現できる機能に関しては、OSコマンドを使用しないのがセキュリティ的には賢明です。OSコマンド呼び出しのオーバーヘッドもなくなることから、処理性能も向上します。

- シェル呼び出し機能のある関数の利用を避ける
    どうしてもOSコマンドを使用しないといけない場合は先にコマンドの形が決まっている関数を使いましょう。今回のケースでは、「 ; 」の挿入によって意図した単文のコマンドではなく、複文のコマンドが実行されてしまいました。PHPでは適当な例がありませんが、先にコマンドの形を決めてあげた上で、パラメータを個別に渡してあげる関数を使うことでOSコマンドを防ぐことができます。

- 外部から入力された文字列をコマンドラインのパラメータに渡さない
    PHPのようにOSコマンドの形を指定する関数が存在しない場合はそもそも外部から入力された文字列をコマンドに含めないようにすることが根本的な対策になります。例えば先程の例ではtemplate.txtに入力されたメールアドレスを埋め込み、それをPHPの関数を使って読み取ることでsendMailコマンドを実行すると、特殊文字によってコマンドが改変されずに済みます。

- OSコマンドに渡すパラメータを安全な関数によりエスケープする
    上記3つの方法いずれも使えない場合は「 ； 」等の文字を適切に加工する関数を用意することになります。シェルのエスケープルールは複雑なので、自作せずにライブラリを使用することが推奨されます。

根本的な対策は以上ですが、漏れがあった場合に被害を軽減する意味で以下の保険的対策を取ることが望ましいとされています。

- パラメータの検証
    外部からの入力値の検証は基本的にはユーザビリティの向上やデータ不整合の防止のために行われるものですが、シェル経由のOSコマンド実施の場合はパラメータ文字列の文字種を制限すべきです。今回の例ではメールアドレスの形式のバリデーションをかけることで防ぐことができます。

- アプリケーションの権限を適切に設定する
    万が一OSコマンド・インジェクションが発生した場合の被害を最小限にする意味で、コマンドの実行権限は最小に設定するべきです。この保険的対策は様々な脆弱性に対して有効なものなので、強く推奨されます。

- WebサーバーのOSやミドルウェアのパッチ適用
    OSコマンド・インジェクションによって最も被害を受けるケースは外部から攻撃用ツールをWebサーバー内部にインストールされ、それをもとにOSの脆弱性をつかれた場合です。OSの脆弱性によって権限昇格が発生しroot権限が乗っ取られると、サーバーのすべての操作が実行可能になります。なので、たとえ外部から攻撃を受けない脆弱性であっても、パッチ適用などの対処が推奨されています。

# 最後に
OSコマンドを使わないと実行できない機能というのは個人的にはかなり珍しいのかなと思います。
とりあえず、
___Webサーバーの実装ではOSコマンドは使用しない___
ことを守っておけばOSコマンド・インジェクション脆弱性が発生することはないでしょう。

# 参考文献
[体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 徳丸浩](https://www.amazon.co.jp/%E4%BD%93%E7%B3%BB%E7%9A%84%E3%81%AB%E5%AD%A6%E3%81%B6-%E5%AE%89%E5%85%A8%E3%81%AAWeb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9%EF%BC%BB%E3%83%AA%E3%83%95%E3%83%AD%E3%83%BC%E7%89%88%EF%BC%BD-%E8%84%86%E5%BC%B1%E6%80%A7%E3%81%8C%E7%94%9F%E3%81%BE%E3%82%8C%E3%82%8B%E5%8E%9F%E7%90%86%E3%81%A8%E5%AF%BE%E7%AD%96%E3%81%AE%E5%AE%9F%E8%B7%B5-%E5%BE%B3%E4%B8%B8-%E6%B5%A9-ebook/dp/B01MR7MOT6/ref=sr_1_5?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1JK2YW214S7JB&keywords=%E5%BE%B3%E4%B8%B8%E6%B5%A9&qid=1661182444&sprefix=%E5%BE%B3%E4%B8%B8h%2Caps%2C415&sr=8-5)