Keith Brown

이 글은 2004년 3월 ASP.NET 2.0 커뮤니티 테크놀로지 프리뷰 내용에서 뽑은 것입니다. 여기 다뤄진 모든 정보는 변경될 수도 있습니다.

주제:
  • ASP.NET 2.0의 향상된 보안 기능
  • 서버 쪽 보안 컨트롤
  • 사용자 및 역할 데이터베이스
  • 쿠키없는 폼 인증

새로운 보안 기능으로 ASP.NET 2.0이 크게 개선되었습니다. 이러한 보안 기능에는 사용자 계정 데이터베이스를 관리하는 구성원 서비스, 해시 암호, 사용자의 역할 구성원을 관리하는 역할 관리자(role manager), 그리고 폼 인증을 훨씬 용이하게 해주는 5개의 새로운 서버 쪽 컨트롤이 있습니다. ASP.NET 2.0은 또한 공급자 모델을 제공하므로 구성원 및 역할 서비스(Membership and Role services) 구현과 쿠키없는 폼 인증을 완벽히 제어할 수 있습니다. 또한 사용자는 웹 기반의 편리한 관리 기능을 통해 사용자 계정 및 역할을 로컬이나 원격으로 간단히 관리할 수 있으며 기타 보안되지 않은 설정에 대한 관리도 강력하게 제어할 수 있습니다.

폼 인증 향상
  폼 인증은 수많은 구현 사례에서 놓쳤던 최상의 방식들을 한 데 모아 놓은 기능이기 때문에 ASP.NET 1.0에서 가장 인기 있는 기능 중 하나로 꼽히고 있습니다. 예를 들어, 클라이언트의 자격 증명 보관에 사용되는 쿠키의 무결성을 보호하기 위해 폼 인증 구현이 얼마나 필요한지 알고 있습니까? 폼 인증은 사용자의 이름을 쿠키에 작성할 뿐만 아니라 메시지 인증 코드(쿠키에서 만들어지는 해시와 웹 서버만 인식하고 있는 비밀 값)를 추가합니다. 이를 통해 악의적인 클라이언트가 권한을 높이거나 쿠키에 있는 이름을 바꿈으로써 다른 사용자의 정보를 보는 것을 막을 수 있습니다.
  만약 여러 가지 뉴스 그룹을 통해 .NET 웹 개발자가 어떤 서버에 시간을 쏟고 있는지 살펴본다면 아마 대부분 같은 작업을 반복해서 실행하고 있다는 것을 알게 될 것입니다. 그것은 바로 사용자 데이터베이스, 쿠키에 캐시된 역할, 사용자 이름 및 암호를 캡처하기 위한 컨트롤, 그리고 사용자 및 역할을 관리하는 관리 도구 등일 것입니다. ASP.NET 팀은 기본으로 제공되는 솔루션을 통해 사실상 이러한 모든 문제를 해결하고 있습니다. 필자는 ASP.NET 2.0의 알파 빌드를 연구하면서 폼 인증을 효율적으로 관리할 수 있는 웹 사이트를 만드는 데 필요한 코드의 수가 급격히 줄어드는 것에 놀라기도 하였습니다.

시작
  이제 몇 가지 실험을 보여드릴 텐데 이것은 MSND Universal 가입자만 다운로드할 수 있는 ASP.NET 2.0 비트가 있어야 실행할 수 있습니다. 이를 통해 얼마나 쉽게 새로운 기능을 사용할 수 있는지 알게 될 것입니다.
  우선 빈 디렉터리를 가리키는 가상 디렉터리가 필요합니다. 그리고 ASP.NET 작업자 프로세스가 읽기, 실행하기, 쓰기 권한을 갖고 있는지 확인합니다. 만약 Windows 2000이나 Windows XP를 실행하고 있다면 이 권한을 ASPNET 로컬 계정에 부여하고, Windows Server 2003을 실행할 경우에는 네트워크 서비스 계정에 권한을 부여합니다.
  이제 web.config 파일을 사용하여 폼 인증을 보여드릴 것입니다. ASP.NET 1.1 사용 방법을 보여드리는 것이라면 텍스트 편집기를 열어서 손으로 XML을 입력했을 것입니다. 그러나 ASP.NET 2.0에서는 가장 만족스러운 기능인 대화형 구성 파일 편집기를 사용합니다. 이 편집기는 IIS 관리 콘솔에 바로 내장되어 있으며, IIS 관리 콘솔은 가상 디렉터리에서 속성 시트의 "ASP.NET" 탭에서 찾을 수 있습니다. "Edit configuration" 단추를 누르면 편집기가 표시됩니다.

