변수에 유효한 파일 이름만 포함되어 있는지 어떻게 안전하게 확인할 수 있나요?

변수에 유효한 파일 이름만 포함되어 있는지 어떻게 안전하게 확인할 수 있나요?

다음 스크립트가 주어지면 매개변수에 경로( )나 와일드카드 등이 포함 /home/charlesingalls/되지 않고 유효한 파일 이름만 포함되는지 어떻게 확인할 수 있습니까 ?../home/carolineingalls/

나는 스크립트가 주어진 하드코딩된 디렉토리에서 단일 파일을 삭제할 수 있기를 원합니다. 스크립트는 권한이 있는 사용자로 실행됩니다.

#!/bin/bash

rm -f /home/charlesingalls/"$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"'

관련 정보