Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
[TestApp.zip]    

(시리즈 글이 8개 있습니다.)
.NET Framework: 439. .NET CLR4 보안 모델 - 1. "Security Level 2"란?
; https://www.sysnet.pe.kr/2/0/1680

.NET Framework: 440. .NET CLR4 보안 모델 - 2. 샌드박스(Sandbox)을 이용한 보안
; https://www.sysnet.pe.kr/2/0/1681

.NET Framework: 441. .NET CLR4 보안 모델 - 3. CLR4 보안 모델에서의 APTCA 역할
; https://www.sysnet.pe.kr/2/0/1682

오류 유형: 228. CLR4 보안 - yield 구문 내에서 SecurityCritical 메서드 사용 불가
; https://www.sysnet.pe.kr/2/0/1683

.NET Framework: 514. .NET CLR2 보안 모델에서의 APTCA 역할 (2)
; https://www.sysnet.pe.kr/2/0/10804

.NET Framework: 573. .NET CLR4 보안 모델 - 4. CLR4 보안 모델에서의 조건부 APTCA 역할
; https://www.sysnet.pe.kr/2/0/10947

.NET Framework: 605. CLR4 보안 - yield 구문 내에서 SecurityCritical 메서드 사용 불가 - 2번째 이야기
; https://www.sysnet.pe.kr/2/0/11041

닷넷: 2219. .NET CLR2 보안 모델에서의 개별 System.Security.Permissions 제어
; https://www.sysnet.pe.kr/2/0/13565




.NET CLR4 보안 모델 - 2. 샌드박스(Sandbox)을 이용한 보안

지난 글에서 .NET 4부터 도입된 "Security Level 2" 모델의 SecurityTransparent, SecuritySafeCritical, SecurityCritical 호출 관계가 어떻게 되는지 살펴봤습니다. 또한 SecurityTransparent를 이용해 어떻게 외부 업체들에게 안전한 어셈블리를 강제할 수 있는지 설명했습니다.

그래도 여전히 별도의 AppDomain을 만들어 보안을 강제하는 것이 필요할 때가 있습니다.

가령, 지난번 글에서 SecurityTransparent 특성을 적용하는 방법에서는 3rd-party 업체들이 SecurityTransparent 특성을 정의하도록 소스코드를 변경해야 하는 어려움(?)이 따릅니다. SecurityTransparent를 적용하지 않으면 외부 업체에서 제공하는 모든 코드들이 SecurityCritical로 분류되기 때문에 비록 내부적으로 보안에 위반하는 코드를 호출하지 않더라도 그에 상관없이 실행 자체가 안되는 사태가 발생합니다.

이 외에도, AppDomain을 이용하는 경우 보다 더 세밀한 보안 제어를 할 수 있다는 장점이 있습니다.

자, 그럼 직접 테스트를 한번 해볼까요? ^^

샌드박싱을 하는 전형적인 코드는 다음에 이미 공개되어 있으므로 이를 가져다 쓰도록 하겠습니다.

How to: Run Partially Trusted Code in a Sandbox
; https://learn.microsoft.com/en-us/dotnet/framework/misc/how-to-run-partially-trusted-code-in-a-sandbox

그래서 우리가 만드는 TestApp 호스트 측의 소스 코드는 다음과 같이 작성됩니다.

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting;
using System.Security;
using System.Security.Policy;

class Program
{
    static void Main(string[] args)
    {
        AppDomainSetup adSetup = new AppDomainSetup();
        adSetup.ApplicationBase = ".";

        Evidence ev = new Evidence();
        ev.AddHostEvidence(new Zone(SecurityZone.MyComputer));

        PermissionSet permSet = SecurityManager.GetStandardSandbox(ev);

        StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();
        AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);

        ObjectHandle handle = Activator.CreateInstanceFrom(
            newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
            typeof(Sandboxer).FullName);
        
        Sandboxer newDomainInstance = (Sandboxer)handle.Unwrap();

        string untrustedAssembly = typeof(Class1).Assembly.FullName;
        string untrustedClass = typeof(Class1).FullName;
        string entryPoint = "DoMethod";
        object [] parameters = null;

        newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);

        entryPoint = "DoCriticalMethod";
        newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);

        Console.WriteLine();
        Console.WriteLine();
        Console.WriteLine("any key to exit ...");
        Console.ReadLine();
    }
}

class Sandboxer : MarshalByRefObject
{
    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)
    {
        MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);

        try
        {
            target.Invoke(null, parameters);
        }
        catch (Exception ex)
        {
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());
        }
    }
}

그렇습니다. 샌드박싱은 별도의 AppDomain 기능을 이용하기 때문에 MarshalByRefObject의 도움이 필요합니다.