Figure 1 Configuration Editor
그림 1 구성 편집기

  그림 1에는 이러한 새 편집기가 나와 있으며, 기본 설정된 Windows 인증 대신 폼 인증을 선택한 것을 보실 수 있습니다. 여러분의 가상 디렉터리에서도 이렇게 하시기 바랍니다. 구성 도구에서 웹 응용프로그램에 필요한 기본 언어를 C#으로 설정해야 하는데, 이렇게 하면 나중에 입력하는 수고를 덜 수 있습니다. Page Language Default 설정은 Application 탭에서 첫 번째 드롭다운 메뉴에 있습니다. 이런 변동 사항을 적용하고 나면 모든 설정과 함께 디렉터리에 web.config 파일이 생성됩니다.
  구성원(Membership) 서비스를 시작하려면 우선 몇 명의 사용자를 등록해야 하므로, 처음으로 작성할 페이지에서 사용자를 추가합니다. 아래와 같이 베타로 사용할 수 있는 서버 컨트롤이 있으며 이 서버 컨트롤을 통해 3개의 코드 줄로 이 페이지를 실행할 수 있습니다.:
<form runat='server'>
  <asp:createuser runat='server'/>
</form>
하지만 현재 알파 비트를 사용하고 있으므로, 구성원(Membership) 클래스를 바로 사용할 수 있도록 손으로 이 특정 폼을 코딩해야 합니다. 지금은 그림 2 에 나온 것처럼 ASPX 페이지를 이용해서 구성원(Membership) 클래스에 대해 설명하도록 하겠습니다. 이 페이지를 브라우저를 통해서 보면 그림 3과 같이 나타납니다. 여기서 몇 명의 사용자 및 암호를 추가하십시오. 여기서 즉시 작동되기 때문에 작업이 훨씬 쉬울 것입니다.

Figure 3 Membership Page
그림 3 구성원(Membership) 페이지

  일단 사용자 추가 작업이 끝나면 가상 디렉터리를 자세히 살펴 보십시오. 그러면 Microsoft Access 데이터베이스가 있는 "DATA"라는 새로운 하위 디렉터리를 볼 수 있습니다. 이 디렉터리가 바로 구성원(Membership) 및 역할(Role) 서비스가 기본적으로 데이터를 저장하는 곳입니다. 그러나 나중에 이러한 기본 저장 매커니즘을 무시하고 SQL Server나 사용자 고유의 사용자 지정 리포지토리를 어떻게 이용할 수 있는지 보여드리겠습니다. 이제 ASP.NET 2.0에 제공되는 보안 컨트롤을 사용해 보겠습니다.

서버 쪽 보안 컨트롤
  그림 4 에는 ASP.NET 2.0에서 사용할 수 있는 새로운 5개의 보안 컨트롤이 나열되어 있습니다. 일단 이것을 살펴보려면 LoginStatus 컨트롤을 먼저 살펴보는 것이 좋습니다. 이 컨트롤을 사용하여 새로운 ASPX 페이지를 만들어 보십시오. 그리고 이 새로운 페이지는 간단히 default.aspx라고 부르겠습니다.:
<form runat='server'>
  <asp:loginstatus runat='server'/>
</form>

   이 페이지를 브라우저를 통해서 보면 Login 링크가 표시됩니다. 브라우저에서 결과 페이지의 소스를 보면, 이 하이퍼링크가 아직 작성하지 않은 login.aspx라는 페이지를 가리키고 있는 것을 볼 수 있습니다. 이것은 3줄짜리 웹 페이지입니다. 지금 만들어 보십시오.:
<form runat='server'>
  <asp:login runat='server'/>
</form>
만약 여기까지 수작업으로 폼 인증을 구현했다면 이 세 줄의 코드가 얼마나 유용한지 알게 될 것입니다. 과거에는 데이터베이스 조회를 수행하는 데 훨씬 많은 양의 코드가 있는 두 개의 명령이 필요했습니다.
  이제 브라우저로 돌아가서 Login 링크를 클릭하십시오. 그러면 그림 5와 같이 로그인 페이지가 나타납니다. 잘못된 사용자 이름이나 암호를 넣어 로그인한 후 이에 해당되는 기본 오류 메시지가 나타나는지 확인하십시오. 이 메시지는 공격자에게 그다지 많은 정보를 주지는 않습니다. 또한 순진한 개발자가 실수로 메시지를 사용자에게 다시 보내서, 사용자 이름을 바로 받았으며 다른 암호를 생각해보라고 말할 가능성도 없습니다.

