Silverlight를 설치하려면 여기를 클릭합니다.*
Korea 대한민국변경|Microsoft 전체 사이트
MSDN
|개발자 센터
MSDN Home   MSDN Home

Security Briefs
CardSpace, SqlMembershipProvider 및 추가 정보


이 문서에 사용된 코드 다운로드: SecurityBriefs2006_10.exe (151KB)

Keith Brown (영문)

이번 달 기사에서는 독자들에게서 자주 받는 몇 가지 질문에 대해 답하는 데 시간을 할애하겠습니다. 여기에서는 정보 카드, ASP.NET 2.0의 SqlMembershipProvider 및 액세스 제어 GUI에 대해 살펴보겠습니다.


Q 자체 발행 정보 카드에서 보안 토큰을 위한 장기간용 키를 찾으려면 어떻게 해야 합니까?


A InfoCard(Windows® CardSpace로 이름이 변경됨)에 대해 살펴본 최근 기사 (영문)에서 사용자 이름과 암호를 대신하는 자체 발행 정보 카드의 사용 방법에 대해 설명했습니다. 그 기사를 마무리할 무렵 2월 CTP(Community Technology Preview)가 배달되었습니다. 아쉽게도 이 릴리스는 CardSpace 관점에서는 좋지 않았습니다. 대부분의 CardSpace 샘플이 해당 릴리스에서 동작하지 않았기 때문입니다. 5월 말 Microsoft는 CardSpace를 매끄럽게 지원하는 새 버전을 릴리스했습니다.

사용자의 장기간용 키를 찾는 방법을 보여 주는 새롭게 개선된 Notepad 웹 서비스 예제를 준비했습니다. 이 예제를 보여 주기 전에 장기간용 키 검색이 중요한 이유부터 설명하겠습니다. 자체 발행 보안 토큰을 허용하는 서비스를 작성한다고 가정해 보겠습니다. Alice라는 사용자가 서비스에 연결하면 클라이언트 쪽 CardSpace 작업에서는 요청된 클래임으로 보안 토큰을 만듭니다. 이러한 클래임은 전자 메일 주소, 전화 번호 또는 자체 발행 카드에 저장 가능한 기타 개인 정보가 될 수 있습니다. 물론 사용자가 익명으로 남을 수 있도록 하는 식별자가 될 수도 있습니다. 이 식별자가 바로 이전 기사에서 설명한 PPID(Personal Private Identifier)입니다. 전자 메일 주소가 일반적이지만 사용자 익명성이 중요한 경우에는 PPID가 유용합니다.

전자 메일 주소를 사용하여 사용자를 식별한다고 가정해 보겠습니다. Alice가 독자의 사이트에 처음 방문하고 자체 발행 보안 토큰을 전송하면 독자는 Alice의 전자 메일 주소를 새 계정으로 기록합니다. Alice가 두 번째로 방문하면 독자는 보안 토큰으로 받은 전자 메일 주소를 사용하여 Alice의 사용자 계정 데이터를 조회할 수 있습니다. 그러나 해당 인물이 Alice를 가장한 다른 사용자가 아니라 실제로 Alice임은 어떻게 알 수 있을까요? 더 이상 암호가 없는 상황에서 어떤 보안 정보로 사용자를 인증해야 할까요?

사용자가 전송한 보안 토큰은 사용자의 자체 발행 보안 토큰 서비스만 알고 있는 개인 키로 서명된다는 점을 기억하십시오. 이에 대응하는 공개 키는 서명에 포함되어 Windows Communication Foundation(또는 신뢰 당사자가 사용하는 다른 유형의 작업)이 해당 서명을 검증합니다. 이는 클라이언트가 자신의 개인 키를 실제로 알고 있다는 암호 보장입니다. 그러나 Windows Communication Foundation은 지정된 사용자에 대한 정확한 공개 키를 알지 못합니다. 공격자는 alice@fabrikam.com을 가장하려면 새로운 공개/개인 키 쌍을 만들고 개인 키를 사용하여 토큰에 서명한 다음 해당하는 공개 키를 서명으로 전송하기만 하면 됩니다. 이러한 작은 세부 사항은 소홀히 넘기기 쉽지만, 제출된 공개 키에 대해 서명을 검증하는 힘든 작업은 Windows Communication Foundation이 수행한다 해도 제출한 공개 키가 사용자가 초기에 사용한 공개 키와 같은지 확인하는 작업은 여전히 개발자의 책임입니다.

