단어 분할을 피하기 위해 문자열 내에서 변수 확장을 인용하는 방법은 무엇입니까?


9
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"

이 경우 $myvar값의 공백으로 인해 단어 분할을 피하기 위해 어떻게 인용 할 수 myvar있습니까?


4
이 문제를 어떻게 해결하더라도 여전히 큰 도움이되지 않습니다. 은 cd단지 내부에 효과가 bash -c쉘을.
Kusalananda

나는 질문에 대해서만 최소한의 예를 제시하고 있는데, 그것은 실제 문제에 대한 해결책이 아닙니다 .unix.stackexchange.com / a / 269080 / 674 에 주어진 명령 문자열 sudo bash -c의 변수 확장은 공백을 포함 할 수있는 경로명
Tim

답변:


13

더 없습니다 단어 분할 이 코드에서와 같이 (인용 부호로 둘러싸이지 않은 확장에 따라 분할 변수가있는 기능에서와 같이) $myvar인용 부호로 둘러싸이지 않은되지는.

그러나 $myvar에 전달되기 전에 확장 된 명령 삽입 취약점 이 bash있습니다. 내용은 bash 코드로 해석됩니다!

공백이 있으면 단어 분리cd 때문이 아니라 셸 구문에서 여러 토큰으로 구문 분석되기 때문에 여러 인수가로 전달됩니다 . 값이 이면 재부팅됩니다! ¹bye;reboot

여기, 당신은 원할 것입니다 :

sudo bash -c 'cd -P -- "$1"' bash "$myvar"

(당신의 내용을 전달할 곳 $myvar이 인라인 스크립트의 첫 번째 인수로를, 메모를하는 방법 모두 $myvar$1방지 IFS-워드 분할 (과) 글 로빙에 대한 각각의 쉘 인용했다).

또는:

sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'

( $myvar환경 변수 의 내용을 전달하는 곳 ).

물론 당신은 실행하여 유용 아무것도 달성하지 않습니다 cd (여부를 확인 이외의 그 인라인 스크립트에서 rootcd거기에를). 아마도, 당신은 그 스크립트를 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

디렉토리 자체에 대한 검색 권한이 있지만 경로의 디렉토리 구성 요소 중 하나에 대한 검색 권한 이없는 경우는 예외입니다 .

$ 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와 같은 값의 $(seq 10)경우, bash 쉘로 시작된 명령 대체의 확장에 따라 단어 분리가 시작됩니다.root


4

당신이 원하는 것을 더 직접적으로 할 수있게 해주는 새로운 (bash 4.4) 인용 마법이 있습니다. 더 큰 컨텍스트에 따라 다른 기술 중 하나를 사용하는 것이 더 나을 수 있지만이 제한된 컨텍스트에서 작동합니다.

sudo bash -c "cd ${myvar@Q}; pwd"

4

의 내용을 신뢰할 수없는 경우 $myvarGNU printf를 사용하여 이스케이프 된 버전을 만드는 것이 합리적 입니다.

#!/bin/bash

myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"

현상태대로 printfman 페이지는 말한다 :

%q

ARGUMENT는 제안 된 POSIX $''구문 으로 인쇄 할 수없는 문자를 이스케이프하여 쉘 입력으로 재사용 할 수있는 형식으로 인쇄됩니다 .


휴대용 버전에 대한 내 대답을 참조하십시오.
R .. GitHub 중지 지원 아이스

GNU printf는 GNU 시스템에서만 호출되며 내장 시스템 이 아닌 $(printf '%q' "$myvar")쉘에서 실행될 경우에만 호출됩니다 printf. printf오늘날 대부분의 껍질은 내장되어 있습니다. 모든 것을 지원하는 %q것은 아니며, 지원할 때 해당 쉘에서 지원하는 형식으로 항목을 인용 할 수 있습니다 bash. 사실 bash'들 printf도 자체의 일부 값에 대한 제대로 일을 인용하지 못한 $myvar일부 로케일한다.
Stéphane Chazelas

bash-4.3적어도, 그 명령 주입 취약점에 여전히 myvar=$'\xa3``reboot`\xa3`' zh_HK.big5hkscs가 예를 들어 로케일에.
Stéphane Chazelas

3

우선, 다른 사람들이 지적했듯이 cd명령은 즉시 종료되는 쉘의 컨텍스트에서 발생하기 때문에 쓸모가 없습니다. 그러나 일반적으로 문제는 의미가 있습니다. 필요한 것은 임의의 문자열쉘 인용하여 쉘 인용 문자열 로 해석되는 컨텍스트에서 사용할 수있는 방법입니다. 내 속임수 페이지 에 이에 대한 예가 있습니다 .

quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }

이 기능으로 다음을 수행 할 수 있습니다.

myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"

3

Stéphane Chazelas가 제안하는 것은 확실히 이것을하는 가장 좋은 방법입니다. sedR ..의 답변과 같이 하위 프로세스 를 사용하는 대신 매개 변수 확장 구문으로 안전하게 인용하는 방법에 대한 답변을 제공합니다 .

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

1

예, 단어 분리가 있습니다.
글쎄, 기술적으로, 명령 줄은 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"

-2

작은 따옴표는 여기에서 작동하지 않으므로 변수가 확장되지 않습니다. 경로 변수가 삭제되었다고 확신하는 경우 가장 간단한 해결책은 새 bash 쉘에 있으면 결과 확장 주위에 따옴표를 추가하는 것입니다.

sudo bash -c "cd \"$myvar\""

2
아직도 명령 주입 취약점의 값처럼 $myvar같은 $(reboot)또는"; reboot; #
스테판 Chazelas가

1
를 사용 sudo bash -c "cd -P -- '$myvar'"하면 문제를 해결하기 $myvar 쉬운 작은 따옴표 문자가 포함 된 값으로 만 문제가 제한됩니다 .
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.