web-dev-qa-db-ja.com

ファイル名を引用符で囲むと、 `xargs Sudo rm -rf`を実行するのに十分なセキュリティですか?

フォルダー内の最後の2つのファイルを除くすべてを削除するスクリプトを作成しました。

#!/bin/bash
ls -1 --quoting-style=Shell-always /path/to/some/folder \
    | head -n -2 \
    | xargs printf -- "'/path/to/some/folder/%s'\n" \
    | xargs Sudo rm -rf

このスクリプトは、毎日cronジョブとして実行されます。

その理由は次のとおりです。

  1. ls -1を使用してすべてのファイルのリストを取得します(したがって、1行に1つのファイルを取得します)。

  2. head -n -2;を使用してリストから最後の2つを削除します。

  3. lsは相対パスを出力するため、xargs printfを使用してフォルダーパスを追加し、絶対パスにします。

  4. xargsを使用してSudo rm -rfに送信します。

誰でもこのフォルダにアクセスできるため、誰でもこのフォルダ内のファイルを作成および削除できます。

問題は:Sudo rm -rfは怖いです。 xargs Sudo rm -rfは非常に怖いです。

削除する巧妙なファイルを作成することで(誤ってまたは故意に)他のフォルダー/システムを損傷することがないようにしたいと思います。私は知りません、次のような巧妙なもの:

file with / spaces.txt

その結果、非常に怖いSudo rm -rf /になる可能性があります。

EDIT:私の間違いです。ファイル名に/を含めることはできないため、この特定の問題は発生しませんが、他のリスクがあるかどうかについての質問はまだ残っています。

これが--quoting-style=Shell-alwaysを使用している理由です。これにより、スペースのあるファイルでのトリックを防ぐことができます。しかし、今私は誰かがファイル名にスペース引用符で余分な巧妙なことができるかどうか疑問に思っています。

スクリプトは安全ですか?


注:Sudoが必要なのは、mountを使用してマップされたネットワークドライブからリモートでフォルダーにアクセスしており、Sudoなしでは機能しないためです。

10
Pedro A

Linuxでは、任意の文字は次を除く文字を構成する有効なファイル名です。

  • \0(ASCII NUL):Cの文字列終了に使用される
  • /(スラッシュ):パス分離に使用

だから、あなたのアプローチは、あなたが想像できるように、多くの場合、間違いなく機能しません。ファイル名の改行(\n)を処理しますか? (ヒント:No)。

いくつかのメモ:

  • 解析しないls;専用ツールを使用します(ほとんどのユースケースには少なくとも1つあります)
  • ファイル名を扱うときは、そのようなデータを扱うほぼすべてのGNUツールによって提供されるNULで区切られた出力を活用してください
  • パイプするときは注意してください、両方のプログラムがNUL分離を理解できることを確認してください
  • xargsを呼び出すたびに、find ... -execで逃げられるかどうかを確認してください。ほとんどの場合、findだけで十分です。

これらは今のところあなたを連れて行くと思います。 steeldriver はすでにコメントでNULで区切られたアイデアを提供しています(printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm)。これを出発点として使用します。

10
heemayl

xargsはいくつかの引用符をサポートします。任意の引数を受け入れることができる単一引用符、二重引用符、またはバックスラッシュを使用しますが、Bourneのようなシェルの引用構文とは異なる構文を使用します。

Ubuntuで見つかったlsのGNU実装には、xargs入力形式と互換性のある引用モードがありません。

ls --quoting-style=Shell-alwaysはksh93、bashおよびzshシェルの構文と互換性がありますが、lsの出力がlsと同じロケールでシェルによって解釈された場合にのみ、それが出力されます。また、BIG5、BIG5-HKSCS、GBK、GB18030を使用しているロケールなどの一部のロケールは避ける必要があります。

したがって、これらのシェルを使用すると、実際に次のことができます。

typeset -a files
eval "files=($(ls --quoting-style=Shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...

しかし、それは次のような利点はほとんどありません

files=(*(N))                 # zsh
files=(~(N)*)                # ksh93
shopt -s nullglob; files=(*) # bash

唯一役立つのは、ls-tオプションを使用して、mtime/atime/ctimeまたは-S/-Vでファイルをソートする場合です。ただし、その場合でも、zshを使用することもできます。

files=(*(Nom))

たとえば、mtimeでファイルをソートするには(-SにはoLを、-Vにはnを使用します)。

最近変更された2つの通常ファイルを除くすべてを削除するには:

rm -f -- *(D.om[3,-1])

¹まだ長さの制限がいくつかあります(execve()およびいくつかの非GNU xargs実装でははるかに低い任意のもの)、およびいくつかの非GNU xargs実装はシーケンスを含む入力をチョークします有効な文字を形成しないバイト数。

3