ぽかぽかコード日和

とっても暑い夏の日にプログラミングはじめました☀️

【Security】OSコマンド・インジェクション+Laravel実装

概要

脆弱性のあるWebアプリケーションの入力領域(入力フォーム等)に、
攻撃者が悪意のあるOSコマンドを紛れ込ませ、Webサーバー上で不正に実行する攻撃手法。


発生しうる脅威
  • 重要情報の漏洩
    サーバーに格納されている重要な情報が盗まれる。企業の信頼を大きく損なうことにもつながる。

  • ファイルの改ざん
    ファイルの書き換え、作成、削除などをされることにより、サービスの信頼性が損なわれる。

  • 不正なシステム操作
    意図しないOSのシャットダウンやユーザーアカウントの追加や削除をされる。

  • 不正なプログラム(マルウェア)への感染
    ウイルスの感染やバックドアランサムウェアなどにつながる。

  • 攻撃の踏み台にされる
    サーバー自体を乗っ取られ、他サイトへ大量のリクエストを送信するなど、さまざまな攻撃に悪用される。


/opt/myapp/bin/add_user $data
↓
/opt/myapp/bin/add_user yamada; rm -rf /

☀︎$dataに「yamada; rm -rf /」と入力されることで、
「add_user yamada;」コマンドを実行した後、「rm -rf /」でルート以下のディレクトリ(システム全体)の強制削除ができ、壊滅的な被害が発生する。

  • ls、cat、rm、chmod、mv、echoコマンドなどファイル操作系や、ファイルのダウンロード機能を持つcurlコマンドにも悪用できる。「cat /etc/passwd」でパスワード閲覧できる。

原因

ユーザーからの入力をそのままOSコマンドに受け渡す実装。

  • 外部プログラムを呼び出し可能な関数を使っている。
  • ユーザーが入力した内容をチェックしていない。

対策

根本的対策

1.OSコマンド(シェル)を実行する機能を持つ関数の利用を避ける

そもそも関数がOSコマンドを直接実行する機能を持っていなければ、攻撃は成立しない。

ファイルの名称を変更したいときは、
exec()ではなくOSコマンドを実行しないrename()など代替の関数を利用する。

保険的対策

どうしてもOSコマンドを実行する機能を持つ関数を利用したい場合

2.その引数を構成する全ての変数に対してチェックを行い、あらかじめ許可した処理のみ実行する
  • バリデーション処理を使う
    ホワイトリスト方式とはその引数に許可する文字の組み合わせを決めておき、それ以外は許可しない方式。
    数字以外を入力することのない引数であれば、「数字のみからなる文字列であること」をチェックする。

  • エスケープ処理をする
    escapeshellarg()などを使って特別な意味を持つ文字を変換し、無害化(サニタイジング)する。

3.WAF(Web Application Firewall)の導入

OSコマンド・インジェクションを含むさまざまな攻撃を検知して自動的にブロックする。
しかし誤検知(False Positive)のリスクがあり、また多重エンコードなど検知の隙間を突いた攻撃や未知の攻撃手法に対しては完全に防げないため、他のセキュリティ対策も合わせて実施する。

4.定期的な脆弱性診断の実施

Webアプリケーションの脆弱性を早期に発見・修正する。

# 脆弱性が含まれているコード
exec("rename " . $oldFileName . " " . $newFileName);

// 1.代替の関数を使う方法
rename($oldFileName , $newFileName);

// 2.エスケープ処理を使う方法
exec("rename " . escapeshellarg($oldFileName). " " . escapeshellarg($newFileName));

Laravelでの実装例

1.Storageファサードなど直接実行しない関数を使う

Laravelでは、Storageファサードを使うとファイル操作をOSコマンドなしで実行できる。

Storage::move($oldFileName , $newFileName);

☀︎ファイル名を変更できる。


2.バリデーションとエスケープ処理をする

どうしてもOSコマンドを実行する機能を持つ関数を利用したいときは、

  • バリデーション処理(validate関数)

PostController.phpファイルで

public function store (Request $request) {
    $validated = $request->validate([
        'title' => 'required|max:20',
        'body' => 'required|max:400',
    ]);
}

☀︎タイトルが20文字以下、本文が400文字以下であることを確認し、
指定する値以外はエラーとなり通過できないようにする。
required属性で必須入力として、空欄であってもエラーとなる。

  • エスケープ処理(escapeshellarg関数)

escapeshellarg()は、
引数の特殊文字などを安全な文字列として自動的に変換してくれるPHPの関数。


参考にしたサイト