그렇다면 이 공개 키는 어떻게 찾을 수 있을까요? Windows Communication Foundation의 AuthorizationContext를 사용하면 간단합니다. 사용자가 전송한 클래임을 열거하면 요청한 전자 메일 주소와 함께 보낸 사람의 RSA 공개 키를 알 수 있습니다.

개인 정보 보호론자들이 필자를 비난하기 전에 이것이 실제 어떤 의미가 있는지 명확히 밝히고 넘어가겠습니다. 모든 상호 작용에 공개 키가 한 개만 사용된다면 신뢰 당사자의 익명성을 유지하는 것은 불가능합니다. 신뢰 당사자가 악의를 갖고 있다면 개발자의 공개 키를 식별자로 사용하고 개발자가 각 당사자에게 제공한 모든 클래임을 손쉽게 연관시켜 개발자에 대한 공유 정보 문서를 만들 수 있습니다. 이러한 시나리오는 반드시 피해야 합니다!

실제로 어떤 일이 진행되는지 이해하려면 PPID가 작성되는 방식을 상기하십시오. 근본적으로 PPID는 특정 정보 카드와 특정 신뢰 당사자 간의 관계에서 가져온 다양한 정보로 이뤄진 해시입니다. 동일한 정보 카드를 10명의 신뢰 당사자에게 사용할 수 있으며, 이 경우 각 신뢰 당사자는 서로 다른 PPID를 보게 됩니다. 각자 제공한 정보(예: 공개 키)가 다르기 때문입니다. 자체 발행 보안 토큰 서비스도 이와 비슷한 방법으로 RSA 키 쌍을 생성합니다. 동일한 정보 카드를 사용하더라도 10명의 신뢰 당사자는 각기 다른 공개 키를 보게 됩니다. 이를 통해 사용자는 자신의 ID를 제어할 수 있습니다.

그림 1은 이 공개 키를 검색하는 데 사용할 수 있는 코드를 보여 줍니다. 자체 발행 정보 카드 클래임의 URI가 필자의 마지막 기사 이후 조금 달라진 것을 알 수 있습니다.

RSA 공개 키는 단순한 바이트 문자열이 아니며 계수와 지수의 두 부분으로 구성됩니다. AuthorizationContext에서 공개 키를 읽으면 실제로는 System.Security.Cryptography 네임스페이스에 있는 AsymmetricAlgorithm을 확장하는 RSA 추상 클래스를 확장하는 개체를 받게 됩니다. 공개 키 매개 변수를 추출하려면 다운캐스트를 수행하고 RSA.ExportParameters를 호출하여 계수 및 지수에 대한 액세스를 얻어야 합니다. 이제 이 정보로 해시를 수행하여 데이터베이스에 저장할 수 있는 키를 위한 고유 식별자를 만들 수 있습니다. 그림 2의 메서드가 이러한 작업을 수행합니다. 이진 데이터를 저장해도 관계없다면 Base64 인코딩 단계를 생략할 수 있습니다. 이렇게 하면 키를 위한 32바이트 식별자가 생성됩니다.

이전 기사에서 설명한 Notepad 샘플은 PPID를 사용하여 사용자를 식별하면서도 익명성을 유지할 수 있도록 하였습니다. 이 샘플은 각 사용자의 상태를 작은 조각(메모)으로 저장하며, 사용자는 GetNote 또는 SetNote를 호출하고 자신의 신원을 밝히는 자체 발행 보안 토큰을 제출하여 이 정보에 액세스할 수 있습니다. 업데이트된 샘플의 차이점은 PPID로만 메모를 인덱싱하지는 않는다는 것입니다. 이 샘플은 PPID와 공개 키의 해시를 조합하여 사용자 식별자를 형성합니다. 이렇게 하면 공격자가 Alice의 PPID를 자신의 공개 키와 함께 제출하더라도 Alice의 메모를 볼 수 없게 됩니다. 그림 3은 GetNote 및 SetNote의 새 코드를 보여 주는데, PPID와 공개 키 해시를 조합하여 각 사용자의 상태를 저장하는 사전에 인덱싱하는 방법을 확인할 수 있습니다.

