Assert 메서드 사용
Assert는 코드 액세스 권한 클래스와 PermissionSet 클래스에서 호출할 수 있는 메서드입니다. Assert를 사용하면 코드에는 수행할 권한이 있지만 호출자에게 권한이 없을 수도 있는 작업을 코드(및 다운스트림 호출자)에서 수행하도록 할 수 있습니다. 보안 어설션은 보안 검사 중 런타임에서 수행하는 일반 프로세스를 변경합니다. 권한을 어설션하면 보안 시스템은 코드 호출자에게 어설션된 권한이 있는지 검사하지 않습니다.
주의 |
---|
어설션은 보안상 허점을 드러낼 수 있으며 보안 제한 사항을 강제로 적용하는 공용 언어 런타임 메커니즘을 약화시킬 수 있으므로 신중하게 사용하십시오. |
어설션은 라이브러리가 비관리 코드를 호출하거나 라이브러리의 의도된 용도와 명확하게 관련되지 않은 권한을 요구하는 호출을 할 때 유용합니다. 예를 들어, 비관리 코드를 호출하는 모든 관리 코드에는 UnmanagedCode 플래그가 지정된 SecurityPermission이 있어야 합니다. 로컬 인트라넷에서 다운로드된 코드와 같이 로컬 컴퓨터에서 발생한 코드에는 기본적으로 이 권한이 부여되지 않습니다. 따라서 로컬 인트라넷에서 다운로드된 코드는 라이브러리에 의해 어설션된 권한이 있어야만 비관리 코드를 사용하는 라이브러리를 호출할 수 있습니다. 또한 일부 라이브러리는 호출자가 알지 못하는 호출을 할 수 있으므로 특수한 권한이 필요할 수도 있습니다.
호출자가 전혀 알지 못하는 방식으로 코드가 리소스에 액세스하는 경우에도 어설션을 사용할 수 있습니다. 예를 들어, 라이브러리가 데이터베이스에서 정보를 얻지만 처리 과정에서 컴퓨터 레지스트리의 정보도 읽는 경우, 특정 개발자가 작성한 라이브러리를 사용하는 다른 개발자는 해당 소스에 액세스할 권한이 없으므로, 원래 개발자의 코드를 사용하기 위해 자신들의 코드에 RegistryPermission이 필요한지 알 수 있는 방법이 없습니다. 이런 경우, 코드 호출자가 레지스트리에 액세스할 권한을 갖는 것이 비합리적이거나 불필요하다고 판단되면 레지스트리를 읽을 권한을 어설션할 수 있습니다. 이런 상황에서 라이브러리가 권한을 어설션하는 것이 좋으므로 RegistryPermission이 없는 호출자는 라이브러리를 사용할 수 있습니다.
어설션된 권한과 다운스트림 호출자가 요청한 권한이 동일한 형식이고 요청된 권한이 어설션된 권한의 하위 집합인 경우에만 어설션은 스택 워크에 영향을 줍니다. 예를 들어, C 드라이브에 있는 모든 파일을 읽는 FileIOPermission을 어설션하고 C:\Temp에 있는 파일을 읽는 FileIOPermission에 대한 다운스트림 요청을 하면, 어설션은 스택 워크에 영향을 줄 수 있습니다. 그러나, 이 요청이 C 드라이브에 쓰는 FileIOPermission이었다면 어설션은 아무 효력도 없습니다.
어설션을 수행하려면 어설션하고 있는 권한과 어설션할 수 있는 권한을 나타내는 SecurityPermission이 모두 해당 코드에 부여되어야 합니다. 코드에 부여되지 않은 권한을 어설션할 수는 있지만 어설션으로 인해 보안 검사에서 성공하기 전에 보안 검사에서 실패할 수 있으므로 어설션이 의미가 없을 수 있습니다.
다음 그림은 Assert를 사용할 때 일어나는 상황을 보여 줍니다. 어셈블리 A, B, C, E, F와 두 개의 권한 P1, P1A에 대해 다음을 가정합니다.
P1A는 C 드라이브에 있는 .txt 파일을 읽을 수 있는 권한을 나타냅니다.
P1은 C 드라이브에 있는 모든 파일을 읽을 수 있는 권한을 나타냅니다.
P1A 및 P1은 FileIOPermission 형식이며 P1A는 P1의 하위 집합입니다.
어셈블리 E 및 F에는 P1A 권한이 부여되어 있습니다.
어셈블리 C에는 P1 권한이 부여되어 있습니다.
어셈블리 A와 B에는 P1 또는 P1A 권한이 부여되어 있지 않습니다.
메서드 A는 어셈블리 A에, 메서드 B는 어셈블리 B에 포함되는 식으로 각 메서드는 해당 어셈블리에 포함됩니다.
Assert 사용
이 시나리오에서 메서드 A는 B를, B는 C를, C는 E를, E는 F를 호출합니다. 메서드 C는 C 드라이브에 있는 파일을 읽는 권한(권한 P1)을 어설션하며, 메서드 E는 C 드라이브에 있는 .txt 파일을 읽을 권한(권한 P1A)을 요청합니다. 런타임에 F의 요청에 이르면 스택 워크가 수행되어 E부터 시작해서 F의 모든 호출자의 권한을 검사합니다. E에는 P1A 권한이 부여되었으므로 스택 워크는 C의 권한을 검사하게 되고 여기서 C의 어설션이 발견됩니다. 요청된 권한 P1A가 어설션된 권한 P1의 하위 집합이므로 스택 워크는 중단되며 자동으로 보안 검사에서 성공합니다. 어셈블리 A와 B에 권한 P1A가 부여되지 않았어도 상관 없습니다. 호출자에게 해당 리소스에 액세스할 수 있는 권한이 부여되지 않은 경우라도, 메서드 C는 P1을 어설션하여 호출자가 P1으로 보호된 리소스에 액세스할 수 있도록 합니다.
클래스 라이브러리를 설계하고 클래스가 보호된 리소스에 액세스하는 경우 대부분, 클래스의 호출자에게 적절한 권한을 요구하는 보안 요청을 해야 합니다. 그런 다음 클래스의 호출자 대부분에게 권한이 없는 작업을 클래스가 수행하거나 이러한 호출자가 코드를 호출할 수 있도록 하려면, 코드가 수행하는 작업을 나타내는 권한 개체에서 Assert 메서드를 호출하여 권한을 어설션할 수 있습니다. 이런 방식으로 Assert를 사용하면 정상적으로는 코드를 호출할 수 없는 호출자가 코드를 호출할 수 있습니다. 따라서, 권한을 어설션하려면 구성 요소가 잘못 사용되지 않도록 미리 적절한 보안 검사를 수행해야 합니다.
예를 들어, 신뢰 수준이 높은 라이브러리 클래스에 파일 삭제 메서드가 있다고 가정합니다. 이 메서드는 관리되지 않는 Win32 함수를 호출하여 파일에 액세스합니다. 호출자가 코드의 Delete 메서드를 호출하면서 C:\Test.txt.와 같이 삭제할 파일 이름을 전달합니다. Delete 메서드 내에서 코드는 C:\Test.txt에 대한 쓰기 권한을 나타내는 FileIOPermission 개체를 만듭니다. 쓰기 권한이 있어야 파일을 삭제할 수 있습니다. 그런 다음 코드는 FileIOPermission 개체의 Demand 메서드를 호출하여 명령적 보안 검사를 호출합니다. 스택 워크의 호출자 중에서 이 권한을 가진 호출자가 없으면 SecurityException이 throw됩니다. 예외가 throw되지 않으면 모든 호출자에게 C:\Test.txt에 액세스할 수 있는 권한이 있는 것입니다. 호출자의 대부분은 비관리 코드에 액세스할 수 있는 권한을 갖지 않는 것으로 간주되므로 코드는 비관리 코드를 호출하는 권한을 나타내는 SecurityPermission 개체를 만들고 개체의 Assert 메서드를 호출합니다. 마지막으로 코드는 관리되지 않는 Win32 함수를 호출하여 C:\Text.txt를 삭제하고 호출자에게 제어를 돌려줍니다.
주의 |
---|
현재 어설션하고 있는 권한으로 보호된 리소스에 액세스하기 위해 다른 코드에서 사용자의 코드를 사용할 수 있는 경우에는 사용자 코드에서 어설션을 사용하지 마십시오.예를 들어, 호출자가 매개 변수로 지정한 이름의 파일에 쓰는 코드에서는, 사용자의 코드가 다른 개발자에 의해 열려서 잘못 사용될 수 있으므로 파일에 쓰기 위한 FileIOPermission을 어설션하지 않는 것이 좋습니다. |
명령적 보안 구문을 하는 경우 동일한 메서드에서 여러 권한에 대해 Assert 메서드를 호출하면 보안 예외가 throw됩니다. 따라서 PermissionSet 개체를 만들어서 호출할 개별 권한에 전달해야 합니다. 그런 다음 PermissionSet 개체에서 Assert 메서드를 호출해야 합니다. 선언적 보안 구문을 사용할 때는 Assert 메서드를 두 번 이상 호출할 수 있습니다.
다음 예제에서는 Assert 메서드를 사용하여 보안 검사를 재정의하는 선언적 구문을 보여 줍니다. FileIOPermissionAttribute 구문에서는 SecurityAction 열거형 및 권한이 부여될 파일이나 디렉터리 위치에 대한 두 개의 값을 사용합니다. Assert를 호출하면 호출자에 대해 C:\Log.txt 파일을 액세스할 수 있는 권한이 있는지 검사하지 않더라도 이 파일에 대한 액세스 요청이 성공합니다.
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub
<FileIOPermission(SecurityAction.Assert, All := "C:\Log.txt")> Public Sub
MakeLog()
Dim TextStream As New StreamWriter("C:\Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now) '
TextStream.Close()
End Sub
End Class
End Namespace
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
[FileIOPermission(SecurityAction.Assert, All = @"C:\Log.txt")]
public void MakeLog()
{
StreamWriter TextStream = new StreamWriter(@"C:\Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}
다음 코드 조각에서는 Assert 메서드를 사용하여 보안 검사를 재정의하는 명령적 구문을 보여 줍니다. 이 예제에서 FileIOPermission 개체의 인스턴스가 선언됩니다. 허용된 액세스 형식을 정의하는 FileIOPermissionAccess.AllAccess가 생성자에게 전달되는데, 그 뒤에는 파일 위치를 나타내는 문자열이 나옵니다. 일단 FileIOPermission 개체가 정의되면 이 개체의 Assert 메서드만 호출하여 보안 검사를 재정의할 수 있습니다.
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub 'New
Public Sub MakeLog()
Dim FilePermission As New FileIOPermission(FileIOPermissionAccess.AllAccess, "C:\Log.txt")
FilePermission.Assert()
Dim TextStream As New StreamWriter("C:\Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now)
TextStream.Close()
End Sub
End Class
End Namespace
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
public void MakeLog()
{
FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.AllAccess,@"C:\Log.txt");
FilePermission.Assert();
StreamWriter TextStream = new StreamWriter(@"C:\Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}