変数に有効なファイル名のみが含まれていることをどのように安全に確認できますか?

変数に有効なファイル名のみが含まれていることをどのように安全に確認できますか?

次のスクリプトが与えられたら、パラメータにパス()やワイルドカードなどが含まれて/home/charlesingalls/おらず、有効なファイル名のみが含まれているかどうかを確認できますか?../home/carolineingalls/

私はスクリプトが与えられたハードコーディングされたディレクトリから単一のファイルを削除できるようにしたいと思います。スクリプトは特権ユーザーとして実行されます。

#!/bin/bash

rm -f /home/charlesingalls/"$1"

答え1

この回答では、$1サブディレクトリを含めることが許可されていると仮定します。単純なディレクトリ名である必要があるより簡単なケースに興味がある場合は、$1他の答えの1つを参照してください。


二重引用符で囲まれたワイルドカード文字は拡張されません。 isは二重引用符で囲まれているので、$1ワイルドカードは問題になりません。

シンボリックリンクは../すべてマスクできます。本物ファイルの場所です。以下に示すテストは、ファイルが実際に(表面にあるのではなく)目的のパスにあることを確認するテストです。

最新システム:使用realpath

ファイルが実際に存在することを確認するには、次のように /home/charlesingalls/しますrealpath

realpath --relative-base=/home/charlesingalls/ "/home/charlesingalls/$1"  | grep -q '^/' && exit 1

exit 1上記のコードは、指定されたファイルが$1ディレクトリの外部にある場合に実行されます/home/charlesingalls/realpathシンボリックリンクと../

realpathこれはGNU coreutilsの一部であり、すべてのLinuxシステムで使用できます。

realpath必要GNU coreutils 8.15(2012年1月)以上

はい

realpathがファイルの実際の場所をどのように../決定するかを示します(たとえば、-qgrepの実際の出力が表示されるようにgrepオプションを省略します)。

$ touch /tmp/test
$ realpath --relative-base=$HOME "$HOME/../../tmp/test" | grep '^/' && echo FAIL
/tmp/test
FAIL

シンボリックリンクに従う方法を示します。

$ ln -s /tmp/test ~/test
$ realpath --relative-base=$HOME "$HOME/test" | grep '^/' && echo FAIL
/tmp/test
FAIL

既存システム:使用readlink -e

readlinkまた、パスをコーンにし、シンボリックリンクをたどることもできます../

readlink -e "$HOME/test" | grep -q "^$HOME" || exit 1

同じサンプルファイルを使用して:

$ readlink -e "$HOME/../../tmp/test" | grep "$HOME" || echo FAIL
FAIL
$ readlink -e "$HOME/test" | grep "^$HOME" || echo FAIL
FAIL

以前のGNUシステムで利用可能なものに加えて、readlinkBSDでもバージョンを使用できます。

答え2

その中にあるファイルだけを削除し/home/charlesingalls(サブディレクトリのファイルは削除しないように)簡単です。買収に/

case "$1" in
  */*) echo 1>&2 "Refusing to remove a file in another directory"; exit 2;;
  *) rm -f /home/charlesingalls/"$1";;
esac

rm.引数がORまたは空の場合でもコマンドは実行されますが、..この場合、rmディレクトリの削除は破損せずに失敗します。

ここではワイルドカード拡張は行われないため、ワイルドカードは関係ありません。

これはシンボリックリンクが存在しても安全です。ファイルがシンボリックリンクの場合、シンボリックリンク(/home/charlesingalls)は削除され、リンク先は影響を受けません。

/home/charlesingallsこれは移動または変更できないと仮定します。ディレクトリがスクリプトにハードコーディングされている場合は問題ありませんが、変数に基づいて決定されると、コマンドが実行されたrmときにその決定が無効になる可能性があります。

に基づいて追加情報パラメータが仮想ホスト名の場合は、ブラックリストではなくホワイトリストに追加する必要があります。名前がスラッシュを許可しないのではなく、合理的な仮想ホスト名であることを確認してください。名前が小文字または数字で始まるか、小文字、数字、ドット、ダッシュ以外の文字が含まれていないことを確認してください。

LC_CTYPE=C LC_COLLATE=C
case "$1" in
  *[!-.0-9a-z]*|[!0-9a-z]*) echo >&2 "Invalid host name"; exit 2;;
  *) rm -f /home/charlesingalls/"$1";;
esac

答え3

パスを完全に無効にする場合は、最も簡単な方法は変数にスラッシュ(/)が含まれているかどうかをテストすることです。バッシュから:

if [[ "$1" = */* ]] ; then...

ただし、これにより、を含むすべてのパスがブロックされますfoo/bar。代わりにこれをテストできますが、..これはターゲットパスの外にあるディレクトリを指すシンボリックリンクを引き起こす可能性があります。

単一ファイルの削除のみを許可するにはrm -r


また、実行している操作に応じて、システムのファイル権限を使用して、ユーザーが直接削除できるファイルのみを削除できるようにすることもできます。このような:

su charlesingalls -c "rm /home/charlesingalls/'$1'"

@Gillesが述べたように、ここに引用の問題があります。一重引用符が含まれている場合は失敗するため、最初に変数を$1テストする必要があります(たとえば、if [[ "$1" = *\'* ]] ; then fail...合理的な文字セットをホワイトリストに追加するなど)、またはファイル環境変数を使用して、

file="$1" su charlesingalls -c 'rm "/home/charlesingalls/$file"'

関連情報