여기서는 일단 테스트 결과를 비교할 수 있도록 보안 셋을 완전 신뢰(Full Trust)가 되는 SecurityZone.MyComputer를 기반으로 구성해 보았습니다. 즉, 전혀 보안이 되지 않는 Full Trust 샌드박스를 구현합니다.

다음은 호스트 측에서 로드할 외부 업체의 add-on이 될 TestLib 라이브러리 코드입니다.

using System;
using System.IO;

public class Class1
{
    public static void DoMethod()
    {
        Console.WriteLine("DoMethod called!");
    }

    public static void DoCriticalMethod()
    {
        try
        {
            using (FileStream file = new FileStream(@".\test.dat", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
            {
                Console.WriteLine(file.Handle.ToString());
            }

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }
}

이렇게 작성하고 실행하면, 당연히 정상적으로 DoMethod와 DoCriticalMethod가 잘 실행됩니다. 왜냐하면 샌드박싱을 제공하는 AppDomain의 보안 셋이 .NET 2.0 CAS 모델의 MyComputer Zone이기 때문입니다.

자, 그럼 보안을 제한하기 위해 "Partial Trust" 수준의 샌드박싱이 될 AppDomain을 만들기 위해 호스트 측의 코드를 다음과 같이 수정합니다.

ev.AddHostEvidence(new Zone(SecurityZone.Intranet));

CAS 모델의 Intranet Zone에 해당하는 보안 셋만 허용하므로 이제 AppDomain의 보안 수준은 "Partial Trust"가 됩니다. 이 상태에서 실행해 보면 DoCriticalMethod 실행 단계에서 다음과 같은 오류가 발생합니다.

System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
   at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
   at System.Security.CodeAccessPermission.Demand()
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,FileShare share)
   at Class1.DoCriticalMethod()
The action that failed was:
Demand
The type of the first permission that failed was:
System.Security.Permissions.FileIOPermission
The Zone of the assembly that failed was:
MyComputer

제가 전에 샌드박싱을 구현한 AppDomain에서는 보다 더 세밀한 보안 제어가 가능하다고 했지요? ^^ 바로 이 결과가 그 증거입니다. 즉, 우리가 현재 만든 Intranet 수준의 샌드박스에서는 파일 접근 조차 허용되지 않게 보안을 제어한 것입니다. 이 상태에서 파일 제어를 허용하고 싶다면 다음과 같이 보안 셋을 추가해 주면 됩니다.

using System.Security.Policy;

// ... 생략...

PermissionSet permSet = SecurityManager.GetStandardSandbox(ev);
permSet.AddPermission(new FileIOPermission(PermissionState.Unrestricted));

이제 빌드하고 실행하면 오류 메시지는 다음과 같이 바뀝니다.

System.MethodAccessException: Attempt by security transparent method 'Class1.DoCriticalMethod()' to access security critical method 'System.IO.FileStream.get_Handle()' failed.

Assembly 'TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=feef41772217d3e6' is partially trusted, which causes the CLR to make it entirely security transparent regardless of any transparency annotations in the assembly itself.  In order to access security critical code, this assembly must be fully trusted.
   at Class1.DoCriticalMethod()

이것은 전에 ".NET CLR4 보안 모델 - 1. "Security Level 2"란?"글에서 설명한 상황과 같습니다.

즉, partial trust에서 로드되는 모든 타입 및 그 코드들은 무조건 SecurityTransparent로 분류됩니다. 다시 말하면 해당 어셈블리에 "[assembly: SecurityTransparent]" 속성이 지정된 것이나 다름없는 상태가 되는 것입니다.

이 때문에 샌드박싱된 AppDomain에서 실행되는 모든 외부 업체들의 DLL들은 보안에 민감한 작업을 함부로 할 수 없게 되는 것입니다.

(첨부된 파일은 위의 코드를 테스트한 프로젝트입니다.)




참고로, CLR4 응용 프로그램에서 기존의 CLR2 기반의 보안 모델로 돌아가고 싶은 경우 app.config에 다음의 설정을 해주면 됩니다.

<NetFx40_LegacySecurityPolicy> Element
; https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/netfx40-legacysecuritypolicy-element

<configuration>
   <runtime>
      <NetFx40_LegacySecurityPolicy enabled="true"/>
   </runtime>
</configuration>

하지만, 위의 설정은 EXE 측에서나 가능하므로 라이브러리 업체 측에서 DLL에 대한 보안 권한을 CLR2로 내리기 위해서는 다음의 설정을 포함시키면 됩니다.

[assembly: SecurityRules(SecurityRuleSet.Level1)]

당연한 이야기겠지만, 위의 옵션들은 미처 .NET 4 보안에 대비하지 못해 문제가 발생한 경우 급하게 땜방으로 처리하는 용도로 생각하시고, 장기적인 관점에서 CLR4 보안 모델에 맞게 변경하는 것이 권장됩니다. (실은, 제니퍼 닷넷 제품도 ^^ 초기에 잠시 SecurityRuleSet.Level1로 설정했지만 이제는 CLR4 보안에 맞게 변경되었습니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 2/15/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2014-08-17 10시26분
[Lyn] 와.... 닷넷 너무어렵네요
[guest]
2014-08-18 12시09분
보안은 어디나 어려운 것 같습니다. ^^ (사실, 위의 닷넷 보안을 이해하고 있는 사람은 국내에서 손꼽지 않을까 싶습니다.)
정성태

... 46  47  48  49  50  [51]  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12664정성태6/9/202115299오류 유형: 723. COM+ PIA 참조 시 "This operation failed because the QueryInterface call on the COM component" 오류
12663정성태6/9/202117686.NET Framework: 1065. Windows Forms - 속성 창의 디자인 설정 지원: 문자열 목록 내에서 항목을 선택하는 TypeConverter 제작파일 다운로드1
12662정성태6/8/202115415.NET Framework: 1064. C# COM 개체를 PIA(Primary Interop Assembly)로써 "Embed Interop Types" 참조하는 방법파일 다운로드1
12661정성태6/4/202127421.NET Framework: 1063. C# - MQTT를 이용한 클라이언트/서버(Broker) 통신 예제 [4]파일 다운로드1
12660정성태6/3/202118141.NET Framework: 1062. Windows Forms - 폼 내에서 발생하는 마우스 이벤트를 자식 컨트롤 영역에 상관없이 수신하는 방법 [1]파일 다운로드1
12659정성태6/2/202119035Linux: 40. 우분투 설치 후 MBR 디스크 드라이브 여유 공간이 인식되지 않은 경우 - Logical Volume Management
12658정성태6/2/202116820Windows: 194. Microsoft Store에 있는 구글의 공식 Youtube App
12657정성태6/2/202117626Windows: 193. 윈도우 패키지 관리자 - winget 설치
12656정성태6/1/202115938.NET Framework: 1061. 서버 유형의 COM+에 적용할 수 없는 Server GC
12655정성태6/1/202114594오류 유형: 722. windbg/sos - savemodule - Fail to read memory
12654정성태5/31/202115260오류 유형: 721. Hyper-V - Saved 상태의 VM을 시작 시 오류 발생
12653정성태5/31/202118507.NET Framework: 1060. 닷넷 GC에 새롭게 구현되는 DPAD(Dynamic Promotion And Demotion for GC)
12652정성태5/31/202115973VS.NET IDE: 164. Visual Studio - Web Deploy로 Publish 시 암호창이 매번 뜨는 문제
12651정성태5/31/202116176오류 유형: 720. PostgreSQL - ERROR: 22P02: malformed array literal: "..."
12650정성태5/17/202115504기타: 82. OpenTabletDriver의 버튼에 더블 클릭을 매핑 및 게임에서의 지원 방법
12649정성태5/16/202117699.NET Framework: 1059. 세대 별 GC(Garbage Collection) 방식에서 Card table의 사용 의미 [1]
12648정성태5/16/202116466사물인터넷: 66. PC -> FTDI -> NodeMCU v1 ESP8266 기기를 UART 핀을 연결해 직렬 통신하는 방법파일 다운로드1
12647정성태5/15/202116723.NET Framework: 1058. C# - C++과의 연동을 위한 구조체의 fixed 배열 필드 사용파일 다운로드1
12646정성태5/15/202115526사물인터넷: 65. C# - Arduino IDE의 Serial Monitor 기능 구현파일 다운로드1
12645정성태5/14/202115579사물인터넷: 64. NodeMCU v1 ESP8266 - LittleFS를 이용한 와이파이 접속 정보 업데이트파일 다운로드1
12644정성태5/14/202116847오류 유형: 719. 윈도우 - 제어판의 "프로그램 및 기능" / "Windows 기능 켜기/끄기" 오류 0x800736B3
12643정성태5/14/202116812오류 유형: 718. 서버 유형의 COM+ 사용 시 0x80080005(Server execution failed) 오류 발생
12642정성태5/14/202118513오류 유형: 717. The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine.
12641정성태5/13/202117315디버깅 기술: 179. 윈도우용 .NET Core 3 이상에서 Windbg의 sos 사용법
12640정성태5/13/202120911오류 유형: 716. RDP 연결 - Because of a protocol error (code: 0x112f), the remote session will be disconnected. [1]
12639정성태5/12/202117315오류 유형: 715. Arduino: Open Serial Monitor - The module '...\detection.node' was compiled against a different Node.js version using NODE_MODULE_VERSION
... 46  47  48  49  50  [51]  52  53  54  55  56  57  58  59  60  ...