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


8

以下のスクリプトを前提として/home/charlesingalls/、パス(../home/carolineingalls/)やワイルドカードなどではなく、有効なファイル名のみが引数に含まれていることをどのように確認できますか?

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

#!/bin/bash

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

2
「foo / bar」をサポートしたくない場合は、が含まれていないことを確認してください/。ワイルドカードは引用符内では解釈されません。
Random832

3
ファイルを削除する場合は、-rと一緒に使用しないでくださいrmrm -rディレクトリとその下のすべてのファイルとディレクトリを再帰的に削除するためのものです。ディレクトリを削除する場合にのみ役立ちます。より一般的には、カーゴカルトをしないでください。つまり、有用と思われるものをコマンドラインやスクリプトにコピーするだけでなく、それらが何をするか、またはどのように機能するかを理解する必要があります。魔法の貨物を運ぶ飛行機の神々は怒り、すべてのファイルを削除する可能性があります。
cas

-rフラグに関する良い点-私はその使用を理解していますが、はっきりと考えていませんでした。
アーロンチカリ16

回答:


7

/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または..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

この場合、ファイルは、前のプロセスで生成されたWebサーバー構成ファイルです。それらはすべて同じレベルの重要度を持っています(それぞれが仮想ホストを構成します)。ただし、それらが存在するディレクトリは類似のディレクトリに隣接しており、すべてWebサーバーが所有するディレクトリの下に存在します。この特定のスクリプトは、特定のディレクトリでのみ仮想ホスト構成を削除できるようにすることを目的としています。
アーロンチカリ16

このアプローチは、そのユースケースを提供する場合に意味がありますか?私はあなたの経験に感謝します、そして私がLinuxで何かを自動化するために私が「銃撃戦にバズーカをもたらした」ことは間違いなくあることを認めます。
アーロンチカリ16

@AaronCicaliなぜスクリプトは、元のリクエストを行ったエンティティに属するものだけでなく、ホスト構成のいずれかを削除できるのですか?なぜ仮想ホスト名が最初に検証されなかったのですか(その後、特殊文字は含まれていません)?
Gilles「SO-邪悪なことをやめなさい」2016

元の要求を行うエンティティは、そのフォルダー内の仮想ホストを削除する権限を実際に持っているGUIです。仮想ホスト名が最初に検証されます。これは、以前に作成された仮想ホスト(ドメイン名)のリストから取得されます。アプリケーションの別の部分のセキュリティに依存することなく、可能な限り安全であることを確認するのがこのスクリプトの仕事だと思います。つまり、この特定のディレクトリ内からのみファイルを削除できます。また、追加のクリーンアップ作業を行う必要もあります。
アーロンチカリ16

@AaronCicali OK、追加の健全性チェックとして、これは理にかなっています。この場合、ホワイトリストに登録する必要があります。もっともらしいホスト名のように見える名前のみを受け入れてください。サブドメインを許可しない場合は、禁止することもできます.
Gilles「SO-邪悪なことをやめ

10

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


ワイルドカードは、二重引用符で囲まれている場合は展開されません。以来$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は8.15(2012年1月)以上をcoreutilsの

../ファイルの実際の場所を決定するために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で利用できます。


私のUbuntu 14.04にcoreutilsはありませんrealpath
heemayl '19

1
これは私が探しているもののようですが、残念ながら私のCentos 6.5サーバーには実際のパスがありません。私はそれをインストールする立場にありません。googlingは、 "readlink -f"の代わりにrealpathについての言及を出しますが、私はまだそれを動作させることができていません。
アーロンチカリ16

1
字幕は言及し-f(「最後のコンポーネントを除くすべて」が存在する必要があります)、例では使用します-e(「すべてのコンポーネントが存在する必要があります」)。これは少し混乱します。
isanae

2
@AaronCicali危険なほど間違っているため、これは間違いなく必要な答えではありません。あなたはシンボリックリンクを解決してはいけません。シンボリックリンクのターゲットは、チェックしてから使用するまでの間に変わる可能性があります。(これは、設計エラーのよく知られたカテゴリです)。また、rmターゲットではなく引数自体に作用するため、シンボリックリンクを解決することは意味がありません。
Gilles「SO-邪悪なことをやめなさい」16/07/19

1
ヒント:一致する行を出力grep -qしないようにするために使用しgrepます。まだ終了ステータスを取得しているので&&、慣れて||いるとおりに機能します。
CVn

2

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

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"'

-rフラグに関して良い点がありますが、それは実際には存在しないはずです。今削除しています...
アーロンチカリ

su引用が間違っているため、コマンドが壊れていることに注意してください。シェルスニペットとして補間された引数を使用してコマンドを実行しています。たとえば、引数が$(touch foo)次の場合、コードが実行されtouch fooます。
Gilles「SO-邪悪なことをやめなさい」16/07/16

@ギレス、がらくた、私は何かが間違っている必要があることを知っていました。ネストされた引用は私の好きな友達ではありません。私が考えて、二重引用符は、外殻内の変数を評価し、現在のバージョンではうまく動作し、単一引用符は、インナーシェルに評価されているから、それを維持します。無保証。
ilkkachu 2016

@ilkkachu引数に単一引用符が含まれている場合、そのバージョンは失敗します。(ただし、その場合のみ、二重引用符を使用する場合よりも検証がはるかに簡単になります。)任意の文字列を直接補間することは不可能です。文字列をマッサージする(たとえば、すべて'をで置き換える'\'')か、環境変数などの別のチャネルを介して渡す必要があります(これは私がここで行うことですfile_to_remove="$1" su -c 'rm "/home/charlesingalls/$file_to_remove"')。ファイル名は仮想ホスト名であることが判明しているため、すべての特殊文字を拒否してもここでは問題ありません。
Gilles「SO-邪悪なことをやめなさい」2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.