본문 바로가기

IT 살이/03. 관리 - 보안 관리

[연재] 8. 보안 요청

8. 보안 요청

보호된 리소스를 직접 액세스하는 코드를 작성하거나 이러한 액세스가 호출자에게 노출되는 경우, 호출하는 코드(어셈블리)에 권한이 있는지를 확인하는 요청을 작성할 수 있다. 호출자에 의한 호출이 실제로 수행되기 전에 이런 요청에 대한 권한 체크를 먼저 통과해야 한다.  이런 권한 요청은 어트리뷰트를 사용한 선언적인 방법을 사용할 수도 있고 또는 메소드를 통해서 프로그램적 요청을 사용할 수 있다.
대부분의 .NET Framework 클래스에는 자신과 연결된 요청이 이미 있으므로 보호된 리소스에 액세스하는 클래스를 사용할 때마다 요청을 추가할 필요가 없다. 예를 들어, StreamWriter 클래스는 열릴 때마다 자동으로 FileIOPermission에 대한 보안 요청을 한다. StreamWriter 클래스를 사용할 때 FileIOPermission을 요청하면 중복된 비효율적인 스택 워크가 발생한다. 사용자 지정 리소스를 보호하기위해서 개발자는 사용자 지정 권한을 요구하는 요청을 사용해야 한다.
여기서는 자원을 보호하기 위해서 코드상에서 특정 권한을 요청할 수 있는 메소드들을 소개한다. 그러나 이 메소드들을 이해하기 위해서는 StackWalk( 호출스택) 개념이 필요하다

■ StackWalk(호출 스택)

현재 비관리형 코드에 접근하려는 메소드 AccessUnmanagedCode()가 있다고 하자. 어셈블리에 대한 권한 체크는 이 메소드가 있는 어셈블리에 대해서만 수행되는 것이 아니다. 만약 AccessUnmanagedCode()가 다른 어셈블리에 있는 코드에서 호출되고 있다면 그 어셈블리에 대한 권한도 체크를 한다. 이것은 StackWalk라는 개념의 기술을 통해서 가능하다.
StackWalk라는 것은 말 그대로 풀이해본다면 "현재까지의 걸어온 자취 즉 어셈블리의 호출을 누적해놓은 것"이다. 그리고 자취의 누적 방향은 위에서 아래 방향으로 쌓여 간다고 상상하면 된다. 그림을 보자.

StackWalk 개념(클릭을 하면 선명한 그림이 뜹니다.)

그림처럼 Demand()(뒤에서 설명한다)라는 메소드를 통해서 해당 권한이 assembly3.dll에 부여되었는지를 체크 하게 되면 현재 어셈블리에서부터 차례로 StackWalk를 올라가면서 주어진 권한을 어셈블리별로 체크하게 된다. StackWalk 상의 모든 어셈블리가 권한 체크를 통과해야만 Demand() 이후의 코드가 실행될 수 있는 것이다.

■ Demand() 메소드

어셈블리 권한 풀이기(permission  resolver)는 대부분의 시간은 그냥 잠든(?)상태에 있다. 이것이 작동을 하는 경우는 어셈블리가 로딩되는 경우 CLR에 의해서 잠에서 깨어난다. 또한 코드상에서 권한 풀이기를 직접 깨울 수도 있다. 어떤 어셈블리가 보호를 받는 자원에 접근하려고 할 때 자신(또는 자신을 호출한 어셈블리)이 적절한 권한을 부여 받았는지를 확인해 볼 수 있다. 이런 권한 체크에, IPermission에 정의되어 있고 CodeAccessPermission에서 구현하고 있는 Demand() 메소드를 사용할 수 있다. 다음 예제는 SecurityPermission을 사용해서 체크할 권한을 지정하고 있는데, 이 클래스는 CodeAccessPermission 클래스를 상속하고 있다.

using System.Security;
using System.Security.Permissions;


void AccessUnmanagedCode()
{
  //비관리형 코드에 접근하는데 필요한 권한을 체크하기 위해서
  //필요한 권한 객체를 생성한다.
  SecurityPermissionFlag flag = SecurityPermissionFlag.UnmanagedCode;
  SecurityPermission perm = new SecurityPermission(flag);
 
  try
  {
  perm.Demand();
  }
  catch( SecurityException)
  {
  return;
  }
 
  //비관리형 코드에 접근
  …
}

AccessUnmanagedCode() 메소드는 비관리형 코드로의 접근을 시뮬레이션하고 있다. 권한 체크를 하려면 우선 적절한 권한(permission)객체를 생성해야 한다. 이 코드는 비관리형 코드(UnmanagedCode)에 대해 접근할 수 있는 권한이 있는지를 체크할 수 있는 권한 객체 SecurityPermission를 생성하고 있다. SecurityPermission 생성자가 받을 수 있는 SecurityPermissionFlg 열거형에는 다른 값들도 정의하고 있다.

