はじめに
この実験 (Lab) では、2 つの基本的なセキュリティ概念、入力検証 (input validation) とコード整合性 (code integrity) を探求します。入力検証とは、プログラムが受け取るあらゆる入力が、処理される前に安全で適切に整形されていることを保証する実践です。これは、コマンドインジェクションを含む幅広い攻撃に対する重要な防御策となります。コード整合性とは、アプリケーションコードが不正な当事者によって改変または破損されていないことを検証することを含みます。
これらの概念を実践的なアプローチで学びます。まず、意図的にコマンドインジェクションに対して脆弱なシンプルなシェルスクリプトを作成します。次に、この脆弱性を悪用してリスクを理解します。その後、適切な入力サニタイゼーションを実装してスクリプトを保護します。最後に、スクリプトの整合性を検証するために暗号学的ハッシュ (cryptographic hashes) を使用する方法を学び、改ざんされていないことを保証します。
ユーザー入力を含む簡単なスクリプトを作成する
このステップでは、ユーザーにファイル名を尋ね、そのファイルに関する詳細情報を ls -l コマンドを使用して表示する、シンプルな Bash スクリプトを作成します。この初期バージョンでは、セキュリティチェックは一切行われません。
まず、nano エディタを使用して、~/project ディレクトリに check_file.sh という名前の新しいファイルを作成します。
nano ~/project/check_file.sh
次に、以下のコードをファイルに追加します。このスクリプトはファイル名の入力を求め、その入力を filename という変数に読み込み、その後 eval コマンドを使用してそのファイル名に対して ls -l を実行します。ユーザー入力と共に eval を使用することは、深刻なセキュリティ脆弱性であることを覚えておいてください。
#!/bin/bash
echo "Please enter a filename to check:"
read filename
echo "Checking details for: $filename"
eval "ls -l $filename"
ファイルを保存して nano を終了するには、Ctrl+X、次に Y、そして Enter を押します。
次に、スクリプトを実行できるように実行可能にする必要があります。chmod コマンドを使用して実行権限を追加します。
chmod +x ~/project/check_file.sh
それでは、スクリプトを実行して動作を確認しましょう。実験環境で用意された testfile.txt を使用します。
./check_file.sh
スクリプトは入力を求めてきます。testfile.txt と入力して Enter を押してください。
Please enter a filename to check:
testfile.txt
Checking details for: testfile.txt
-rw-rw-r-- 1 labex labex 0 Aug 4 15:19 testfile.txt
ユーザー入力を受け取るシンプルなスクリプトを正常に作成し、実行しました。次のステップでは、このスクリプトがどのように悪用されるかを見ていきます。
基本的なコマンドインジェクション攻撃をシミュレートする
このステップでは、前のステップで作成したスクリプトがコマンドインジェクション攻撃に対してどのように脆弱であるかを確認します。コマンドインジェクションとは、攻撃者が脆弱なアプリケーションを通じて、ホストオペレーティングシステム上で任意のコマンドを実行できる状態を指します。
私たちのスクリプトが脆弱である理由は、ユーザー入力 ($filename) を含むコマンドを、まず入力が安全かどうかを確認せずに実行するために eval を使用しているためです。eval コマンドは、文字列全体をそのまま実行されるコマンドとして扱うため、特に危険です。攻撃者は、追加のシェルコマンドを含む入力を提供することで、これを悪用できます。
スクリプトを再度実行してみましょう。
./check_file.sh
今回は、ファイル名を尋ねられた際に、以下の文字列を入力してください。セミコロン (;) はシェルにおいてコマンドを区切る特別な文字です。
testfile.txt; whoami
Enter を押すと、以下の出力が表示されます。
Please enter a filename to check:
testfile.txt; whoami
Checking details for: testfile.txt; whoami
-rw-rw-r-- 1 labex labex 0 Aug 4 15:19 testfile.txt
labex
何が起こったかに注目してください。スクリプトはまず、予想通り ls -l testfile.txt を実行しました。しかし、セミコロンと eval の使用により、シェルは次に whoami コマンドを実行し、現在のユーザー名 (labex) を表示しました。これは、コマンドインジェクションが成功したことを示しています。eval コマンドは、入力全体を実行可能なコードとして扱うことで、これを可能にしました。攻撃者はこの脆弱性を利用して、はるかに危険なコマンドを実行できます。
注意: もし eval を使用せずに ls -l $filename を使用した場合(元のバージョンにあったように)、攻撃は成功しません。なぜなら、シェルは testfile.txt; whoami を ls の別々の引数として扱い、「cannot access 'testfile.txt;'」や「cannot access 'whoami'」のようなエラーメッセージが表示されるからです。この例でコマンドインジェクションを可能にしているのは eval コマンドです。
インジェクションを防ぐための入力サニタイズを実装する
このステップでは、ユーザー入力をサニタイズし、危険な eval コマンドを削除することで、コマンドインジェクション攻撃を防ぐようにスクリプトを変更します。入力サニタイズとは、潜在的に悪意のある文字を削除または無効化するために、入力をクリーニングまたはフィルタリングするプロセスです。
私たちの戦略は以下の通りです。
- 危険な
evalコマンドを削除し、適切な引用符を使用した直接的なコマンド実行を使用する - 入力されたファイル名が許可された文字のみを含んでいるかを確認する。典型的なファイル名の場合、文字(英字、数字、アンダースコア、ハイフン、ピリオドなど)のホワイトリストを作成できます。セミコロンのような他の文字を含む入力を拒否します。
nano を使用して check_file.sh スクリプトを再度開きます。
nano ~/project/check_file.sh
正規表現を使用した検証チェックを含めるようにスクリプトを変更します。既存の内容を以下のコードに置き換えてください。
#!/bin/bash
echo "Please enter a filename to check:"
read filename
## Input sanitization: only allow alphanumeric characters, underscores, hyphens, and dots.
if [[ "$filename" =~ ^[a-zA-Z0-9_.-]+$ ]]; then
echo "Checking details for: $filename"
ls -l "$filename"
else
echo "Error: Invalid filename. Malicious input detected."
fi
主な変更点は以下の通りです。
- 危険な
evalコマンドの削除 lsコマンドでの"$filename"の周りへの適切な引用符の追加- 入力検証を含む
ifステートメント。式[[ "$filename" =~ ^[a-zA-Z0-9_.-]+$ ]]は、filename変数が指定されたセット内の文字のみを最初 (^) から最後 ($) まで含んでいるかを確認します。
変更を保存するには、Ctrl+X、Y、そして Enter を押します。
それでは、同じ攻撃を再度試してみましょう。スクリプトを実行します。
./check_file.sh
悪意のある入力 testfile.txt; whoami を入力して Enter を押します。
Please enter a filename to check:
testfile.txt; whoami
Error: Invalid filename. Malicious input detected.
ご覧のように、スクリプトは現在無効な文字を検出し、悪意のある whoami コマンドを実行する代わりにエラーメッセージを表示します。攻撃は正常に防止されました。
ハッシュによるスクリプトの整合性を検証する
このステップでは、暗号化ハッシュを使用してスクリプトの整合性を検証する方法を学びます。これにより、スクリプトを保護した後、攻撃者によって変更または改ざんされていないことを確認できます。ここでは、SHA-256 ハッシュを計算する sha256sum ユーティリティを使用します。
まず、安全な check_file.sh スクリプトのハッシュを生成し、ファイルに保存しましょう。このファイルは「既知の良好な」署名として機能します。
sha256sum ~/project/check_file.sh > ~/project/check_file.sha256
このコマンドは check_file.sh の SHA-256 ハッシュを計算し、その出力を check_file.sha256 という新しいファイルにリダイレクトします。このファイルのコンテンツを表示してみましょう。
cat ~/project/check_file.sha256
ファイル名に続く長い文字列が表示されます。この文字列はスクリプトの一意のハッシュです。
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 /home/labex/project/check_file.sh
(注意:あなたのハッシュ値は異なる場合があります。)
次に、不正な変更をシミュレーションしましょう。スクリプトの末尾に簡単なコメントを追加します。このような小さな変更でも、ハッシュは完全に変わるはずです。
echo "## A harmless comment" >> ~/project/check_file.sh
これでスクリプトは変更されました。その整合性を確認するために、-c (check) オプション付きの sha256sum を使用できます。これは、署名ファイルからハッシュを読み取り、スクリプトの現在のハッシュと比較します。
sha256sum -c ~/project/check_file.sha256
ファイルが変更されたため、コマンドは失敗を報告します。
/home/labex/project/check_file.sh: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
これにより、スクリプトの整合性が侵害されたことが確認されます。この手法は、実行しているコードが信頼できるコードであることを保証するために不可欠です。
まとめ
この実験を完了された皆さん、おめでとうございます!重要なセキュリティ原則を 2 つ、実践的に体験することができました。
入力サニタイズを実装することで、コマンドインジェクションの脆弱性を特定し、防止する方法を学びました。許可された文字のホワイトリストに対してユーザー入力を検証することで、脆弱なシェルスクリプトを正常に保護しました。
また、暗号化ハッシュを使用してコードの整合性を確保する方法も学びました。スクリプトの SHA-256 チェックサムを生成することで、不正な変更を検出できる検証可能な署名を作成することができました。
これらのスキルは、安全なコードを作成し、あらゆるシステムのセキュリティを維持するための基本となります。