일반적으로 사용자의 공개 키를 신뢰할 수 있는 사용자 추적 메커니즘의 일부로 저장하는 것을 고려해야 합니다. 예를 들어 사용자 데이터베이스의 열은 이러한 목적에 잘 부합합니다. 자체 발행 토큰의 클래임을 열거할 때 RSA 클래임도 확보하여 사용자가 이전에 제출했던 키와 동일한지 검증합니다. MSDN®Magazine 웹 사이트에서 Windows CardSpace 베타 2에서도 잘 작동하는 최신 샘플 코드를 다운로드하십시오.


Q ASP.NET 2.0에서 SqlMembershipProvider를 사용할 때 어떤 암호 형식을 사용해야 합니까?


A SqlMembershipProvider에서는 Clear, Encrypted 및 Hashed의 세 가지 암호 형식을 제공합니다. 이러한 값은 MembershipPasswordFormat 열거형에 있으며 구성을 통해 사용할 형식을 선택할 수 있습니다. 사용자가 암호를 입력해야 로그인할 수 있도록 해놓았다면 시스템에 보호해야 할 자산이 있다는 의미일 것입니다. 그렇지 않다면 사용자에게 굳이 불편한 로그인을 요구할 필요가 없습니다.

사용자에게 암호 입력을 요구하고 있다면 그만큼의 성의를 보여야 합니다. 암호를 일반 텍스트로 노출함으로써 멤버 테이블에 대한 SQL SELECT 권한을 가진 사용자가 아무 때나 이 정보를 볼 수 있도록 한다면 이것은 분명한 태만입니다. 따라서 프로덕션 시나리오에서 Clear 형식은 배제해야 합니다.

그렇다면 Encrypted는 어떨까요? 한 가지 고려해야 할 점은 사용자 암호를 암호화한다면 사용자가 암호를 분실한 경우 언제든 암호를 해독하여 알려줄 수 있다는 것입니다. 편하게 전자 메일을 사용하여 암호를 전달하고 싶은 생각이 들겠지만 대개 전자 메일 메시지는 일반 텍스트로 전송되기 때문에 암호와 같은 보안 정보를 보내기에 안전한 방법은 아닙니다. 암호가 단순히 웹 사이트 사용을 위해 형식적으로 거치는 절차가 아닌 바에야 SSL로 보호되는 웹 페이지를 사용해야 합니다. 그러나 곧 알게되겠지만 암호화된 암호의 가장 큰 문제는 이를 사용하기 위해서는 비밀이 필요하다는 점입니다.

SqlMembershipProvider가 암호를 암호화하는 데 사용하는 비밀 키는 구성 파일의 <machineKey> 요소에 지정됩니다. 이 구성 요소를 지정하지 않으면 AutoGenerate, IsolateApps에 해당하는 값으로 설정됩니다. AutoGenerate 옵션을 사용하면 ASP.NET은 시스템에 저장된 임의로 생성된 마스터 키를 사용합니다. 한편 IsolateApps 옵션은 응용 프로그램 가상 디렉터리의 해시를 바탕으로 마스터 키를 수정하여 각 응용 프로그램에 대한 개별 키를 파생하도록 ASP.NET에 지시합니다. 그러나 이러한 데이터는 백업하기가 어렵기 때문에 암호를 암호화하는 데 썩 적합한 방법은 아닙니다. 한 시스템에서 개발하고 다른 시스템에서 테스트한다면 각 시스템에 새 테스트 사용자 계정을 만들어야 합니다. 또한 프로덕션 시스템에서 암호화 키를 분실한다면 암호를 암호화하거나 해독하는 기능도 잃게 되므로 더 이상 기존 사용자를 인증할 수 없게 됩니다. 따라서 대부분의 개발자는 암호를 암호화할 계획이라면 자동 생성 키를 사용하지 말라고 경고하기 위해 ASP.NET 팀에서 삽입한 예외를 가장 먼저 경험하게 됩니다.