Figure 5 Login Page
그림 5 로그인 페이지

  이제 올바른 사용자 이름과 암호를 입력하십시오. 바로 이전에 adduser.aspx 페이지 때 입력했던 것을 입력하면 됩니다. 그러면 default.aspx 페이지로 다시 리디렉션됩니다. 로그인 컨트롤에 어떤 사용자 지정 동작도 제공하지 않았기 때문에 폼 인증을 이용하게 되면 기본적으로 사용자 동작을 기록하게 되는데, 이것은 브라우저에 사용자 이름을 유지하는 암호화된 쿠키가 있다는 것을 뜻합니다.
  이제 default.aspx 페이지로 리디렉션되었는데 뭔가 차이점이 보이십니까? 로그인 상태 컨트롤은 로그인 대신에 로그아웃을 표시하고 있을 것입니다. 폼 인증 쿠키가 그러한 요청을 통해 보내졌으므로 FormsAuthenticationModule은 인증된 사용자 이름을 만들고 그것을 요청의 컨텍스트와 연관시켰습니다. 그러면 로그인 상태 컨트롤은 이것을 인지하고 로그아웃할 수 있도록 변경합니다. 로그아웃한 후에 다시 로그인 해서 이 작업이 제대로 이루어지는지 확인해보시기 바랍니다.
  이제 default.aspx 페이지에 코드를 몇 개 더 추가해보겠습니다.:
<h3>User Name: <%= User.Identity.Name %>
</h3>
<h3>User Type: <%= User.GetType() %></h3>

   페이지를 새로 고치면 해당 페이지에서 로그인했던 사용자 이름이 표시됩니다. 사용자 이름을 표현하는 기본 개체가 바로 GenericPrincipal 유형의 개체이며 이것이 바로 FormsAuthenticationModule이 사용자를 표현하는 방법이라는 것을 염두에 두십시오. 일단 역할 관리자(Role Manager)를 켜면 이런 유형 변화를 볼 수 있는데, 그 이유는 역할 관리자(Role Manager)가 실행될 때 FormsAuthentication이 고유의 유형으로 만들었던 사용자 이름을 새로운 RoleManagerModule이 대체하기 때문입니다.
  이제 default.aspx에 LoginView 컨트롤을 추가해보겠습니다. default.aspx는 사용자 로그인에 따라 대체 콘텐츠를 표시해 줍니다. 가장 쉽게 이 컨트롤을 사용하는 방법은 두 개의 콘텐츠를 제공하는 것입니다. 하나는 사용자가 로그인하기 전 익명의 요청을 위한 것이고, 나머지 하나는 사용자가 로그인한 후 인증된 요청을 위한 것입니다. :
<asp:loginview runat='server'>
  <anonymoustemplate>
    <h4>If you see this, you've not yet logged in!</h4>
  </anonymoustemplate>
  <loggedintemplate>
    <h4>Welcome to my website, <asp:loginname runat='server'/>!</h4>
  </loggedintemplate>
</asp:loginview>
로그인하고 로그아웃할 때 예상대로 LoginView 컨트롤에서 텍스트가 바뀌는 것을 볼 수 있을 것입니다. 이것은 매우 간단한 개념이지만 코드를 훨씬 명확하게 해줍니다.

역할 정의
  이제 간단한 페이지를 만들었으므로 역할 관리자(Role Manager)를 사용하여 역할에 사용자를 추가할 수 있습니다. 그러나 우선 응용 프로그램에 대한 역할 관리자(Role Manager)를 활성화해야 합니다. 구성 도구로 다시 돌아가서 Authentication 탭을 찾은 다음, "Role management enabled" 상자를 체크한 후 적용하십시오.
  addrole.aspx 페이지에 대한 코드는 그림 6 에 나와 있으며 그림 7은 그것이 어떤 형태인지 보여줍니다. 이 페이지를 가상 디렉터리안에 넣고 브라우저를 통해 보면 역할을 추가할 수 있습니다. 역할 이름과 함께 이전에 adduser.aspx 폼에서 추가했던 사용자 이름을 지정하고, 단추를 누르면 사용자가 그 역할에 추가됩니다. 아직 존재하진 않지만 코드가 우선 역할을 추가하고 그 다음 사용자를 역할에 추가합니다. 그러는 동안 역할 관리자(Role Manager)는 구성원(Membership) 서비스가 사용한 동일한 Microsoft Access 데이터베이스에서 역할 매핑을 추적합니다. 그러나 이것은 그야말로 우연의 일치일 뿐입니다. 역할 관리자(Role Manager)는 SQL Server나 다른 특정 저장소에서 데이터를 저장할 수 있으며 구성원(Membership) 서비스와 똑같은 매커니즘을 사용할 필요는 없습니다. 구성원(Membership)과 역할 관리자(Role Manager)를 위한 공급자 모델은 분명히 이를 위해서 필요한 것입니다.

