질문

질문

이 사이트의 많은 답변과 의견에는 eval일반적으로 쉘 스크립트에서의 사용을 피해야 한다고 언급되어 있습니다. 이러한 리뷰는 일반적으로 초보자를 위한 것입니다.

언급된 보안 문제와 견고성을 본 적이 있지만 예는 거의 없습니다.

나는 이미 알고 있다eval명령이 뭐예요?그리고 그것을 사용하는 방법. 그러나 거기에 제공된 답변에서는 eval.모든답변에 따르면 이는 eval주요 보안 취약점일 수 있지만 대부분은 설명하지 않고 결론적으로 이 사실을 언급합니다. 이는 더 깊이 생각해 볼 가치가 있는 것으로 보이며 실제로 그 자체의 질문이기도 합니다.

eval나쁜 생각을 사용하고 있습니까 ?

언제회의사용하는 것이 적절한가요?

완전히 안전하게 사용하기 위한 지침이 있습니까, 아니면 항상 필연적으로 보안 허점이 있습니까?


편집하다:이 기사이 질문을 게시할 때 실제로 찾고 있던 표준 참조(답변)입니다. 어떤 학대자라도 eval이 언급을 지적할 수 있습니다.

답변1

이 질문은 의견을 불러일으킬 것입니다...

eval표준 셸의 일부입니다.

유틸리티는 eval각각 구분 기호로 구분된 인수를 함께 연결하여 명령을 구성해야 합니다.<space>특징. 생성된 명령은 쉘에서 읽고 실행해야 합니다.

싫어하는 이유 eval:

  • 스크립트 작성자가 올바르게 인용하지 못해 예상치 못한 동작이 발생할 수 있습니다(평가 중인 변수에서 참조가 일치하지 않을 가능성을 고려).
  • 평가 결과는 명확하지 않을 수 있습니다(부분적으로는 참조 문제로 인해, 부분적으로는 eval다른 변수의 이름을 기반으로 변수를 설정할 수 있기 때문에).

반면, eval입력한 데이터를 검사하는 경우(예: 파일 이름 결과를 테스트하고 복잡한 따옴표가 없는지 확인하는 경우) 유용한 도구입니다. 제안된 대안은 이식성이 떨어지는 경우가 많으며, 예를 들어 일부 특정 셸 구현에만 국한됩니다.

추가 읽기 (그러나 bash는 구현별/비표준 대안을 제공한다는 점을 명심하십시오):

답변2

질문

언제 사용하는 것이 더 적절합니까?

eval의 매개변수가 올바르게 인용되면(2개 수준) 첫 번째 구문 분석에서 eval로 확장된 변수 값은 두 번째 구문 분석에서 실행되는 명령이 되지 않습니다.

eval을 사용하는 것이 왜 나쁜 생각입니까?

자신이 하고 있는 일을 정말로 알고 있다면 이는 IMO에 문제가 되지 않습니다(이 질문은 조언을 구할 것입니다).

eval먼저, 작성한 내용(평가)이 및 로 대체하여 예상한 내용과 일치하는지 확인하세요. echo둘째: 어떤 변수가 로컬로 설정되어 있고 어떤 값을 가질 수 있는지 신중하게 생각하십시오.

완전히 안전하게 사용하기 위한 지침이 있습니까, 아니면 항상 필연적으로 보안 허점이 있습니까?

예, 일부 상황에서는 완전히 안전하게 사용할 수 있습니다.

하지만 이는 항상 위험합니다(잘못된 스크립트를 작성할 때 위험이 있는 것처럼).
eval이 일부 지수에 따라 위험을 증가시키는 경우가 있습니다.

여기에는 확실히 긴 설명이 필요합니다.

세부 사항:

eval 명령(항상 내장되어 있음)을 사용하면 명령줄을 두 번 구문 분석할 수 있습니다.