System.Security.Permissions
{
  public enum SecurityPermissionFlag
  {
  AllFlags, Assertion, BindingRedirects, ControlAppDomain,
  ControlDomainPolicy, ControlEvidence, ControlPolicy,
  ControlPrincipal, ControlThread, Execution, Infrastructure,
  NoFlags, RemotingConfiguration, SerializationFormatter,
  SkipVerification, UnmanagedCode
  }
}

이 열거형들이 생성한 권한 객체는 어떤 내용의 권한을 체크할 수 있는지에 대한 내용은 MSDN을 참고하라.

권한 체크에 필요한 SecurityPermission객체가 생성되면 이제 그것의 Demand() 메소드를 호출하면 된다. 만약 현재 어셈블리가 해당 권한(비관리형 코드 호출 권한)을 가지고 있지 않다면 SecurityException  예외를 발생시킨다.

Demand() 메소드에 대해서 착각하지 말 것은, Demand()를 호출한다는 것은 "특정 권한을 요구하는 것이 아니다"것이 아니라 현재 어셈블리에 "특정 권한이 있는지 체크하는"것이다. 어셈블리의 코드가 실행될때쯤이면 실행되고 있는 컴퓨터의 보안 정책에 따랏 이미 사용 가능한 권한은 확정된다. 그리고 그 권한의 범위내에서만 실행될 수 있는 것이다.

Demand()를 사용하면 실행되고 있는 현재 컴퓨터의 보안 정책에 따라 몇가지로 행동하는 코드를 작성할 수 있다는 것이다. Demand() 메소드는 StackWalk를 통해서 호출하고 있는 모든 어셈블리들의 권한도 함께 체크한다.

■ Assert() 메소드

그러나 현재의 어셈블리가 충분한 권한을 가지고 있다면 StackWalk상에서 이전 어셈블리의 권한과는 상관없이 보호된 자원에 접근하려는 메소드가 실행될 수 있는 방법이 있다. 현재 어셈블리가 Assert() 메소드를 사용하여 권한을 체크하면 된다.

Assert() 권한 체크

Assert() 메소드는 StackWalk상에서 이전의 어셈블리에 대해서는 해당 권한을 부여하는 능력을 가지고 있다. 사실 권한 부여라기보다는 권한 체크 자체를 멈춘다.
그러나 현재 어셈블리가 해당 권한을 가졌는지를 먼저 체크한다. 즉 현재 어셈블리가 비관리형 코드를 호출할 수 있는 권한이 있는지를 체크한다. 자신도 가지고 있지 않은 권한에 대해 이전 어셈블리에 대해서 해당 권한에 대한 체크를 하지 말아달라고 요청하는 것은 받아 들여지지 않는다. 또한 현재 어셈블리가 Assert() 메소드를 호출할 만한 권한이 있는지도 체크한다. 비관리형 코드를 호출할 수 있는 권한이 있다는 것과, StackWalk상의 이전 어셈블리에 대해서도 그 권한 체크를 멈춰달라고 요청할 수 있는 권한은 다른 것이다. 다음은 이전에 Demand() 메소드에서 봤던 예제 코드에서 메소드만 Assert()로 변경했다.

using System.Security;
using System.Security.Permissions;


void AccessUnmanagedCode()
{
  //비관리형 코드에 접근하는데 필요한 권한을 체크하기 위해서
  //필요한 권한 객체를 생성한다.
  SecurityPermissionFlag flag = SecurityPermissionFlag.UnmanagedCode;
  SecurityPermission perm = new SecurityPermission(flag);
 
  try
  {
  perm.Assert();
  }
  catch( SecurityException)
  {
  return;
  }
 
  //비관리형 코드에 접근
  …
}

현재 어셈블리에 비관리형 코드를 호출할 수 있는 권한과 Assert() 메소드를 호출할 수 있는 권한이 있다면 예를 들어 FullTrust 권한 집합을 부여받은 어셈블리라면 위 Assert() 메소드는 효력을 발휘해서 StackWalk상의 이전 어셈블리에도 비관리형 코드에 접근할 수 있는 권한이 부여되는 효과가 발휘될 수 있다.

■ 기타 자원을 보호하기 위한 권한 요청 메소드들은 여러가지 있다. CodeAccessPermition 클래스를 참고하라.

참조 문서

ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.ko/
dv_fxsecurity/html/1d072877-ad4c-4d2c-8544-4b2502cb1f37.htm(msdn 한글)

http://msdn.microsoft.com/library/default.asp?url=/library/
en-us/cpguide/html/cpconCodeAccessSecurity.asp(영문
)