Figure 7 Add Role
그림 7 역할 추가

  ASP.NET에서 사용자 지정 역할을 구현한 적이 있다면, 기본으로 제공되는 역할 관리자(Role Manager) 덕분에 역할 기반의 보안을 설정하기 위해 더 이상 ASP.NET HTTP 파이프라인에 대해 배우지 않아도 된다는 사실에 감사할 것입니다. 일단 몇 개의 역할을 추가한 다음 다시 default.aspx로 돌아가서 LoginView 컨트롤을 사용해 봅시다. <loggedintemplate/> 요소 다음에 아래 섹션을 추가하십시오.:
<rolegroups>
  <asp:rolegroup roles='ForumModerators'>
    <contenttemplate>
      <h4>Controls for forum moderators
          go here.</h4>
    </contenttemplate>
  </asp:rolegroup>
  <asp:rolegroup roles='Friends'>
    <contenttemplate>
      <h4>Welcome, friend!</h4>
    </contenttemplate>
  </asp:rolegroup>
</rolegroups>

   아마도 필자가 선택한 것과 동일한 역할을 선택하지 않았기 때문에 필자의 역할 이름을 여러분의 역할 이름으로 바꾸고 그 역할에 맞도록 콘텐츠를 조정해야 할 것입니다. 일단 작업이 끝나면 새로운 역할에 새로운 사용자 계정으로 로그인해서 새로운 페이지를 시도해 보십시오. 그리고 역할이 바뀌면서 그 페이지의 콘텐츠가 어떻게 바뀌는지 살펴 보십시오. 두 개의 역할 그룹이 해당 사용자의 역할과 일치할 경우 위에서 아래로 제일 처음 일치하는 역할 그룹이 항상 나타날 것입니다.
  새로운 것은 아니지만 User.IsInRole을 통해서 프로그래밍 방식으로 역할을 항상 테스트할 수 있음을 기억하시기 바랍니다. 또한 web.config의 <authorization/> 섹션에서 여러 페이지에 대한 액세스 권한을 부여하거나 거부할 수 있습니다.:
<authorization>
  <deny users='?'/>
  <allow roles='ForumModerators'/>
  <deny users='*'/>
</authorization>

   첫 번째 항목은 ASP.NET이 인증되지 않은 요청(강제 인증)을 바운스하도록 합니다. 두 번째와 세 번째 항목은 web.config 파일이 존재하는 디렉터리 트리에 오직 ForumModerators만이 엑세스할 수 있도록 해줍니다. 권한 부여(authorization) 섹션은 하위 디렉터리에 있는 web.config 파일에서도 사용할 수 있으며 또한 개별 파일에 대한 액세스를 제어하는 요소에서도 사용할 수 있습니다.