rm -rf /원칙적으로 이는 다른 명령보다 더 위험하지도 덜 위험하지도 않습니다(생각해 보세요). 현재 사용자 권한으로 실행되므로 루트로 사용하기 전에 두 번 생각하십시오. 그러나 위에 표시된 명령도 마찬가지입니다 rm. 제한된 사용자의 경우 루트가 소유한 대부분의 디렉토리에서 rm이 ​​실패합니다. 하지만 rm여전히 위험한 명령이다.

잘 알려진 관용구입니다.

eval 명령은 알려진 관용구에서 매우 유용하고 안전합니다.
예를 들어,이 표현식은 마지막 위치 인수의 값을 인쇄합니다.:

$ set -- "ls -l" "*" "c;date"

$ eval echo \"\$\{$#\}\"          ### command under test

c;date

$#이 명령은 가능한 결과 가 숫자 뿐이므로 무조건 안전합니다(실제로) . 가능한 유일한 결과 $n(n은 숫자)는 해당 위치에 있는 변수의 내용입니다.

명령이 수행하는 작업을 보려면 eval을 echo로 바꾸십시오.

$ set -- "ls -l" "*" "c; date"
$ echo echo \"\$\{$#\}\"          ### command under test
echo "${3}"

And는 echo ${3}매우 일반적이고 안전한 관용어입니다.

일부 참조를 변경해도 동일한 결과를 얻을 수 있습니다.

$ echo echo '"${'$#\}\"
echo "${3}"
$ eval echo '"${'$#\}\"
c;date

이 새로운 참조 방법이 위의 방법보다 더 모호하다는 것을 쉽게 알 수 있기를 바랍니다.

약간 다른 관용구.

$ b=book
$ book="A Tale of Two Cities"
$ eval 'a=$'"$b"               ### safe for some values of $b.
$ echo "$a"
A Tale of Two Cities

여기서 우리는 eval의 첫 번째(두 가지 문제 중)이자 주요 문제를 발견합니다
.

$ b='book;date'

$ eval 'a=$'"$b"               ### safe for some values of $b.
Fri Apr 22 22:03:09 UTC 2016

명령 날짜가 실행되었습니다(우리는 실행할 의도가 없음).

그러나 이것은 실행되지 않습니다 date(또는 @Wildcard에게 감사드립니다).
이 명령에 대한 올바른 해결책은 입력을 삭제하는 것이지만 eval과 관련된 두 번째 문제가 정의될 ​​때까지 이에 대한 논의를 미루겠습니다.

$ eval 'a="$'"$b"\"
$ echo "$a"
A Tale of Two Cities;date

트릭은 어렵지 않습니다. eval을 echo로 바꾸고 인쇄된 명령줄이 안전한지 평가하면 됩니다. 안전하지 않은 명령과 안전한 명령 비교:

$ echo 'a=$'"$b"
a=$book;date
$ echo 'a="$'"$b"\"
a="$book;date"

이러한 간단한 큰따옴표는 문자열을 문자열로 남겨두고 쉘에서 실행되는 명령으로 변환되지 않습니다.

"외부 따옴표"와 "내부 따옴표"(가독성을 위해 공백 추가)를 배치하면 인용 논리를 이해하는 것이 더 쉬울 수 있습니다.

$ echo a\=\"\$    "$b"   \"

내부 참조는 항상 $b"좋은 생각"입니다.
외부 항목은 백슬래시로 표시된 항목입니다(이 경우).
공백이 없는 명령(사용되어야 함):

$ echo a\=\"\$"$b"\"
a="$book;date"

$ eval a\=\"\$"$b"\"
$ echo "$a"
A Tale of Two Cities;date

하지만 이 예도 사용자에 의해 금방 깨졌습니다(@Wildcard 덕분에).

$ b='";date;:"'
$ eval a\=\"\$"$b"\"
Fri Apr 22 23:25:43 UTC 2016

eval에 의해 실행되는 명령은 악의적인 명령이 되는 것을 방지합니다. echo를 사용합시다:

