마이클 하워드와 키스 브라운
이 문서에서는 사용자가 C++, C# 및 SQL에 익숙하다고 가정합니다.
요약: 보안 문제와 관련하여 문제를 일으킬 수 있는 상황은 많습니다. 아마도 네트워크에서 실행되는 모든 코드를 신뢰하고 모든 사용자에게 중요한 파일에 대한 액세스 권한을 부여하며 컴퓨터의 코드가 변경되었는지 확인하지 않아도 됩니다. 또한 바이러스 백신 소프트웨어가 설치되어 있지 않거나 자신의 코드를 보호하지 못하거나 너무 많은 계정에 너무 많은 권한을 부여할 수도 있습니다. 악의적인 침입을 허용하는 여러 내장 기능을 부주의하게 사용할 수도 있고, 모니터링 없이 서버 포트를 열어 둘 수도 있습니다. 분명히 더 많은 예를 들 수 있습니다. 정말 중요한 문제는 무엇입니까(예: 데이터와 시스템 손상을 방지하기 위해 즉시 주의를 기울여야 하는 가장 위험한 오류)? 보안 전문가 Michael Howard와 Keith Brown이 도움이 될 10가지 팁을 제공합니다.
------------------------------------- ----------------------------------
보안 문제에는 여러 측면이 관련되어 있습니다. 보안 위험은 어디에서나 발생할 수 있습니다. 비효율적인 오류 처리 코드를 작성했거나 권한을 부여할 때 너무 관대했을 수 있습니다. 서버에서 어떤 서비스가 실행되고 있는지 잊어버렸을 수도 있습니다. 모든 사용자 입력을 수락할 수 있습니다. 등. 컴퓨터, 네트워크 및 코드를 보호하기 위한 첫 번째 시작을 제공하기 위해 보다 안전한 네트워크 전략을 위해 따를 수 있는 10가지 팁이 있습니다.
1. 사용자 입력을 신뢰하면 위험에 처하게 됩니다.
나머지 내용을 읽지 않더라도 "사용자 입력을 신뢰하지 마십시오"를 기억하십시오. 항상 데이터가 유효하고 악의적이지 않다고 가정하면 문제가 발생합니다. 대부분의 보안 취약점에는 공격자가 악의적으로 작성된 데이터를 서버에 공급하는 것과 관련이 있습니다.
입력의 정확성을 신뢰하면 버퍼 오버플로, 사이트 간 스크립팅 공격, SQL 삽입 코드 공격 등이 발생할 수 있습니다.
이러한 잠재적인 공격 벡터에 대해 자세히 논의해 보겠습니다.
2. 버퍼 오버플로 방지
공격자가 응용 프로그램이 예상하는 것보다 더 긴 데이터 길이를 제공하면 버퍼 오버플로가 발생하고 데이터가 내부 메모리 공간으로 오버플로됩니다. 버퍼 오버플로는 주로 C/C++ 문제입니다. 위협이지만 일반적으로 해결하기 쉽습니다. 우리는 명확하지 않고 수정하기 어려운 두 가지 버퍼 오버플로만 보았습니다. 개발자는 외부에서 제공되는 데이터가 내부 버퍼보다 클 것이라고 예상하지 않았습니다. 오버플로로 인해 메모리의 다른 데이터 구조가 손상되며, 공격자가 악성 코드를 실행하기 위해 이를 악용하는 경우가 많습니다. 배열 인덱스 오류로 인해 버퍼 언더플로우 및 오버런이 발생할 수도 있지만 이는 덜 일반적입니다.
다음 C++ 코드 조각을 살펴보세요.
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
char cBuffDest[32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}
무엇이 문제인가? 실제로 cBuffSrc 및 cbBuffSrc가 신뢰할 수 있는 소스(예: 데이터를 신뢰하지 않아 유효성과 크기를 확인하는 코드)에서 나온 경우 이 코드에는 아무런 문제가 없습니다. 그러나 데이터가 신뢰할 수 없는 소스에서 왔고 검증되지 않은 경우 공격자(신뢰할 수 없는 소스)는 쉽게 cBuffSrc를 cBuffDest보다 크게 만들고 cbBuffSrc를 cBuffDest보다 크게 설정할 수도 있습니다. memcpy가 cBuffDest에 데이터를 복사하면 DoSomething의 반환 주소가 변경되는데, 함수의 스택 프레임에서 cBuffDest가 반환 주소에 인접해 있기 때문에 공격자가 코드를 통해 일부 악의적인 작업을 수행할 수 있습니다.
보상하는 방법은 사용자의 입력을 신뢰하지 않고 cBuffSrc 및 cbBuffSrc에 전달된 모든 데이터를 신뢰하지 않는 것입니다.
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
const DWORD cbBuffDest = 32;
char cBuffDest[cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#endif
memcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));
}
이 함수는 버퍼 오버플로를 줄일 수 있는 적절하게 작성된 함수의 세 가지 속성을 보여줍니다. 첫째, 호출자가 버퍼 길이를 제공해야 합니다. 물론, 이 값을 맹목적으로 신뢰할 수는 없습니다! 다음으로, 디버그 빌드에서 코드는 버퍼가 실제로 소스 버퍼를 보유할 만큼 충분히 큰지 여부를 감지합니다. 그렇지 않으면 액세스 위반이 발생하고 코드가 디버거에 로드될 수 있습니다. 디버깅할 때 얼마나 많은 버그를 발견했는지 놀라게 될 것입니다. 마지막으로 가장 중요한 점은 memcpy에 대한 호출이 대상 버퍼가 보유할 수 있는 것보다 더 많은 데이터를 복사하지 않는다는 점에서 방어적이라는 것입니다.
Microsoft에서는 Windows® Security Push의 일환으로 C 프로그래머를 위한 안전한 문자열 처리 기능 목록을 만들었습니다. Strsafe.h: C에서 안전한 문자열 처리(영어)에서 찾을 수 있습니다.
3. 교차 사이트 스크립팅 방지
교차 사이트 스크립팅 공격은 웹의 고유한 문제로 단일 웹 페이지에 숨겨진 취약점을 통해 클라이언트 데이터에 피해를 줄 수 있습니다. 다음 ASP.NET 코드 조각의 결과를 상상해 보십시오.
<script 언어=c#>
Response.Write("안녕하세요," + Request.QueryString("이름"));
</script>
유사한 코드를 본 사람은 몇 명입니까? 하지만 놀랍게도 문제가 있습니다! 일반적으로 사용자는 다음과 유사한 URL을 사용하여 이 코드에 액세스합니다.
http://explorationair.com/welcome.aspx?name=Michael
C# 코드는 데이터가 항상 유효하고 이름만 포함한다고 가정합니다. 그러나 공격자는 스크립트와 HTML 코드를 이름으로 제공하여 이 코드를 악용할 수 있습니다. 다음 URL을 입력하시면
http://northwindtraders.com/welcome.aspx?name=<script>alert(' 안녕하세요!');
</script>
"안녕하세요!"라는 대화 상자가 있는 웹 페이지가 나타납니다. 공격자가 사용자를 속여 이와 같은 링크를 클릭하도록 할 수 있지만 쿼리 문자열에 매우 위험한 스크립트와 HTML이 포함되어 있어 사용자의 쿠키를 획득하여 해당 웹사이트로 보내는 경우를 상상해 보십시오. 이제 공격자는 귀하의 개인 쿠키 정보에 접근할 수 있습니다.
이를 방지하려면 두 가지 방법이 있습니다. 첫 번째는 입력을 불신하고 사용자 이름에 포함되는 내용을 엄격하게 제한하는 것입니다. 예를 들어 정규식을 사용하여 이름에 공통 문자 하위 집합만 포함되어 있고 너무 크지 않은지 확인할 수 있습니다. 다음 C# 코드 조각은 이 단계를 수행하는 방법을 보여줍니다.
Regex r = new Regex(@"^[w]{1,40}$")
if (r.Match(strName).Success) {
// 좋은! 문자열은 괜찮습니다
} 또 다른 {
// 좋지 않아요! 잘못된 문자열
}
이 코드는 정규식을 사용하여 문자열에 1~40개의 문자나 숫자만 포함되어 있는지 확인합니다. 이는 값이 올바른지 확인하는 유일한 안전한 방법입니다.
HTML이나 스크립트가 이 정규식을 속일 수 있는 방법은 없습니다! 잘못된 문자를 찾고 잘못된 문자가 발견되면 요청을 거부하는 데 정규식을 사용하지 마세요. 뭔가 놓치기 쉽기 때문입니다.
두 번째 예방 조치는 모든 입력을 출력으로 HTML로 인코딩하는 것입니다. 이렇게 하면 위험한 HTML 태그가 더 안전한 이스케이프 문자로 줄어듭니다. ASP.NET에서 HttpServerUtility.HtmlEncode를 사용하거나 ASP에서 Server.HTMLEncode를 사용하여 잠재적으로 문제가 있는 문자열을 이스케이프할 수 있습니다.
4. sa 권한을 요청하지 마십시오.
우리가 논의할 마지막 입력 신뢰 공격은 SQL 코드 삽입입니다. 많은 개발자는 입력을 받아 해당 입력을 사용하여 Microsoft® SQL Server™ 또는 Oracle과 같은 백엔드 데이터 저장소와 통신하는 SQL 쿼리를 작성하는 코드를 작성합니다.
다음 코드 조각을 살펴보십시오.
void DoQuery(string Id) {
SqlConnection sql=new SqlConnection(@"데이터 소스=localhost;" +
"사용자 ID=sa;비밀번호=비밀번호;");
sql.Open();
sqlstring= "SELECT 해시됨" +
" 배송지 WHERE id='" + Id + "'";
SqlCommand cmd = new SqlCommand(sqlstring,sql);
•••
이 코드에는 세 가지 심각한 결함이 있습니다. 먼저 시스템 관리자 계정 sa로 웹 서비스에서 SQL Server로의 연결을 설정합니다. 당신은 곧 이것의 함정을 보게 될 것입니다. 두 번째 포인트, sa 계정의 비밀번호를 "비밀번호"로 사용하는 현명한 습관에 주목하세요!
그러나 실제 관심사는 SQL 문을 구성하는 문자열 연결입니다. 사용자가 ID에 1001을 입력하면 완전히 유효한 다음 SQL 문이 표시됩니다.
SELECT hasshipped FROM Shipping WHERE id = '1001'
그러나 공격자는 그보다 훨씬 더 창의적입니다. ID로 "'1001' DROP table Shipping --"을 입력하면 다음과 같은 쿼리가 실행됩니다.
SELECT가 FROM에서 해시됨
배송지 ID = '1001'
DROP 테이블 전달 -- ';
쿼리 작동 방식을 변경합니다. 이 코드는 제품이 배송되었는지 확인하는 것뿐만 아니라 배송 테이블을 삭제(삭제)하는 작업도 진행합니다! 연산자 -- 공격자가 일련의 유효하지만 위험한 SQL 문을 더 쉽게 구성할 수 있도록 하는 SQL의 주석 연산자입니다!
이때 사용자가 어떻게 SQL Server 데이터베이스의 테이블을 삭제할 수 있는지 궁금할 것입니다. 물론 당신 말이 맞습니다. 관리자만이 그러한 일을 할 수 있습니다. 그러나 여기서는 sa로 데이터베이스에 연결하고 sa는 SQL Server 데이터베이스에서 원하는 모든 작업을 수행할 수 있습니다. 어떤 응용 프로그램에서도 sa로 SQL Server에 연결하지 마십시오. 적절한 경우 Windows 통합 인증을 사용하거나 적절한 권한이 있는 미리 정의된 계정으로 연결하는 것이 올바른 접근 방식입니다.
SQL 삽입 코드 문제를 해결하는 것은 쉽습니다. 다음 코드는 SQL 저장 프로시저와 매개변수를 사용하여 이러한 쿼리를 생성하는 방법과 정규식을 사용하여 입력이 유효한지 확인하는 방법을 보여줍니다. 트랜잭션에서는 배송 ID가 4~10자리여야 한다고 지정하기 때문입니다.
정규식 r = new Regex(@"^d{4,10}$");
if (!r.Match(Id).Success)
throw new Exception("잘못된 ID");
SqlConnection sqlConn= new SqlConnection(strConn);
문자열 str="sp_HasShipped";
SqlCommand cmd = new SqlCommand(str,sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID",Id);
버퍼 오버플로, 사이트 간 스크립팅 및 SQL 삽입 코드 공격은 모두 신뢰할 수 있는 입력 문제의 예입니다. 이러한 모든 공격은 달리 입증되지 않는 한 모든 입력을 유해한 것으로 간주하는 메커니즘에 의해 완화됩니다.
5. 암호화 코드에 주의하세요!
우리를 놀라게 할 수도 있는 것을 살펴보겠습니다. 우리가 조사한 보안 코드의 30% 이상이 보안 취약점을 갖고 있다는 사실을 발견했습니다. 아마도 가장 일반적인 취약점은 취약할 가능성이 있는 자신의 암호화 코드일 것입니다. 자신만의 암호화 코드를 만들지 마십시오. 그것은 어리석은 일입니다. 자신만의 암호화 알고리즘이 있다고 해서 다른 사람이 깨뜨릴 수 없다고 생각하지 마세요. 공격자는 디버거에 액세스할 수 있으며 시스템 작동 방식을 결정할 시간과 지식도 갖고 있습니다. 종종 몇 시간 내에 시스템을 중단시키는 경우도 있습니다. Win32® CryptoAPI를 사용해야 합니다. System.Security.Cryptography 네임스페이스는 우수하고 테스트된 여러 암호화 알고리즘을 제공합니다.
6. 공격을 받을 가능성을 줄입니다.
90% 이상의 사용자가 요청하지 않는 경우 기본적으로 해당 기능을 설치하지 않아야 합니다. IIS(인터넷 정보 서비스) 6.0은 이 설치 권장 사항을 따릅니다. 이 권장 사항은 이번 달에 발표된 Wayne Berry의 기사 "인터넷 정보 서비스의 혁신을 통해 보안 데이터 및 서버 프로세스를 단단히 보호할 수 있습니다"에서 읽을 수 있습니다. 이 설치 전략의 기본 개념은 사용하지 않는 서비스에 주의를 기울이지 않고 해당 서비스가 실행 중이면 다른 사람이 이를 악용할 수 있다는 것입니다. 기능이 기본적으로 설치된 경우 최소 인증 원칙에 따라 실행되어야 합니다. 즉, 필요한 경우가 아니면 관리자 권한으로 응용 프로그램을 실행하는 것을 허용하지 마십시오. 이 조언을 따르는 것이 가장 좋습니다.
7. 최소 권한 부여 원칙 사용
운영 체제와 공용 언어 런타임에는 여러 가지 이유로 보안 정책이 있습니다. 많은 사람들은 이 보안 정책이 존재하는 주요 이유가 사용자가 의도적으로 해를 끼치는 것, 즉 액세스 권한이 없는 파일에 액세스하는 것, 필요에 맞게 네트워크를 재구성하는 것, 기타 심각한 행동을 방지하는 것이라고 생각합니다. 예, 이러한 유형의 내부자 공격은 흔히 발생하므로 예방해야 하지만 이 보안 전략을 고수해야 하는 또 다른 이유가 있습니다. 즉, 사용자가 의도적이거나 (종종 발생하는) 의도하지 않은 작업을 통해 네트워크에 큰 피해를 입히는 것을 방지하기 위해 코드 주위에 방어 장벽을 구축하는 것입니다. 예를 들어, 이메일을 통해 다운로드한 첨부 파일을 Alice의 컴퓨터에서 실행하면 Alice가 액세스할 수 있는 리소스로 제한됩니다. 첨부 파일에 트로이 목마가 포함되어 있는 경우 좋은 보안 전략은 이로 인해 발생할 수 있는 피해를 제한하는 것입니다.
서버 응용 프로그램을 디자인, 구축 및 배포할 때 모든 요청이 합법적인 사용자로부터 온다고 가정할 수는 없습니다. 악의적인 사람이 악의적인 요청을 보내고(그렇지 않기를 바랍니다) 코드가 잘못 작동하는 경우 애플리케이션이 피해를 제한하기 위해 가능한 모든 방어 장치를 갖추기를 원합니다. 따라서 우리는 귀하의 회사가 귀하나 귀하의 코드를 신뢰하지 않기 때문일 뿐만 아니라 악의적인 외부 코드로부터 회사를 보호하기 위해 보안 정책을 구현하고 있다고 믿습니다.
최소 권한 부여 원칙은 코드에서 요구하는 최소 권한을 최소한의 시간 내에 부여해야 한다는 것입니다. 즉, 항상 코드 주위에 가능한 한 많은 보호벽을 세우십시오. 뭔가 나쁜 일이 발생하면 머피의 법칙이 보장하는 것처럼 보호벽이 제자리에 있다는 사실에 기뻐할 것입니다. 따라서 최소 인증 원칙을 사용하여 코드를 실행하는 몇 가지 구체적인 방법은 다음과 같습니다.
작업을 수행하는 데 필요한 리소스에만 액세스할 수 있도록 서버 코드에 대한 보안 환경을 선택하세요. 코드의 일부 부분에 높은 권한이 필요한 경우 코드의 해당 부분을 격리하고 더 높은 권한을 사용하여 별도로 실행하는 것이 좋습니다. 다른 운영 체제 인증 정보로 실행되는 이 코드를 안전하게 분리하려면 이 코드를 별도의 프로세스(더 높은 권한을 가진 보안 환경에서 실행)에서 실행하는 것이 가장 좋습니다. 이는 프로세스 간 통신(예: COM 또는 Microsoft .NET 원격)이 필요하고 왕복을 최소화하도록 해당 코드에 대한 인터페이스를 디자인해야 함을 의미합니다.
.NET Framework 환경에서 코드를 어셈블리로 분리하는 경우 각 코드 부분에 필요한 권한 수준을 고려하십시오. 쉬운 프로세스라는 것을 알게 될 것입니다. 더 높은 권한이 필요한 코드를 더 많은 권한을 제공하는 별도의 어셈블리로 분리하고 나머지 어셈블리의 대부분은 더 낮은 권한으로 실행되도록 두어 코드 주위에 더 많은 가드를 추가할 수 있습니다. 이 작업을 수행할 때 CAS(코드 액세스 보안) 스택으로 인해 자체 어셈블리뿐만 아니라 호출하는 어셈블리의 권한도 제한된다는 점을 잊지 마십시오.
많은 사람들이 새로운 구성요소를 테스트하고 고객에게 제공한 후 제품에 연결할 수 있도록 자체 애플리케이션을 구축합니다. 버그와 보안 허점을 찾기 위해 가능한 모든 코드 경로를 테스트할 수 없기 때문에 이러한 유형의 애플리케이션을 보호하는 것은 매우 어렵습니다. 그러나 애플리케이션이 호스팅되는 경우 CLR은 이러한 확장성 지점을 닫는 데 사용할 수 있는 뛰어난 기능을 제공합니다. 권한 개체 또는 권한 집합을 선언하고 PermitOnly 또는 Deny를 호출하면 호출하는 모든 코드에 대한 권한 부여를 차단하는 표시를 스택에 추가할 수 있습니다. 플러그인을 호출하기 전에 이 작업을 수행하면 플러그인이 수행할 수 있는 작업을 제한할 수 있습니다. 예를 들어 할부 결제를 위한 플러그인은 파일 시스템에 대한 액세스가 필요하지 않습니다. 이는 사전에 자신을 보호하는 최소 권한의 또 다른 예일 뿐입니다. 이러한 제한 사항에 유의하고 더 높은 권한의 플러그인은 Assert 문을 사용하여 이러한 제한 사항을 피할 수 있다는 점에 유의하십시오.
8. 실패 패턴을 인지
하고 이를 수용하세요. 다른 사람들은 당신만큼 오류 처리 코드 작성을 싫어합니다. 코드가 실패할 수 있는 이유는 너무나 많고, 그것에 대해 생각하는 것만으로도 좌절스러울 수 있습니다. 우리를 포함한 대부분의 프로그래머는 일반적인 실행 경로에 집중하는 것을 선호합니다. 그곳에서 작업이 실제로 완료됩니다. 이러한 오류 처리를 최대한 빠르고 쉽게 완료하고 실제 코드의 다음 줄로 넘어가겠습니다.
불행하게도 이 감정은 안전하지 않습니다. 대신, 코드의 실패 패턴에 더 세심한 주의를 기울여야 합니다. 이 코드는 심층적인 주의를 기울이지 않고 작성되는 경우가 많으며 완전히 테스트되지 않은 경우가 많습니다. 마지막으로 함수의 모든 작은 오류 핸들러를 포함하여 함수의 모든 코드 줄을 디버깅했다고 확신했던 때를 기억하십니까?
테스트되지 않은 코드는 보안 취약점으로 이어지는 경우가 많습니다. 이 문제를 완화하는 데 도움이 되는 세 가지 사항이 있습니다. 먼저, 작은 오류 처리기에 일반 코드와 동일한 주의를 기울이십시오. 오류 처리 코드가 실행될 때 시스템 상태를 고려하십시오. 시스템이 효율적이고 안전한 상태인가요? 둘째, 함수를 작성한 후에는 이를 단계별로 실행하고 철저하게 몇 번 디버그하여 모든 오류 처리기를 테스트하십시오. 이러한 기술을 사용하더라도 매우 미묘한 타이밍 오류가 발견되지 않을 수 있습니다. 오류 매개변수를 함수에 전달하거나 오류 핸들러가 실행될 수 있도록 어떤 방식으로든 시스템 상태를 조정해야 할 수도 있습니다. 시간을 들여 코드를 단계별로 실행하면 속도를 늦추고 코드가 실행되는 동안 시스템 상태와 코드를 볼 수 있는 충분한 시간을 가질 수 있습니다. 디버거의 코드를 주의 깊게 살펴봄으로써 우리는 프로그래밍 논리에서 많은 결함을 발견했습니다. 이는 검증된 기술입니다. 이 기술을 사용해 보세요. 마지막으로 테스트 도구 모음으로 인해 함수가 실패하는지 확인하세요. 함수의 모든 코드 줄을 검사하는 테스트 스위트를 만들어 보세요. 이는 특히 테스트를 자동화하고 코드를 빌드할 때마다 실행할 때 패턴을 찾는 데 도움이 될 수 있습니다.
실패 모드에 관해 말할 수 있는 매우 중요한 사항이 하나 있습니다. 코드가 실패할 경우 시스템이 가장 안전한 상태인지 확인하세요. 문제가 있는 코드 중 일부는 다음과 같습니다.
bool accessGranted = true; // 너무 낙관적입니다!
노력하다 {
// c:test.txt에 액세스할 수 있는지 확인합니다.
새로운 FileStream(@"c:test.txt",
파일 모드.열기,
FileAccess.Read).Close();
}
잡기(SecurityException x) {
// 접근이 거부되었습니다.
접근권한 부여 = 거짓;
}
잡다 (...) {
// 다른 일이 일어났습니다.
}
CLR을 사용하고 있더라도 여전히 파일에 액세스할 수 있습니다. 이 경우 SecurityException이 발생하지 않습니다. 하지만 예를 들어 파일의 DACL(임의 액세스 제어 목록)이 액세스를 허용하지 않으면 어떻게 될까요? 이때 다른 유형의 예외가 발생합니다. 그러나 코드 첫 번째 줄의 낙관적인 가정으로 인해 우리는 이를 결코 알 수 없습니다.
이 코드를 작성하는 더 좋은 방법은 주의하는 것입니다.
bool accessGranted = false; // 주의하세요!
노력하다 {
// c:test.txt에 액세스할 수 있는지 확인합니다.
새로운 FileStream(@"c:test.txt",
파일 모드.열기,
FileAccess.Read).Close();
// 아직 여기 있다면 좋습니다!
액세스 권한 부여 = true;
}
catch (...) {}
아무리 실패하더라도 항상 가장 안전한 모드로 돌아가기 때문에 이는 더 안정적입니다.
9. 가장은 매우 취약합니다.
서버 응용 프로그램을 작성할 때 가장이라는 Windows의 편리한 기능을 직간접적으로 사용하는 경우가 많습니다. 가장을 사용하면 프로세스의 각 스레드가 일반적으로 클라이언트의 보안 환경과 같은 다른 보안 환경에서 실행될 수 있습니다. 예를 들어 파일 시스템 리디렉터가 네트워크를 통해 파일 요청을 받으면 원격 클라이언트를 인증하고 클라이언트의 요청이 공유의 DACL을 위반하지 않는지 확인한 다음 클라이언트의 플래그를 요청을 처리하는 스레드에 연결합니다. . 클라이언트를 시뮬레이션합니다. 그러면 이 스레드는 클라이언트의 보안 환경을 사용하여 서버의 로컬 파일 시스템에 액세스할 수 있습니다. 로컬 파일 시스템이 이미 안전하기 때문에 편리합니다. 요청된 액세스 유형, 파일의 DACL 및 스레드의 가장 플래그를 고려하여 액세스 확인을 수행합니다. 액세스 확인에 실패하면 로컬 파일 시스템은 이를 파일 시스템 리디렉터에 보고한 다음 원격 클라이언트에 오류를 보냅니다. 이는 파일 시스템 리디렉터에게 의심할 여지 없이 편리합니다. 요청을 로컬 파일 시스템에 전달하고 마치 클라이언트가 로컬인 것처럼 자체 액세스 확인을 수행할 수 있기 때문입니다.
이는 파일 리디렉터와 같은 간단한 게이트웨이에 적합하고 좋습니다. 그러나 시뮬레이션은 더 복잡한 다른 응용 프로그램에서도 자주 사용됩니다. 웹 애플리케이션을 예로 들어보겠습니다. 관리되지 않는 일반적인 ASP 프로그램, ISAPI 확장 또는 ASP.NET 응용 프로그램을 작성하고 Web.config 파일
<identity impersonate='true'>
에 다음 사양이 있는경우 실행 환경에는 두 가지 다른 보안 환경이 있습니다. 프로세스 태그와 스레드 태그 일반적으로 스레드 태그는 액세스 확인에 사용됩니다(그림 3 참조). 웹 서버 프로세스에서 실행되는 ISAPI 애플리케이션을 작성하고 대부분의 요청이 인증되지 않았다고 가정하면 스레드 태그는 IUSR_MACHINE일 수 있지만 프로세스 태그는 SYSTEM입니다! 버퍼 오버플로를 통해 악의적인 행위자가 코드를 악용할 수 있다고 가정해 보겠습니다. IUSR_MACHINE으로 실행하는 것만으로도 만족할 것이라고 생각하십니까? 물론 그렇지 않습니다. 그의 공격 코드는 RevertToSelf를 호출하여 그의 권한 수준을 높이기 위해 가장 플래그를 제거할 가능성이 높습니다. 이 경우 그는 쉽게 성공할 것입니다. CreateProcess를 호출할 수도 있습니다. 새 프로세스의 태그를 가장 태그에서 복사하는 것이 아니라 프로세스 태그에서 복사하여 새 프로세스가 SYSTEM으로 실행될 수 있도록 합니다.
그렇다면 이 작은 문제를 어떻게 해결할까요? 애초에 버퍼 오버플로가 발생하지 않도록 하는 것 외에도 최소 인증 원칙을 기억하세요. 코드에 SYSTEM만큼 큰 권한이 필요하지 않은 경우 웹 서버 프로세스에서 실행되도록 웹 응용 프로그램을 구성하지 마십시오. 중간 또는 높은 격리 환경에서 실행되도록 웹 애플리케이션을 구성하는 경우 프로세스 태그는 IWAM_MACHINE이 됩니다. 실제로 권한이 없으므로 이 공격은 거의 효과가 없습니다. IIS 6.0(곧 Windows .NET Server의 구성 요소가 될 예정)에서는 사용자가 작성한 코드가 기본적으로 SYSTEM으로 실행되지 않습니다. 개발자가 실수할 수 있다는 점을 이해하면 코드에 보안 문제가 있는 경우 웹 서버가 코드에 부여된 권한을 줄이는 데 도움이 될 수 있습니다.
COM 프로그래머가 직면할 수 있는 또 다른 함정은 다음과 같습니다. COM은 스레드를 무시하는 나쁜 경향이 있습니다. In-Process COM 서버를 호출했는데 해당 스레드 모델이 호출 스레드의 모델과 일치하지 않으면 COM은 다른 스레드에서 호출을 수행합니다. COM은 호출자 스레드에 가장 플래그를 전파하지 않으므로 결과적으로 호출은 호출 스레드의 보안 컨텍스트가 아닌 프로세스의 보안 컨텍스트에서 실행됩니다. 놀랐는 걸!
시뮬레이션의 함정에 대한 또 다른 예는 다음과 같습니다. 서버가 명명된 파이프, DCOM 또는 RPC를 통해 전송된 요청을 수락한다고 가정합니다. 클라이언트를 인증하고 가장하여 클라이언트를 대신하여 커널 개체를 엽니다. 그리고 클라이언트 연결이 끊어졌을 때 개체(예: 파일) 중 하나를 닫는 것을 잊었습니다. 다음 클라이언트가 들어오면 이를 인증하고 가장하며 무슨 일이 일어날지 추측해 보세요. 새 클라이언트가 파일에 대한 액세스 권한을 얻지 못한 경우에도 이전 클라이언트가 "누락한" 파일에 계속 액세스할 수 있습니다. 성능상의 이유로 커널은 객체가 처음 열릴 때만 객체에 대한 액세스 확인을 수행합니다. 다른 사용자를 가장하고 있기 때문에 나중에 보안 환경을 변경하더라도 이 파일에 계속 액세스할 수 있습니다.
위에서 언급한 상황은 모두 시뮬레이션이 서버 개발자에게 편리함을 제공하지만 이러한 편리함에는 큰 위험이 숨겨져 있음을 상기시키기 위한 것입니다. 모의 플래그를 사용하여 프로그램을 실행할 때는 코드에 세심한 주의를 기울이십시오.
10. 관리자가 아닌 사용자가 실제로 사용할 수 있는 애플리케이션을 작성하십시오.
이는 실제로 최소 인증 원칙의 결과입니다. 프로그래머가 관리자가 Windows에서 제대로 실행해야 하는 코드를 계속 개발한다면 시스템 보안 향상을 기대할 수 없습니다. Windows에는 매우 견고한 보안 기능 세트가 있지만 사용자는 이를 운영하기 위해 관리자가 되어야 하는 경우 이러한 기능을 활용할 수 없습니다.
어떻게 개선할 수 있나요? 먼저 관리자 권한으로 실행하지 말고 직접 시도해 보세요. 보안을 염두에 두고 설계되지 않은 프로그램을 사용하는 데 따른 어려움을 곧 알게 될 것입니다. 어느 날 나(Keith)는 내 데스크탑 컴퓨터와 휴대용 장치 사이의 데이터를 동기화하는 휴대용 장치 제조업체가 제공한 소프트웨어를 설치했습니다. 평소와 같이 일반 사용자 계정에서 로그아웃하고 내장된 관리자 계정으로 다시 로그인하여 소프트웨어를 설치한 후 다시 일반 계정으로 로그인하여 소프트웨어를 실행해 보았습니다. 그 결과, 애플리케이션은 필요한 데이터 파일에 접근할 수 없다는 대화 상자를 띄운 후 접근 위반 메시지를 표시합니다. 친구 여러분, 이것은 주요 휴대용 장치 제조업체의 소프트웨어 제품입니다. 이 실수에 대한 변명의 여지가 있습니까?
http://sysinternals.com (영문)에서 FILEMON을 실행한 후 응용 프로그램이 응용 프로그램의 실행 파일 중간과 동일한 디렉터리에 설치된 쓰기 액세스를 위해 데이터 파일을 열려고 한다는 것을 빨리 발견했습니다. 응용 프로그램이 예상대로 Program Files 디렉터리에 설치된 경우 해당 디렉터리에 데이터를 쓰려고 시도하면 안 됩니다. Program Files에는 이유가 있는 제한적인 액세스 제어 정책이 있습니다. 우리는 사용자가 이러한 디렉터리에 쓰기를 원하지 않습니다. 이렇게 하면 한 사용자가 다른 사용자가 실행할 트로이 목마를 쉽게 남길 수 있기 때문입니다. 실제로 이 규칙은 Windows XP의 기본 서명 요구 사항 중 하나입니다( http://www.microsoft.com/winlogo [영어] 참조).
코드를 개발할 때 관리자 권한으로 실행하기로 선택한 이유에 대해 변명하는 프로그래머가 너무 많다는 것을 들었습니다. 우리가 이 문제를 계속해서 무시한다면 상황은 더욱 악화될 뿐입니다. 친구 여러분, 텍스트 파일을 편집하는 데 관리자 권한이 필요하지 않습니다. 프로그램을 편집하거나 디버그하는 데에도 관리자 권한이 필요하지 않습니다. 관리자 권한이 필요한 경우 운영 체제의 RunAs 기능을 사용하여 玎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [English]를 실행
하십시오. 프로그래머는 변명을 너무 많이 합니다. 코드를 개발할 때 관리자 권한으로 실행하는 이유 이 문제를 계속 무시하면 상황이 더욱 악화될 뿐입니다. 또는 프로그램을 디버깅하는 데 관리자 권한이 필요하지 않습니다. 권한이 있는 경우 운영 체제의 RunAs 기능을 사용하여 높은 권한으로 별도의 프로그램을 실행하십시오(2001년 11월 보안 브리핑 [영어]. 칼럼 참조). 개발자용 도구를 작성하는 경우 이 그룹에 대한 추가 책임이 필요합니다. 관리자로만 실행할 수 있는 코드 작성의 악순환을 멈추십시오.
개발자가 비관리자로 쉽게 실행할 수 있는 방법에 대한 자세한 내용은 Keith의 웹 사이트 (http://www.develop)를 참조하십시오. com/kbrown (영문) 비관리자 환경에서 잘 실행되는 응용 프로그램을 작성하는 방법에 대한 팁을 제공하는 Michael의 저서 Writing Secure Code(Microsoft Press, 2001)를 확인하십시오.