이 문제를 해결하려면 강력한 임의 키를 생성하고 구성 파일에 <machineKey> 요소를 추가해야 합니다. 요소 이름 때문에 오해하지 않도록 하십시오. 응용 프로그램이 자체 개별 키를 갖도록 하려면 web.config 파일에 이 요소를 넣을 수 있습니다. 키 생성을 위한 도구가 필요하다면 www.pluralsight.com/tools.aspx (영문)에서 ASP.NET 관리 콘솔을 다운로드하십시오. 이 도구를 사용하면 ASP.NET 2.0이 지원하는 가장 강력한 <machineKey>를 생성할 수 있습니다. 혹시 필자에게 전자 메일로 키 복사본을 보내는 기능이 숨겨져 있지 않을까 걱정된다면 소스 코드와 함께 제공되므로 직접 확인할 수 있습니다. 물론 농담이긴 합니다만, 이는 이러한 종류의 도구를 사용할 때 신중하게 고려해야 하는 사항입니다. 또한 가상 디렉터리 이름은 시간이 지나면 변경될 수 있으므로 키에 가상 디렉터리 이름이 사용되지 않도록 IsolateApps 기능도 피해야 합니다.

다른 sysadmin이 일상적인 탐색을 통해 키를 볼 수 있는 상황을 원하지는 않을 것입니다. 키의 백업 복사본을 만든 다음에는 aspnet_regiis를 사용하여 <machineKey> 섹션을 암호화할 수 있습니다. 이 작업의 작동 원리를 확인하려면 msdn.microsoft.com/library/en-us/dnpag2/html/PAGHT000005.asp (영문)에 있는 patterns & practices 기사 "How To: Encrypt Configuration Sections in ASP.NET 2.0 using DPAPI"(영문)를 참조하십시오. 상당히 쉬운 내용입니다.

이제부터는 정말 좋지 않은 소식입니다. <machineKey>를 사용하여 모든 사용자 계정을 암호화하므로 해당 키를 변경하면 사용자 데이터베이스의 키를 다시 대조해야 하는데, 현재 공급자 수준에서는 이를 쉽게 수행할 수 있는 방법이 없습니다. 따라서 일단 암호화를 수행한 다음에는 <machineKey>를 변경하기가 매우 어렵습니다. 키를 변경하기 어렵다는 것은 불리한 조건입니다. 시스템 관리자가 퇴사해 경쟁업체에 취업한다면 어떻겠습니까? 상당히 난감한 일이 될 수 있습니다. 또한 로그인 쿠키 보호, 상태 보호 보기와 같은 많은 다른 보안 기능도 동일한 키에 의존하기 때문에 쉽게 변경할 수 없습니다. <machineKey>를 변경하려면 멤버 사용자 데이터베이스의 키를 다시 대조하는 코드를 작성해야 하는데, 이 작업을 쉽게 수행하도록 해주는 기본 제공 메커니즘은 없습니다.

따라서 필자는 Hashed 암호 형식을 사용하기를 권장합니다. SqlMembershipProvider로 사용자 계정을 추가하면 암호를 기반으로 솔트 해시가 생성되며 해당 솔트와 해시가 암호 검증 도구로 사용자 계정에 저장됩니다. 이 기술에 대한 내용은 msdn.microsoft.com/msdnmag/issues/03/08/SecurityBriefs (영문)에 있는 2003년 8월 Security Briefs 기사에서 설명한 적이 있습니다. 사용자가 로그인을 시도하면 공급자는 사용자가 제시한 사용자 이름을 바탕으로 계정을 조회하고 레코드에서 솔트를 읽은 다음 이를 사용하여 사용자가 제공한 암호의 해시를 계산합니다. 해시가 저장된 검증 도구와 일치하면 사용자가 인증되고 로그인할 수 있게 됩니다.

Hashed 암호를 선택하면 SqlMembershipProvider의 일부 기능을 사용할 수 없습니다. 예를 들어 사용자의 암호를 검색하려고 하면 해시를 되돌리는 직접적인 방법이 없기 때문에 예외로 처리됩니다. 사용자가 암호를 분실한 경우에는 ResetPassword 메서드를 사용하여 멤버 시스템에서 새 임의 암호를 생성할 수 있으며 사용자에게 이 암호를 전달해야 합니다. 그러면 사용자는 ChangePassword 컨트롤을 사용하여 암호를 변경할 수 있습니다. 물론 이제는 사용자를 인증할 수 있는 2차적인 방법에 대해 걱정해야 합니다. 그렇지 않으면 악성 사용자가 정식 사용자의 암호를 다시 설정하고 계정을 탈취할 수 있게 됩니다. 멤버 기능에 질문과 답변 시스템을 구축하면 이러한 문제를 해결할 수 있습니다.