암호 복구
  이 소개 데모에서 암호 복구 컨트롤에 대해 보여드리지 않았는데 그 이유는 이 작업에는 상당한 주의가 필요하기 때문입니다. 아마도 이 컨트롤이 무엇인지는 잘 알고 있으리라 생각합니다. 이 컨트롤을 사용하면 사용자가 암호를 전자 메일로 받을 수 있도록 요청할 수 있습니다. 사용자에게 일반 텍스트 암호를 전자 메일로 보내기 전에 일단 위험 요소를 분석해야 합니다.
  사실, 이 컨트롤을 이미 존재하는 사이트의 페이지에서 사용할 경우 구성원(Membership) 서비스가 기본적으로 일반 텍스트 암호의 공개를 거부하기 때문에 작동하지 않습니다. 구성원(Membership) 서비스는 기본적으로 암호 자체가 아닌 암호의 한쪽 해시만을 저장하기 때문에 일반 텍스트 암호를 공개할 수가 없는 것입니다. 암호 확인 요청을 받으면 구성원 서비스는 제시된 암호를 해시한 다음 해시 값과 복사본을 비교합니다. 만약 일반 텍스트 암호를 복구하기를 원한다면 구성원(Membership) 공급자를 재구성해서 암호를 암호화된 형태로 저장하면 되는데 그럴 경우 공급자는 <machineKey/>를 사용해 암호를 암호화합니다. 따라서 암호를 해독한 후 사용자에게 전자 메일로 보낼 수 있게 됩니다.
  만약 해시 암호를 저장한다면, 좋은 생각이긴 하지만 사용자 인증에 대한 대안책을 준비해야 합니다. 사용자에게 암호를 전자 메일로 보낼 수는 없지만, 만약 "당신이 가장 좋아하는 애완 동물의 이름은?"과 같이 미리 어떤 질문을 한다면 그 답을 이용해서 사용자 인증을 하고 사용자에게 새로운 암호를 보내줄 수 있습니다. 구성원(Membership) 서비스가 각 사용자에 해당하는 질문과 답을 보유하고 있지만, 이것은 해당 암호를 전자 메일로 보내야 할지를 결정하는 데만 사용되므로 해시 암호로는 이용할 수 없습니다. 필자의 의견으로는 이 영역에는 추가적인 작업이 필요할 것이라 생각됩니다.
  질문과 답을 통해서 암호를 재설정하는 이와 같은 바람직한 모델은 Viega와 Mcgraw가 제안한 모델로서 Building Secure Software (Addison-Wesley, 2002)의 95페이지에 나와 있습니다. 이 모델에서는 수백 개의 질문 모음을 사용하여 사용자가 계정을 처음 설정할 때 임의로 몇 개의 질문을 선택합니다. 그런 다음 사용자가 암호 재설정을 요청할 경우 차례로 이들 질문을 사용자에게 물어봅니다. 사용자는 정확한 진행을 위해서 많은 질문에 대답해야 합니다. 사용자가 답을 맞춘다면 새로운 임의의 질문 집합을 선택해서 이전에 사용했던 질문과 바꿉니다.

공급자 조정
  지금까지 모든 것을 간단히 하기 위해서 기본 설정을 사용했지만 이제 여러분의 환경에 맞게 이러한 설정을 조정해야 합니다. 예를 들면, 구성원(Membership) 서비스에서 데이터를 SQL Server에 저장하고자 한다면 기본 설정인 AspNetAccessProvider 대신에 AspNetSqlProvider을 선택해야 합니다. 이 설정은 구성 도구의 Authentication 페이지에 나옵니다.
  그러나 통합해야 하는 기존 사용자 데이터베이스가 이미 있다면 어떻게 해야 할까요? AspNetSqlProvider에 필요한 표 또는 열은 당연히 없습니다. 게다가 데이터베이스가 AS/400 서버나 Oracle에 설치되어 있다면 어떻게 해야 할까요? 다행히도 그림 8 에 나타난 것처럼 구성원(Membership)과 역할 관리자(Role Manager) 시스템이 계층화된 모델로 갖춰져 있습니다. System.Web.Security 네임스페이스에 정의된 추상 MembershipProvider 클래스를 확대하여 구성원(Membership) 데이터 저장을 완전하게 대체할 수 있습니다. 마찬가지로 RoleProvider를 확대하여 역할 관리자(Role Manager) 데이터 저장을 대체할 수 있습니다. Rob Howard의 "'Nothin' But ASP.NET" 칼럼에서는 이 공급자 모델에 대해 더욱 상세히 설명되어 있습니다.

Figure 8 Provider Model
그림 8 공급자 모델

  기존 공급자를 사용하는 것이 가장 쉬운 방법입니다. 알파 버전에는 두 개의 공급자가 있습니다. 하나는 Access 데이터베이스에서 작동하며 이미 알고 있는 바와 같이, 별도의 구성 없이도 곧바로 잘 작동합니다. 나머지 하나는 앞에서 언급한 SQL Server 공급자입니다. 베타 버전에는 Active Directory에 대조하여 사용자를 확인하는 구성원(Membership) 공급자와 승인 관리자(Authorization Manager)로부터 역할을 조회하는 역할(Role) 공급자가 있습니다.
  이렇게 이미 내장된 공급자 중 하나를 선택하더라도 web.config에서 동작을 조정할 수 있습니다. 그림 9는 SQL Server 구성원 공급자의 공급자 조정을 보여줍니다. PasswordFormat 설정에서 해시(기본 설정), 암호화, 일반의 세 가지의 선택 사항이 있습니다. 그런 다음 enablePasswordRetrieval와 requiresQuestionAndAnswer 특성으로 암호 회수에 대한 정책을 선택합니다. 물론 해시 암호를 사용하기로 선택한 경우에는 enablePasswordRetrieval를 false로 설정해야 합니다. 그렇지 않은 경우에는 시스템에서 암호를 전자 메일로 보내기 전에 사용자에게 질문에 대답하도록 요구해야 합니다.

