7.2. 호출을 합당한 값으로 제한해라

반드시 모든 다른 프로그램에 대한 호출이 모든 매개변수에 대해 타당하고 예상한 값들만을 허용하게 해라. 이는 많은 라이브러리 호출들 또는 명령들이 잠재적으로 예기치 않은 방식으로 더욱 저수준 루틴들을 호출하기 때문에 생각과는 달리 더욱 어렵다. 예를 들어, popen(3) 과 system(3) 과 같은 몇몇 시스템 호출들은 명령 쉘을 호출함으로써 구현되는데 이는 쉘 메타문자들에 의해 영향을 받을 것임을 의미한다. 비슷하게 execlp(3) 과 execvp(3) 들때문에 쉘이 호출될 수도 있다. 많은 지침들은 프로세스를 생성시키려고 할 때는 popen(3), system(3), execlp(3) 과 execvp(3) 들을 전부 피하고 C 에서 execve(3) 을 직접적으로 사용하라고 제안하고 있다 [Galvin 1998b]. 적어도 execve(3) 을 사용할 수 있을 때는 system(3) 를 사용하는 것을 피해라; system(3) 은 문자들을 전개하기 위해 쉘을 사용하기 때문에 이는 더욱 많은 악영향을 끼칠 기회가 있다. 비슷한 방식으로 펄과 쉘의 backtick (`) 또한 명령쉘을 호출한다; 펄에 대한 더욱 자세한 정보는 9.2절 을 보라.

쉘 메타문자들은 이 문제들 중 가장 다루기 힘든 예들중의 하나로 표준 유닉스 계열 명령쉘 (/bin/sh 에 저장된) 은 많은 문자들을 특별히 해석한다. 이러한 문자들이 쉘로 보내지면 이들의 특별한 해석이 이스케이프되지 않는다면 사용될 것이다; 이 사실이 프로그램을 파괴하는데 사용될 수 있다. WWW 보안 FAQ [Stein 1999, Q37] 에 따르면 이러한 문자들은 다음과 같다:

& ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r

많은 상황에 있어 탭과 공백 문자들을 이스케이프하길 원할 것이라고 저자는 언급해야 하는데 왜냐하면 이들 (과 개행) 은 디폴트 매개변수 분리자들이기 때문이다. 분리자 값들은 IFS 환경 변수를 설정함으로써 변경될 수 있지만 이 변수의 출처를 믿을 수 없다면 이를 버리거나 어쨌든 환경 변수 처리의 일부분으로 재설정해야 한다.

불행히도 실제 이는 완전한 목록이 아니며 다음은 문제가 있을 수 있는 약간의 다른 문자들이다:

이러한 문자들중 한개라도 잊는다면 재난을 초래할 수도 있는데 예를 들어 많은 프로그램들은 백슬래쉬를 메타문자로서 생략한다 [rfp 1999]. 4장 에 논의되었듯이 누군가에 의해 추천되는 접근 방법은 적어도 이러한 문자들이 입력일 때 모든 문자를 즉각적으로 이스케이프하는 것이다. 그러나 더욱 최선의 접근 방법은 어떤 문자들을 허용하길 바라는지 식별하여 단지 이러한 문자들만 허용하도록 필터를 사용하는 것이다.

많은 프로그램들 특히 대화식으로 설계된 프로그램들은 ``특별한" 기능을 수행하는 "escape" 코드를 갖고 있다. 가장 보편적인 (또한 위험한) 이스케이프 코드중의 하나는 명령 행을 초래하는 것이다. 이러한 "escape" 명령들이 포함될 수 없도록 확인해라 (특정 명령이 안전하다고 확신하지 못한다면). 예를 들어, 많은 라인 지향 메일 프로그램 (mail 과 mailx 같은) 들은 틸드 (~) 를 이스케이프 문자로서 사용하는데 이 문자는 많은 명령을 보내기 위해 사용될 수 있다. 그 결과로 "mail admin < file-from-user" 와 같이 명백히 해롭지 않은 명령들이 임의의 프로그램들을 실행시키기 위해 사용될 수 있다. vi, emacs 와 ed 와 같은 대화식 프로그램들은 사용자들이 그들 세션으로부터 임의의 쉘 명령들을 실행시키도록 허용하는 "escape" 메카니즘을 갖고 있다. 이스케이프 메카니즘을 검색하기 위해서는 호출하는 프로그램들의 문서들을 늘 조사해라. 다른 프로그램에 의해 사용될 프로그램만을 호출하는 것이 최선이다; 7.3절 를 보라.

이스케이프 코드들을 피하는 문제는 더욱 저수준 하드웨어 컴포넌트들과 이들의 에뮬레이터들로 내려간다. 대부분의 모뎀들은 소위 "Hayes" 커맨드셋을 구현하는데 이 커맨드셋이 금지되지 않는다면 문구 ``+++" 인 지연 및 다른 지연을 야기해 모뎀에게 모든 다음의 텍스트를 모뎀에 대한 명령들로 해석하게끔 한다. 이는 서비스 부인 공격 (단절 명령어 ``ATH0" 를 설정함으로써) 또는 사용자에게 다른 누군가에 연결하도록 하는 것을 구현하는데 사용될 수 있다 (상급 공격자는 자신의 제어하에 있는 머신을 통해 사용자가 연결하도록 라우팅 설정을 다시 할 수 있다). 특정 모뎀의 경우 이에 대처하는 것은 쉽지만 (예, 모뎀 초기화 문자열에 ``ATS2-255" 를 추가한다) 아직도 일반적인 문제는 남아있다: 하위 수준 컴포턴트 또는 이의 에뮬레이션을 제어하고 있다면 반드시 커맨드셋을 금지하거나 그렇지 않은 경우 내장되어 있는 모든 이스케이프 코드를 다루어야 한다.

많은 "터미널" 인터페이스들은 VT-100 같은 예전의 오래전에 사라진 물리적 터미널의 이스케이프 코드들을 구현한다. 이러한 코드들은 유용할 수 있는데 예를 들어 문자를 굵게 하기, 폰트색 변경하기 또는 터미널 인터페이스내에서 특별한 위치로 이동하는데 유용할 수 있다. 그러나 임의의 신뢰되지 않은 데이타가 터미널 스크린으로 직접 보내지도록 허용하지 마라 왜냐하면 이러한 코드들의 일부가 심각한 문제를 일으킬 수 있기 때문이다. 어떤 시스템에서 키들을 다시 배치할 수 있다 (예, 따라서 사용자가 "엔터" 또는 function 키를 누를때 이는 실행시키길 원하는 명령을 보낸다). 어떤 시스템에서는 스크린을 지우고 victim 이 실행시켰으면 하는 일련의 명령들을 표시한 후 이들 명령셋을 다시 돌려보내 victim 에게 키스트로크를 기다리지 않고 공격자가 선택한 명령들을 실행시키게 하는 코드를 보낼 수도 있다. 이는 "페이지 모드 버퍼링" 을 사용해서 일반적으로 구현되는데 이러한 보안 문제가 왜 에뮬레이트된 tty (보통 /dev/ 내의 디바이스 파일들로 표현되는) 들이 이들의 소유자들에게만 쓰기가능하며 다른 누구에게도 허용되지 않는 이유이다 - 이들은 절대로 "other write", 허가권이 설정되어서는 안되며 단지 사용자만이 그룹의 멤버가 아니라면 (즉, "user-private group" 스킴이 아니라면) "group write" 허가권이 터미널에 대해 설정되지 않아야 한다 [Filipski 1986]. 모사된 터미널에서 사용자에게 데이타를 표시하려면 사용자에게 되보내지는 데이타의 모든 제어 문자 (32 미만의 값을 갖는 문자) 들은 안전하다고 확인되지 않는다면 아마도 필터링되어 제거될 필요가 있다. 나쁜 것은 더욱 악화되는데 탭과 개행 (과 아마도 carriage return) 이 안전하다고 식별할 수 있으며 나머지는 모두 제거한다. high 비트가 설정된 문자 (127 보다 큰 값) 들은 어떤 방식에서는 다루기가 더욱 어렵다; 어떤 예전 시스템들은 이들이 설정되지 않은 것처럼 이들을 다루지만 단순히 이들을 필터링하는 것은 많은 국제적 사용을 금지한다. 이런 경우 명확한 상황을 살펴볼 필요가 있다.

관련 문제는 NIL 문자 (문자 0) 가 예기치 않은 효과를 가질 수 있다는 것이다. 대부분의 C 와 C++ 함수들은 이 문자가 문자열의 끝을 표시한다고 가정하지만 다른 언어 (펄과 Ada95와 같은) 들에서는 문자열을 다루는 루틴들이 NIL 을 포함한 문자열들을 다룰 수 있다. 많은 라이브러리들과 커널 호출들이 C 관례를 사용하기 때문에 결과적으로 검사된 것이 실제 사용된 것은 아니다 [rfp 1999].

다른 프로그램을 호출하거나 파일을 참조할때는 늘 절대 경로 (예, /usr/bin/sort) 를 지정해라. 프로그램 호출의 경우 이는 PATH 값이 잘못 설정되어 있더라도 잘못된 명령을 호출할 때의 가능한 에러들을 제거할 것이다. 다른 파일 참조의 경우 이는 올바르지 않은 시작 디렉토리로부터 발생하는 문제들을 줄인다.