한편, 해시를 직접적으로 되돌릴 수 없다고 해서 암호를 검색하는 방법이 없는 것은 아닙니다. 공격자가 멤버 테이블의 사용자 레코드를 사용할 수 있게 되면 오프라인 사전을 탑재하거나 무차별 공격을 시도하여 암호를 추측하고 일치하는 항목을 찾을 때까지 해시를 계산할 수 있습니다. 약한 암호는 이러한 공격에 쉽게 무너질 수 있습니다. 이러한 종류의 공격을 늦추는 최선의 방법은 강력한 암호 정책을 사용하는 것입니다. 이러한 공격이 문제가 되는 경우에는 구성 파일에 있는 멤버 공급자에 대한 특성을 사용하여 강력한 암호를 요구하십시오. 기본적으로 SqlMembershipProvider는 암호에 최소 7자를 사용할 것과, 이 중 1자는 영숫자가 아닌 기호일 것을 요구하고 있습니다. 필요에 따라 이를 더욱 강화하여 신규 사용자를 등록할 때 충족시켜야 하는 정규식을 지정할 수 있습니다. 공급자의 ValidatingPassword 이벤트를 연결하여 새 암호를 검증하는 코드를 작성할 수도 있습니다. 암호 검증 코드를 직접 작성하는 경우에는 암호 내에 사전 단어가 있는지 확인하는 등의 작업을 수행할 수 있습니다. 그림 4는 암호의 길이가 8자일 것과, 여기에 최소 2자의 영숫자가 아닌 기호를 사용할 것을 요구하는 Hashed 암호 형식을 사용하는 구성의 예를 보여 줍니다.


Q 파일의 액세스 제어 목록을 편집하기 위해 AclUIAdapter를 사용해야 합니까?


A 사용자가 파일에 대한 ACL(액세스 제어 목록)을 편집할 수 있게 해주는 Windows 탐색기의 기능과 비슷한 대화 상자를 관리되는 코드로 작성하는 방법에 대한 질문을 한 독자에게서 받았습니다. 필자는 우선 2005년 3월 기사에서 설명했던 AclUIAdapter를 사용할 것을 권했습니다.

그러나 이보다 훨씬 더 쉬운 방법이 있습니다. 힘든 작업은 Windows 탐색기에 맡기는 것입니다! Win32에는 파일 및 프린터에 대한 Windows 탐색기 속성 시트를 표시하는 SHObjectProperties라는 함수가 있습니다. Win32® 정의는 다음과 같습니다.

BOOL SHObjectProperties(
    HWND hwndOwner, DWORD dwType,
    LPCWSTR szObject, LPCWSTR szPage
);

첫 번째 인수는 속성 시트의 소유자 창 핸들이며 알맞은 창이 없는 경우에는 NULL이 될 수 있습니다. 두 번째 인수는 Windows 탐색기가 속성 시트를 표시할 개체의 유형을 나타냅니다. 이러한 형식은 shlobj.h에 정의되어 있습니다.

#define SHOP_PRINTERNAME 0x00000001
#define SHOP_FILEPATH    0x00000002
#define SHOP_VOLUMEGUID  0x00000004

이를 활용하면 약간의 P/Invoke 스텁을 사용해서 이 함수를 호출하여 손쉽게 속성 시트를 표시할 수 있습니다. 그림 5는 FSP.EXE(File System Properties의 약자)의 코드를 보여 줍니다. 그림 6은 이를 명령줄에서 실행할 경우 표시되는 속성 시트를 보여 줍니다.

fsp security c:\autoexec.bat

이 프로그램은 간단한 수정을 통해 프린터 또는 볼륨 속성 페이지를 표시할 수 있습니다.

그림 6 표시된 속성
그림 6 표시된 속성

Keith에게 질문이나 의견이 있으면 메일을 보내시기 바랍니다.  briefs@microsoft.com.

Keith BrownKeith Brown은 프리미어 Microsoft .NET 교육 제공업체인 Pluralsight의 공동 창립자입니다. Keith는 Pluralsight에서 제공하는 .NET 보안 관련 과정뿐 아니라 The .NET Developer's Guide to Windows Security와 같은 몇 권의 책을 저술하기도 했습니다. 이 책은 출판물로 볼 수도 있고 웹에서도 볼 수 있습니다. www.pluralsight.com/keith (영문)에서 자세한 내용을 알아보십시오.


페이지 맨 위로페이지 맨 위로QJ: 061013

Microsoft