Figure 9 Provider Settings
그림 9 공급자 설정

  이 데이터베이스를 위한 연결 문자열은 web.config 파일에 저장되지 않으며 대신 간접적으로 참조됩니다. 이 속성을 connectionStringName이라고 부르며 연결 문자열을 확보하기 위해 특별히 고안된 machine.config 섹션을 가리킨다는 것을 알아두어야 합니다. 연결 문자열을 web.config에 포함되지 않도록 하면, 통합 인증을 사용할 수 없는 상황에서 암호를 사용해야만 하는 경우에 특히 유용합니다. ASP.NET 2.0은 구성 파일의 중요한 섹션을 위해 XML 암호화를 지원할 계획입니다. 이것은 machine.config의 연결 문자열 섹션에 필요한 대단히 편리한 기능입니다.
  쿠키 또는 URL 다시 쓰기(munging)를 이용할 수 있도록 역할 관리자(Role Manager)를 구성할 수도 있으며 역할 데이터베이스로 왕복하는 시간을 줄이기 위해서 쿠키에 있는 역할을 캐시할 수도 있습니다. 캐싱은 지능적으로 기능합니다. 만약 캐시된 역할이 너무 커지기 시작하면 역할 관리자(Role Manager)가 쿠키에서 가장 최근에 사용된 역할을 캐시하고 가장 덜 사용된 역할을 적극적으로 찾습니다. 이 기능은 아마도 제한된 저장 용량으로 모바일 장치를 지원해야 하는 필요성 때문에 개발되었을 것입니다.
  이 외에도 조정할 수 있는 다른 많은 설정들이 많이 남아 있지만 각자 스스로 연구해 보시기 바라며 여기서 마무리하겠습니다. 그러면 이제 앞에서 사용한 서버 쪽 보안 컨트롤을 조절할 수 있는 방법을 살펴보겠습니다.

컨트롤 조정
  로그인 페이지를 단 세 줄의 코드로 작성하면 깔끔하지만, 일반적으로 응용 프로그램에 적합하도록 로그인 컨트롤을 지정해야 할 필요가 있습니다. 그림 10 은 앞에서 만든 단순한 로그인 페이지를 대체하는 데 사용할 수 있는 코드를 보여줍니다. 또한 이런 컨트롤의 모양은 웹 컨트롤에서 기대할 수 있는 모든 속성을 동원해서 수정할 수 있습니다. 또한 ASP.NET 2.0에 포함된 테마 지원을 통해 코드를 바꾸지 않고도 웹 사이트 전체에 걸쳐 일관성 있는 형태를 유지할 수 있습니다.
  로그인 컨트롤의 한 가지 흥미로운 기능은 이 예제에서 본 바와 같이 자체의 페이지에 고착되어 있지 않아도 된다는 점입니다. 그 대신 여백에 항상 나타나도록 자체 페이지를 마스터 페이지의 일부로 만들 수 있습니다. 일단 사용자가 로그인한 후에는 페이지를 계속해서 나타나게 할 필요가 없으므로, 인증된 사용자가 이미 존재한다고 감지하면 페이지가 바로 사라지도록 기본 설정되어 있습니다. 이 동작은 VisibleWhenLoggedIn 속성을 통해서도 조정할 수 있습니다. 이 기능은 ASP.NET 1.1에서는 개발자가 수동으로 실행했던 기능이지만 지금은 ASP.NET 2.0에 포함되어 있습니다.
  다른 컨트롤에도 유사한 옵션이 있습니다. 예를 들면, 사용자가 로그인하거나 로그아웃할 때 예쁜 단추를 보여주고 싶다면 로그인 상태 컨트롤에 Login(Out)ImageUrl 속성을 설정하면 됩니다.
  이 모든 작업이 어떻게 일어나는지 알아보려면 Visual Studio 2005 프로젝트 마법사를 사용해서 인터넷 웹 사이트를 만들어 보시기 바랍니다. 설명한 바와 같이 이 마법사는 "Web.vssettings" IDE 설정 파일을 Visual Studio로 가져오는 경우에만 나타납니다. 이 작업은 도구-가져오기/내보내기 설정 대화 상자를 통해서도 수행할 수 있습니다. 이 마법사에는 지금까지 설명한 모든 기능이 포함되어 있으며, 이 마법사를 이용하면 수많은 UI 사용자 지정을 통해 새로운 웹 사이트에서 여러분이 원하는 모양과 기능을 얻을 수 있습니다.