$ echo a\=\"\$"$b"\"
a="$";date;:""

두 인용의 결과가 무엇인지 생각하는 것은 결코 쉽지 않습니다.

두 번째 질문입니다.

이것은 첫 번째 것보다 더 복잡합니다. 어떤 경우에는 eval 매개변수의 첫 번째 확장 결과가 참조로 남아 있기를 원하지 않습니다. 많은 경우 변수 내에서 명령을 실행하려고 하기 때문입니다.

또는 공격자(위에 표시된 대로)가 따옴표와 일치하는 문자열을 만든 다음 명령을 따옴표 바깥에 배치합니다. 명령이 실행되고 (운이 좋다면) 오류가 보고됩니다.

이로 인해 첫 번째 보호 계층인 참조가 중단
되고 변수 값이 실행될 수 있도록 완전히 공개됩니다.

이 경우 변수가 갖게 될 값을 결정해야 합니다. 가능한 모든 값.

변수 값이 사용자에 의해 제어되는 경우 사용자에게 다음과 같이 알려줍니다.

나에게 어떤 명령을 내리면 내 허락하에 그것을 실행하겠습니다.

그것은 항상 위험한 내기이고, 확실한 실수이며, 근본적으로 그것은 광기입니다.

외부 데이터를 정리합니다.

요약하면 이것은 완전히 안전합니다.

#!/bin/bash

a=${1//[^0-9]}       ### Only leave a number (one or many digits).

eval echo $(( a + 1 ))

외부 사용자가 위치 인수에 무엇을 입력하든 숫자는 큰 숫자가 될 수 있으며 $(( ... ))는 실패하지만 해당 입력은 명령 실행을 트리거하지 않습니다.

이제 b위 명령에서 clean 변수에 대해 논의할 수 있습니다. 명령은 다음과 같습니다:

$ b='";date;:"'
$ eval a\=\"\$"$b"\"
Sat Apr 23 01:56:30 UTC 2016

b에는 줄 바꿈에 사용되는 여러 문자가 포함되어 있기 때문입니다.

$ echo a\=\"\$"$b"\"
a="$";date;:""

작은따옴표로 변경하면 작동하지 않으며 b의 다른 값도 일치합니다. b(적어도) 따옴표를 제거하여 "정리" 해야 합니다 .
이를 $b다음으로 대체할 수 있습니다 ${b//\"}.

$ eval a\=\"\$"${b//\"}"\"
$ echo "$a"
$;date;:

더욱 강력한 변수 이름 정리.

하지만 쉘의 변수 이름에는 밑줄과 밑줄만 포함될 수 있다는 점을 인정하면 0-9a-zA_Z더 나은 결과를 얻을 수 있습니다. 다음을 사용하여 다른 모든 항목을 제거할 수 있습니다.

$ c="$( LC_COLLATE=C; echo "${b//[^0-9A-Za-z_]}" )"

그러면 변수가 b유효한 변수 이름으로 정리됩니다. 그런 다음 내부 변수 이름이 존재하는지 실제로 확인하여 더 엄격한 확인을 추가
할 수 있습니다 $b( clean 사용 ).c

$ if declare -p "$c" &>/dev/null; then   eval a\=\"\$"$c"\" ; fi

답변3

Eval은 잘못된 코드에 대한 간단한 솔루션입니다. 생각나는 예는 다음과 같습니다.

참조로 배열 요소를 가져오고 싶다고 가정해 보겠습니다. 평가판을 사용할 수 있습니다.

$ nov=(osc pap que)
$ rom=nov
$ eval echo \${$rom[2]}
que

더 나은 접근 방식은 다음과 같습니다.

$ nov=(osc pap que)
$ rom=nov[2]
$ echo ${!rom}
que

더 나은 접근 방식은 다음과 같습니다.

$ tail -1 <<+
> osc
> pap
> que
> +
que

관련 정보