file_<your_stuff_here>.txt
bash 스크립트에서 ls의 출력을 평가하거나 백틱하는 것이 위험할 정도로 주어진 접두사와 접미사를 가져야 한다는 제약 조건을 가질 만큼 충분히 병리적인 파일 이름 또는 파일 이름 집합이 있습니까 ? "위험하다"는 것은 관련 디렉터리에 파일을 생성할 수 있는 권한이 있는 사람이라면 누구나 모든 명령을 실행할 수 있다는 것을 의미합니다.
예를 들어 bash 스크립트를 다음과 같이 해보자.
#!/usr/bin/env bash
hello=(`ls -t`)
파일 시스템은 파일 이름을 따옴표로 묶어 공백과 같은 특수 문자를 삭제하는 것 같습니다. 이 모든 것을 포착할 수 있을까요?
답변1
파일 이름...위험 평가
틀림없이.
$ touch 'file $(date >&2).txt'
$ bash -c 'eval ls *'
Wed 03 Feb 2021 06:07:08 PM EET
ls: cannot access 'file': No such file or directory
ls: cannot access '.txt': No such file or directory
eval
물건을 무작위로 넣지 마세요
아니면 bash 스크립트에서 backtick ls의 출력이요?
이것이 무엇을 의미하는지 잘 모르겠습니다.
말씀하신 예를 말씀드리자면,
hello=(`ls -t`)
아니면 좀 더 합리적인 구문을 사용하세요.
files=( $(ls -t) )
ls
그런 다음 wordsplit의 출력을 배열로 가져옵니다 .
$ declare -p files
declare -a files=([0]="file" [1]="\$(date" [2]=">&2).txt")
명령 주입이 가능하기 전에도 여기서 가장 큰 문제는파일 이름에 공백이 있으면 문제가 발생합니다., 우리는 하나 대신 두 개의 배열 항목을 얻습니다. 관련 페이지 보기ls를 구문 분석하다Greg의 위키에 있습니다. 아니요, 따옴표를 추가해도 이 문제를 해결할 수 없습니다.분사그것은 그렇지 않습니다.
그러니 ls
거기에서는 사용하지 마세요. 쉘에 파일 이름 목록을 생성하도록 요청하십시오.
files=(*)
declare -p files
declare -a files=([0]="file \$(date >&2).txt")
여기서 유일한 문제는 Bash가 날짜별로 파일을 정렬하는 좋은 방법을 제공하지 않는다는 것입니다.ls -t
예유혹적이다. 좋은 옵션은 파일 이름 자체에 날짜를 넣는 것입니다따라서 기본 정렬을 사용하면 날짜별로 정렬할 수 있습니다., 또는날짜별로 정렬할 수 있으므로 Zsh를 사용하세요.. 아니면못생긴 해커문제를 해결하세요(경고, 이 솔루션은 아직 테스트하지 않았습니다).
eval
다음과 같이 쉘 스크립트만 필요한 항목에 파일 이름을 전달해야 하는 경우에도 동일한 문제가 발생합니다.
ssh somehost "do something with $file" # wrong
ssh somehost "do something with '$file'" # still wrong, the name
# could contain single ticks
파일 시스템은 파일 이름을 따옴표로 묶어 공백과 같은 특수 문자를 삭제하는 것 같습니다. 이 모든 것을 포착할 수 있을까요?
아, 이런, 아뇨, 그게 아닙니다. 파일 시스템이 특수 문자가 셸에 저장되지 않도록 조치를 취했다면 unix.SE에 있는 게시물의 절반은 필요하지 않을 것입니다.
너무 많은 지식으로 인한 고통을 겪고 싶다면 David Wheeler의 기사를 읽어보세요.Unix/Linux/POSIX 파일 이름 수정: 제어 문자(예: 개행 문자), 선행 대시 및 기타 문제
그리고 그의 또 다른 하나는,셸의 파일 이름과 경로 이름: 올바르게 얻는 방법. 많은 관련 주제가 unix.SE에서도 논의되었습니다.
또한 인용문도 도움이 되지 않습니다.
$ touch '"quoted name"' 'othername' # two files
$ printf "%s\n" $(ls) # oops
othername
"quoted
name"
$ printf "%s\n" * # this works better
othername
"quoted name"
단어 분할이 발생할 때 인용문은 단지 일반 문자이기 때문입니다. (설정에 IFS
따옴표가 포함되어 있지 않으면 상황이 더 악화될 수 있습니다.) 또한 이름이 따옴표로 묶여 있어도중간에 여전히 따옴표가 있을 수 있습니다., 이것을 깨기 위해. 또한 올바르게 이스케이프하거나 인용하도록 주의해야 합니다.
인용을 위한 GNU ls입니다.버전 및 설정에 따라 다름:
$ ls -l
total 0
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 othername
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 '"quoted name"'
이는 와 동일합니다 ls --quoting-style=shell
. 그런데 모든 줄 바꿈, 달러 기호 및 따옴표에 대해 올바른 것 같습니다. 하지만 그것이 올바르게 이루어질 수 있다고 믿습니까? 이렇게 하면,그리고올바르게 사용하는 방법을 알고 있다면 이를 사용하여 정렬된 목록을 얻을 수 있습니다.
답변2
hello=(`ls -t`)
이 형식은 안전해 보입니다. Bash는 구문으로 해석하지 않고 명령 대체 결과에 대해서만 분할 + glob을 수행합니다.
a=(`echo '[0]=1'`)
typeset -p a
declare -a a=([0]="[0]=1") # aha!
그러나 (모호한) declare "var=val"
또는 local "var=val"
형식은 다음과 같이 작동하므로 그렇지 않습니다 eval "var=val"
.
cd "$(mktemp -d)"
touch '$(yes BOOBS >&2&)'
declare -a "a=($(ls))"
BOOBS
BOOBS
BOOBS
...
등과 동일 합니다 declare -a a="($(ls))"
.declare -a a="(`ls`)"
아니면 심지어인용되지 않음변수가 배열로 선언된 경우 양식에도 동일하게 적용됩니다.
cd "$(mktemp -d)"
a=(1 2 3)
touch '($(yes BOOBS >&2&))'
declare a=$(ls) # I forgot that a was an array
BOOBS
BOOBS
BOOBS
...