프로그래밍 구성원 및 역할
  서버 쪽 보안 컨트롤로부터 많은 이점을 얻을 수 있지만 이러한 고차원 기능을 구현하는 데 사용되는 클래스를 직접 이용할 수 있다는 것도 알아두면 좋습니다. 구성원(Membership) 및 역할(Role) 서비스에 필요한 프로그래밍 모델을 배우기 위해 살펴볼 두 가지 주요 클래스가 있습니다. 지면 관계상 이 부분을 철저히 다룰 수 없고 또한 세부 사항은 제품이 최종 출시에 가까워지면서 변경될 것이므로 어떤 것인지에 대해서만 간단히 설명드리겠습니다.
  구성원(Membership) 클래스에서 사용자를 생성하고 관리할 수 있으며 각 사용자는 MembershipUser 클래스 인스턴스로 나타납니다. 이 클래스는 사용자 프로필을 나타내는데 여기에는 Email, CreationDate, PasswordQuestion 등과 같은 속성이 포함됩니다. 이 사용자 프로필을 생성하고 업데이트할 때 구성원(Membership) 클래스를 통해서 할 수 있는데 계층적 모델로 인해 어디서 어떻게 프로필이 저장되는지는 보여주지 않습니다 (그림 8 참조). 사용자 암호를 변경하고 그 암호를 임의의 컴퓨터가 생성한 암호로 재설정하는 방법이 이 클래스에서 제공되는데, 이것은 현재 사용자 수를 유지하기 위해 사용자 작업을 추적하는 타임스탬프로 제공됩니다. (이 숫자는 구성원 클래스에서 GetNumberOfUsersOnline 매서드를 호출하여 얻을 수 있습니다.)
  사용자의 암호를 확인하기 위해서는 구성원(Membership) 클래스에서 ValidateUser를 호출하여 사용자 이름과 암호로 보냅니다. 여기에서 기본 공급자가 필요한 모든 암호 해싱과 해독을 처리할 것입니다. 만약 사용자가 암호를 잊어버렸다면 전자 메일 주소를 요청하여 GetUserNameByEmail로 전달함으로써 암호를 알려 줄 수 있지만 안전한 옵션은 아닙니다.

쿠키없는 폼 인증
  제가 ASP.NET 폼 인증을 가르치면서 가장 많이 접하는 불만 사항은 쿠키가 필요하다는 점입니다. 다행히 ASP.NET 2.0에서 이런 제약은 사라졌습니다. 왜냐하면 web.config의 <forms/>요소에 "쿠키 없음(cookieless)"이라는 새로운 특성이 있기 때문입니다. 이것을 UseCookies, UseUri, UseDeviceProfile, AutoDetect의 네 가지 값 중 하나로 설정하면 됩니다.
  UseCookies와 UseUri는 FormsAuthenticationModule을 강제로 적용해서 모든 요청에 각각 쿠키 또는 URL 다시 쓰기(munging)를 사용하도록 합니다. UseDeviceProfile은 브라우저 기능을 통해 어떤 모드를 사용할지 결정하라고 지시합니다. 마지막으로, AutoDetect는 쿠키 설정을 시도하는데, 만약 실패할 경우 그 대신 URL 다시 쓰기(munging)를 사용합니다. 아래는 URL이 다시 쓰여진 후에 전형적으로 어떤 형태를 띠는지 예로 든 것입니다. (이러한 URL은 아주 길어질 수 있기 때문에 줄임표를 사용했습니다.) http://www.acme.com/foo/(F(Cvc...A1))/default.aspx.
  이 URL에서 괄호에 삽입된 부분이 일반적으로 쿠키가 담고 있는 정보를 포함하고 있고, HTTP 파이프라인에서 모듈에 의해 없어집니다. 그러므로 ASPX 페이지에서 Request.Path 속성을 읽는 경우에는 이 URL에 추가로 붙어 있는 것들을 볼 수가 없습니다. 그 요청을 리디렉션한다면 그 URL은 자동으로 다시 써질 것입니다. 다시 말해서 이 코드를 통해 URL이 다시 써진 채 현재 보고 있는 똑같은 페이지로 정확히 되돌아갈 수 있습니다.:
Response.Redirect(Request.Path)

   이 기능으로 폼 인증을 훨씬 폭넓게 구현할 수 있습니다. 그러나 웹 사이트가 ASP.NET 폼 인증을 많이 사용할수록 그만큼 공격자도 약점을 찾으려고 애쓸 것입니다. 그러므로 기본 원칙을 따르는 것이 좋습니다.

