2012-03-03 16 views
3

競合状態からのシェル(またはbash)のコマンドリストの安全性はどれくらいですか?シェルコマンドリストの競合条件

if [ -h "$dir" ]; then 
    echo 'Directory exists and is a symlink' 
    exit 1 
fi 
cd "$dir" 

上記のコードは明らかに競合状態になりやすいです:攻撃者がチェックした後、まだそれにディレクトリを変更する前にシンボリックリンクを作成することができます。

||コマンドリストにも同じことが適用されますか?言い換えれば、以下のコマンドは競争条件に影響されないか、上記と同じルールが適用されますか?エラーメッセージが表示されて

[ -h "$dir" ] || cd "$dir" 

:同じ競合状態に対して脆弱ではないでしょう||を使用して、なぜ

[ -h "$dir" ] 
    && { echo 'Directory exists and is a symlink'; exit 1; } 
    || cd "$dir" 
+0

あなたは、攻撃者がシンボリックリンクを持つ既存のディレクトリを_replace_かもしれない、またはそのS /彼はシンボリックリンクを_create_、またはその両方かもしれないと懸念していますか? –

+0

-hに関するテスト・マンページから:この演算子は、このプログラムの以前のバージョンとの互換性のために保持されます。その存在に頼らないでください。代わりに-Lを使用してください。 –

+0

@WilliamPursell:興味深い... sh(1)の私のマンページはちょうど反対です:»-L fileファイルが存在し、シンボリックリンクであれば真です。この演算子は、このプログラムの以前のバージョンとの互換性のために保持されます。その存在に頼らないでください。代わりに-hを使用してください。«man bash(1)は2つの間に違いは何も言及していません。 – knittl

答えて

0

私は理由を見ません。それが脆弱であることがないため実際に、それは使用して実装する必要があるであろういずれか

  • 原子STAT-と-のchdirファイルシステムの操作のいくつかの種類(私はかなり確信して存在していない)、または

  • cdの後に何らかの二重チェックをして、ディレクトリが以前と同じ場所を指していることを確認してください。

私はbashの専門家ではありませんが、どちらもそうではありません。

+0

ダブルチェックは良いアイデアのように聞こえ、おそらく唯一の安全な方法です。 – knittl

+2

私はそれも行く方法だと思う。ただし、元のディレクトリパスが同じ場所に解決されていることを確認するだけでは不十分であることに注意してください。誰かがそれを変更した可能性があります。その代わりに、あなたが実際に変更したディレクトリを 'stat'して、それが本来statと同じディレクトリであることを確認する必要があります。 –

+1

私はそれを正しい方法で行うことを確認してくれてありがとう。私はすでにinodeを比較しています: 'stat -c%i dir && cd dir && stat -c%i ' &&両方のinodeを比較する。私はパスが別の競合条件を導入することなく同じディレクトリに解決するかどうかをチェックする方法を知りません;) – knittl

3

これは素晴らしい教育問題ですが、実際には間違った問題を解決しています。 Linuxセキュリティモデルは、並行処理の問題をスクリプト化するのではなく、ファイルシステムのアクセス権に依存しています。

攻撃者が実行中のプログラムの下で作業環境を変更する可能性がある場合は、環境を保護する必要があります。実行するスクリプトとバイナリへの書き込みアクセスのみを許可し、あなただけが書き込むことができる場所にのみ存在します。

setuidビットを使用すると、通常のユーザーは昇格された特権でそれらを実行できますが、この方法で実行されるプロセスは慎重にデバッグし、セキュリティの問題をチェックする必要があります。ほとんどのLinuxディストリビューションでは、このような方法でスクリプトを実行することはできません。

あなたの(またはルート)パスワードを取得した攻撃者は、このや他のセキュリティ対策を簡単に打ち負かすことができますが、その場合には大きな問題を抱えていることに言及する価値があります。 :-)

幸運!

+0

あなたの答えをありがとう。問題のスクリプトはsetuidとして実行され、最初の引数としてディレクトリに渡され、そのディレクトリに 'cd'してから' chmod' + 'chown'タスクを実行します。 (そして、誰かに許可されていないファイルに対してそのタスクを実行させたくない) – knittl

+1

おそらく、別のプログラミング言語を使う方がよいでしょう。 –

+0

代わりにchownコマンドに特別なものがある場合は、限られた数のUnixユーザー間ですべての変更が行われている可能性があります。代わりに 'chown'をスクリプトの' sudo chown'に変更してください。 –

3

シンボリックリンクを使用せずにディレクトリを変更するには、2つのセキュアでポータブルな方法しかありません。また、シェルスクリプトでは簡単に実行できません。

「foo」に安全にchdirしようとしていることを前提としています。最初の方法は、現在のディレクトリをopen(".", O_RDONLY),lstat() "foo"ディレクトリのファイル記述子に保存し、結果としてst_devst_inoという値を記録し、chdir( "foo")を呼び出してからstat() "。結果のst_devst_inoの値を比較してください。彼らが同じなら、あなたはレースに勝った。そうでない場合は、保存したfdを使用してfchdir()というエラーメッセージを出してから、中止するか、もう一度やり直してください。

秒未満、移植可能な方法は、fd = open("foo", O_RDONLY|O_NOFOLLOW)、次いでfchdir(fd)を使用することです。 openの代わりにopenatを使用することもできます。移植性の問題は、すべてのシステムにO_NOFOLLOWがあるわけではなく、一部の古いカーネルはそのフラグを正しく解釈しない(セキュリティ上の問題ではなく無視する)ということです。詳細については

は見つけるGNUのソースコードを見てみましょう、私は上記のものと非常によく似た方法を使用して、この種の問題を回避するために、かなりのトラブルに行くれます。シェルスクリプトでこの問題を解決するためのよう

お使いのシステムがstat(1)コマンドやPerlのようなものを持っている場合は、STAT操作を実行するために、これらを使用することができます。結果をシェル変数に記録することができます。これは、回復するためにfchdirを使用する必要性を除いて、シェルスクリプトの最初のメソッドを多かれ少なかれ実装できることを意味します。シェルスクリプトがレースを失ったときにただちに中止することができれば、最初の方法をシェルで使用することができます。しかし、最終的にシェルで安全なコードを書くことは非常に困難です。

+0

この詳細な回答ありがとうございます。私は[既に 'stat'を使っています](http://stackoverflow.com/a/9549413/112968#comment-12103060)ディレクトリに変更した後のinodeを比較する – knittl

0

一般的に使用すると、シェルスクリプトでの競合状態を避けることができない、話します。 Cを使用し、openat,fchdirなどのTOCTTOU安全なインターフェースを使用する必要があります。 TOCTTOU(レース)の脆弱性を避けることは軽微であり、慎重な検討が必要です。