$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
そのような場合、$myvar
値の空白のために単語の分割を避けるためにどのように引用できmyvar
ますか?
sudo bash -c
、私は、変数の拡張を持っています空白を含む可能性のあるパス名。
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
そのような場合、$myvar
値の空白のために単語の分割を避けるためにどのように引用できmyvar
ますか?
sudo bash -c
、私は、変数の拡張を持っています空白を含む可能性のあるパス名。
回答:
何もありません単語の分割は、ように、そのコードに(引用符で囲まれていない拡張時に変数を分割機能のように)$myvar
引用符で囲まれていないではありません。
ただし、$myvar
に渡される前に拡張されるコマンドインジェクションの脆弱性がありbash
ます。したがって、その内容はbashコードとして解釈されます!
そこにスペースがいくつかの引数が渡されることになりますcd
ないための、単語の分割が、彼らはシェルの構文にはいくつかのトークンとして解析されますので。の値を指定bye;reboot
すると、再起動します!¹
ここでは、次のようにします。
sudo bash -c 'cd -P -- "$1"' bash "$myvar"
($myvar
そのインラインスクリプトの最初の引数としての内容を渡す場合、IFSの単語の分割(およびグロビング)を防ぐために、それぞれのシェルで両方$myvar
と$1
が引用されていることに注意してください)。
または:
sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
($myvar
環境変数での内容を渡す場所)。
もちろん、インラインスクリプトでのみ 実行cd
してroot
もcd
、そこに実行できるかどうかを確認する以外に、有用なことは何もありません。おそらく、そのスクリプトをcd
そこに置いてから、次のような他のことを行います。
sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"
を使用sudo
しcd
て、他の方法ではアクセスできないディレクトリにアクセスできるようにするつもりなら、それは実際には機能しません。
sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
のbash
現在のディレクトリを使用してインタラクティブを開始し$myvar
ます。しかし、そのシェルはとして実行されroot
ます。
あなたがすることができます:
sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash
現在のディレクトリが$myvar
である非特権インタラクティブを取得するにcd
は、最初にそのディレクトリへのアクセス権を持っていなかった場合、現在の作業ディレクトリであっても、そのディレクトリで何かを行うことはできません。
$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied
例外は、ディレクトリ自体に対する検索権限はあるが、パスのディレクトリコンポーネントの1つに対する権限がない場合です。
$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied
¹厳密に言えば、$myvar
like $(seq 10)
(文字通り)の値の場合、次のようにbash
開始されたシェルによるコマンド置換の展開時に、もちろん単語分割があります。root
の内容が信頼できない場合は$myvar
、GNU printf
を使用してエスケープバージョンを作成するのが妥当なアプローチです。
#!/bin/bash
myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"
同様にprintf
manページは言います:
%q
ARGUMENTは、シェル入力として再利用できる形式で出力され、提案されたPOSIX
$''
構文を使用して出力不可能な文字をエスケープします。
printf
は、GNUシステム上でのみ呼び出され、それ$(printf '%q' "$myvar")
がprintf
組み込みでないシェルで実行される場合。printf
現在、ほとんどのシェルにビルトインがあります。%q
ただし、すべてがサポートしているわけではなく、サポートしている場合は、対応するシェルでサポートされている形式で引用されbash
ます。実際、一部のロケールの一部の値については、bash
' printf
自体についても正しく引用できません$myvar
。
bash-4.3
、少なくとも、それはまだでコマンドインジェクションの脆弱性だmyvar=$'\xa3``reboot`\xa3`'
インスタンスのロケールzh_HK.big5hkscsインチ
まず、他の人が指摘したように、cd
コマンドはすぐに終了するシェルのコンテキストで発生するため、役に立たない。しかし、一般的にこの質問は理にかなっています。必要なのは、任意の文字列をシェルクォートして、シェルクォートされた文字列として解釈されるコンテキストで使用できるようにする方法です。私のshトリックページにこの例があります。
quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }
この関数を使用すると、次のことができます。
myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"
StéphaneChazelasの提案は、間違いなくこれを行うための最良の方法です。sed
R ..の回答のように、サブプロセスを使用するのではなく、パラメーター展開構文で安全に引用する方法についての回答を提供します。
myvar='foo \'\''"bar'
quote() {
printf "'%s'" "${1//\'/\'\\\'\'}"
}
printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"
そのスクリプトの出力は次のとおりです。
value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar
はい、単語分割があります。
まあ、技術的には、bashで行を解析する際のコマンドライン分割です。
最初に引用を正しくしましょう:
あなたが望んでいるとおりにコマンドを機能させるための最も簡単な(そして安全でない)ソリューションは次のとおりです。
bash -c "cd \"$myvar\""
何が起こるか確認してみましょう(を想定myvar="/path to/my directory"
):
$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>
ご覧のとおり、パス文字列はスペースで区切られています。そして:
$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>
分割されていません。
ただし、コマンドは(書かれているとおり)安全ではありません。より良い使用:
$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory
これにより、ダッシュ(-)で始まるファイルまたは問題を引き起こす可能性のあるリンクをたどるファイルからcdが保護されます。
文字列$myvar
がパスであると信頼できる場合、これは機能します。
ただし、「文字列を実行」しているため、「コードインジェクション」は引き続き可能です。
$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May 8 12:25:51 UTC 2018
次のように、文字列をより強く引用する必要があります。
$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>
上記のように、値は解釈されず、そのままとして使用されました"$1'
。
これで(安全に)sudoを使用できます。
$ sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory
複数の引数がある場合は、次を使用できます。
$ sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"
ここでは単一引用符は機能しません。それ以降、変数は展開されません。パスの変数が無害化されていると確信している場合、最も簡単な解決策は、新しいbashシェルに配置されたら、結果の展開を単に引用符で囲むことです。
sudo bash -c "cd \"$myvar\""
$myvar
ように$(reboot)
か"; reboot; #
sudo bash -c "cd -P -- '$myvar'"
すると、問題を$myvar
一重引用符が含まれている値のみに制限し、サニタイズが容易になります。
cd
内側のみ効果があるbash -c
シェルを。