예방책이 최선
  폼 인증이 SSL(secure sockets layer)에 의해 보호 받지 못한다면 강력해지지 못합니다. 적어도 로그인 페이지가 사용자에게 보내지고 안전한 연결을 통해 웹 서버로 다시 전달되어야, 도청자가 사용자의 일반 텍스트 암호를 알아채지 못하도록 막을 수 있습니다. 그러나 일반적으로 그것만으로 충분하지 않습니다. 폼 인증 쿠키를 뺏으려는 도청자가 로그인을 훔치는 것은 쿠키가 작동하는 방식으로 인해 탐지 기능 재생을 구현할 방법이 없기 때문입니다. 페이지 단추에 필요한 GIF 파일에 대한 요청과 같은 아주 단순한 일이라도, 모든 요청과 함께 쿠키가 보내진다는 것을 기억해야 합니다. 일단 로그인을 훔치면 공격자는 그 쿠키를 이용해서 사용자를 위장할 수 있습니다. 이러한 위험을 줄이려면 쿠키 시간 제한을 현저히 줄이거나 웹 사이트의 전 섹션을 SSL에서 운영해야 합니다.
  필자는 개인적으로 더 높은 보안 수준을 필요로 하는 사이트에 대해 후자의 방법을 권하고 싶습니다. 만약 SSL이 느리다고 불평하는 사람이 있다면 필자는 속도를 높이기 위해 왜 하드웨어를 구입하지 않았는지 물어봅니다. 하지만 일부 웹 사이트에 대해서만 SSL을 사용하기를 고집하는 회사도 있을 것입니다. 만약 이러한 상황이라면 쿠키 재생 공격을 완화시키기 위해 <forms/> 요소에서 requireSSL 특성을 실행하십시오. 이렇게 하면 폼 인증 쿠키에 "보안" 특성이 추가되고, 브라우저는 오직 안전한 채널을 통해서만 서버로 쿠키를 다시 돌려 보냅니다. 즉, SSL을 거치지 않는 요청으로는 보내지지 않는 것입니다. 이 기능은 .NET Framework 1.1 버전에 추가되었기 때문에 ASP.NET 2.0의 고유 기능은 아닙니다. ASP.NET 2.0의 새 기능은 이 방안이 세션 쿠키에도 적용된다는 점입니다.:
<httpCookies requireSSL='true' httpOnlyCookies='true'/>

   안전한 쿠키는 SSL을 통해 들어오지 않는 요청으로는 보내지지 않기 때문에, 원시 HTTP를 통해 엑세스할 수 있는 페이지에서는 User.Identity.IsAuthenticated가 매번 false를 반환하게 됩니다. 다시 말해 SSL을 거쳐 실행되지 않는 페이지에서는 사용자가 누군지 알 수 없습니다. 또한 SSL을 통해 전체 사이트를 실행하기로 결정했다고 해도 우연히 원시 HTTP를 거쳐 파일에 엑세스가 허용되는 경우에 대비하여 requireSSL을 이용하는 것이 바람직합니다.
  httpOnlyCookies 특성은 교차 사이트 스크립팅 공격을 막기 위한 유용한 방법으로서, 쿠키가 스크립트에서 액세스할 수 없도록 브라우저에 지시합니다. 이것은 현재 Internet Explorer의 최신 버전에서만 인식할 수 있는 HttpOnly라고 하는 쿠키 특성을 이용하는 것입니다. 그러나 필자는 다른 브라우저 업체에서도 이것을 채택했으면 하는 바람이 있습니다.

결론
  ASP.NET 2.0은 폼 인증을 이용하는 웹 사이트에 보안상 중요한 이점을 제공합니다. 역할에 대한 지원과 함께 사용자 프로필 리포지토리를 제공함으로써, 폼 인증은 ASP.NET 내부 전문가들의 시야를 넘어 더욱 폭넓게 구현될 것입니다. 이제 옛날로 돌아가서 오래된 것을 사용하는 것은 어려울 것 같습니다.


Keith Brown 은 응용 프로그램 보안을 전문으로 하는 컨설턴트로서 DevelopMentor에서 강의를 하고 있으며 보안 커리큘럼을 맡고 있습니다. Keith는 Programming Windows Security(Addison-Wesley, 2000)의 저자이며, 현재 새로운 .NET 보안에 관한 책을 쓰고 있습니다. http://www.pluralsight.com/keith 를 방문하시면 온라인으로 읽으실 수 있습니다.

출처 MSDN Magazine . 2004년 6월호 .


화면 맨 위로화면 맨 위로


최종수정일: 2004년 6월 11일

+ Recent posts