성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>.NET CLR4 보안 모델 - 1. "Security Level 2"란?</h1> <p> .NET 3.5 SP1부터 이미 네트워크 공유로부터 실행되는 응용 프로그램의 권한이 Full Trust로 바뀌면서 사실 대부분의 닷넷 프로그래머들은 이로 인해 그동안 겪었던 숱한 보안 오류를 피할 수 있었습니다.<br /> <br /> 하지만, 마이크로소프트는 .NET 4부터 기존의 보안 모델을 업그레이드하는 작업을 합니다. (CAS가 없어진 것이 아니라 업그레이드입니다. 내부적으로 CAS는 여전히 살아 있습니다.) 이름하여 "Security Level 2" 모델입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > What's New in Code Access Security in .NET Framework 4.0 - Part I ; <a target='tab' href='https://www.simple-talk.com/dotnet/.net-framework/whats-new-in-code-access-security-in-.net-framework-4.0---part-i/'>https://www.simple-talk.com/dotnet/.net-framework/whats-new-in-code-access-security-in-.net-framework-4.0---part-i/</a> What's New in Code Access Security in .NET Framework 4.0 - Part 2 ; <a target='tab' href='https://www.simple-talk.com/dotnet/.net-framework/whats-new-in-code-access-security-in-.net-framework-4.0---part-2/'>https://www.simple-talk.com/dotnet/.net-framework/whats-new-in-code-access-security-in-.net-framework-4.0---part-2/</a> </pre> <br /> "Security Level 2" 모델의 동작 방식은 굉장히 간단합니다. 코드는 SecurityTransparent, SecuritySafeCritical, SecurityCritical 이렇게 딱 3가지로 부류로만 나뉘고 그 사이에는 다음과 같은 호출 규칙이 있습니다.<br /> <br /> <ul> <li>SecurityTransparent 코드는 SecurityCritical을 제외하고 호출 가능</li> <li>SecuritySafeCritical, SecurityCritical 코드는 모든 부류의 코드를 호출 가능</li> </ul> <br /> 이를 간단히 정리하면 다음과 같은 한 가지 제약을 발견할 수 있습니다.<br /> <br /> [출처: <a target='tab' href='https://www.simple-talk.com/dotnet/.net-framework/whats-new-in-code-access-security-in-.net-framework-4.0---part-i/'>What's New in Code Access Security in .NET Framework 4.0 - Part I</a>]<br /> <img alt='net4_sec_model_1.png' src='/SysWebRes/bbs/net4_sec_model_1.png' /> <br /><br /> 즉, SecurityCritical로 분류된 코드는 SecurityTransparent 측에서 호출하려면 반드시 그 중간에 SecuritySafeCritical에 해당하는 코드를 마련해 줘야 합니다.<br /> <br /> 원칙은 간단하지만, 이게 어떻게 보안과 관계있는지 감이 안 오는데요. 대략 어떤 것인지 테스트를 통해 체험해 보는 것이 좋겠지요. ^^<br /> <br /> 우선 라이브러리 프로젝트를 하나 만들고, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; public class Class1 { public void DoMethod() { Console.WriteLine("DoMethod called!"); } } </pre> <br /> 이를 사용하는 콘솔 프로젝트를 만듭니다. (물론 모두 .NET 4.0 대상으로 합니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class Program { static void Main(string[] args) { Class1 cl = new Class1(); cl.DoMethod(); } } </pre> <br /> 실행하면 당연히 잘 되겠지요. ^^<br /> <br /> 자, 이제 현실적인 문제 상황을 가정합니다. 여러분이 만든 프로그램이 있고, 그 프로그램에 누구든지 만들 수 있는 Plug-in을 로드해서 사용하는 기능이 있다고 가정해 보겠습니다. .NET 2.0 시절에는 이런 상황에서 보안이 축소된 별도의 AppDomain을 만들고 그 안에서 plug-in을 로드해야 했습니다. 그런데, 이렇게 구현하다 보면 사실 문제가 많습니다. AppDomain간의 호출이므로 MarshalByRefObject 상속도 처리해야 하고 개별 개체의 Serialize 문제도 고려해야 합니다.<br /> <br /> 그런데, .NET 4.0부터는 이를 아주 간단하게 해결할 수 있습니다.<br /> <br /> AppDomain 생성 과정없이 단지 plug-in을 로드하는 코드를 별도의 DLL에 만들고 그것에 SecurityTransparent 특성을 부여하기만 하면 됩니다. 실제로 어떻게 동작하는지 한번 테스트 해볼까요? 이를 위해 다음과 같이 3개의 프로젝트를 만들고,<br /> <br /> <ul> <li>HostApp (콘솔 프로젝트: 우리가 만드는 응용 프로그램)</li> <li>LoadLib (라이브러리 프로젝트: 3rd-party 라이브러리를 로드해서 그 기능을 호출하는 클래스를 정의)</li> <li>UntrustedLib (라이브러리 프로젝트: 3rd-party에서 제작한 라이브러리라고 가정)</li> </ul> <br /> HostApp의 소스 코드는 이렇게,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class Program { static void Main(string[] args) { LoadManager lm = new LoadManager(); lm.DoFunc(); } } </pre> <br /> LoadLib는 이렇게,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using UntrustedLib; using System.Security; <span style='color: blue; font-weight: bold'>[assembly: SecurityTransparent]</span> public class LoadManager { public void DoFunc() { PlugInExtension plugin = new PlugInExtension(); plugin.NormalMethod(); } } </pre> <br /> 마지막으로 UntrustedLib 코드는 이렇게 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Security; namespace UntrustedLib { public class PlugInExtension { public void NormalMethod() { Console.WriteLine("NormalMethod"); } } } </pre> <br /> 물론, 현실적으로 보면 LoadLib 어셈블리가 UntrustedLib 어셈블리를 동적으로 Assembly.Load를 이용하는 것이 보통이겠지만 여기서는 (어차피 결과는 같으므로) 테스트를 간단히 하기 위해 직접 참조해서 사용하겠습니다.<br /> <br /> 위와 같이 구성하고 실행하면 다음과 같은 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled Exception: System.MethodAccessException: <span style='color: blue; font-weight: bold'>Attempt by security transparent method 'LoadManager.DoFunc()'</span> to access security critical method 'UntrustedLib.PlugInExtension..ctor()' failed. at LoadManager.DoFunc() in d:\...\LoadLib\LoadManager.cs:line 9 at Program.Main(String[] args) in d:\...\HostApp\Program.cs:line 7 </pre> <br /> 오류 메시지의 의미는, DoFunc 메서드는 (그것이 정의된 어셈블리에 [assembly: SecurityTransparent] 특성을 정의했으므로) SecurityTransparent에 속하지만, 그 내부에서 사용하는 PlugInExtension은 SecurityCritical에 속하므로 호출할 수 없다는 것입니다.<br /> <br /> PlugInExtension 타입이 정의된 UntrustedLib에는 [assembly: SecurityTransparent]를 설정하지 않았으므로 기본적으로 모든 타입의 코드가 SecurityCritical 부류에 속합니다.<br /> <br /> 이 문제를 해결하려면 어떻게 해야 할까요? PlugInExtension 타입에 SecuritySafeCritical을 적용하면 될까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Security; namespace UntrustedLib { <span style='color: blue; font-weight: bold'>[SecuritySafeCritical]</span> public class PlugInExtension { <span style='color: blue; font-weight: bold'>[SecuritySafeCritical]</span> public void NormalMethod() { Console.WriteLine("NormalMethod"); } } } </pre> <br /> 상식적으로 생각해 봅시다. Host 프로그램 측에서 보안을 적용하기 위해 SecurityTransparent가 적용된 LoadLib 어셈블리를 경유해 UntrustedLib 어셈블리를 사용하는데, UntrustedLib 스스로 SecurityCritical을 호출할 수 있는 상태로 만드는 것이 허용된다면 이것은 절대 보안이라고 부를 수 없습니다.<br /> <br /> 이런 경우, .NET 4.0 CLR은 UntrustedLib 어셈블리 내부에 적용된 "Security Level 2"의 모든 속성을 무시하고 무조건 SecurityCritical로 여깁니다.<br /> <br /> 따라서, PlugInExtension 타입을 정상적으로 사용하려면 그것이 정의된 UntrustedLib 어셈블리도 "[assembly: SecurityTransparent]" 특성이 정의되어야 합니다. 그럼 UntrustedLib 어셈블리에 정의된 모든 타입은 SecurityTransparent에 속하게 됩니다. 이렇게 변경하고 빌드를 한 후 실행하면 이제 정상적으로 NormalMethod 메서드가 호출됩니다.<br /> <br /> 이 상태에서 PlugInExtension 타입에 다음의 메서드를 추가해 볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.IO; using System.Reflection; using System.Security; using System.Text; <span style='color: blue; font-weight: bold'>[assembly: SecurityTransparent]</span> namespace UntrustedLib { public class PlugInExtension { // ... 생략: NormalMethod public void AccessCritical() { try { <span style='color: blue; font-weight: bold'>using (FileStream file = new FileStream(@".\test.dat", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { Console.WriteLine(file.Handle.ToString()); }</span> } catch (Exception e) { Console.WriteLine(e.ToString()); } } } } </pre> <br /> 이전에 설명한대로 "[assembly: SecurityTransparent]" 특성이 적용되었으므로 PlugInExtension 타입 및 그 내부에 구현된 모든 메서드들은 SecurityTransparent 부류에 속하므로 AccessCritical 메서드 역시 SecurityTransparent에 속합니다. 그리고 그 내부에서 호출되는 코드를 보니 2개의 메서드가 있습니다.<br /> <br /> <ul> <li>FileStream 생성자</li> <li>FileStream.Handle 공용 속성의 get 메서드</li> </ul> <br /> FileStream 생성자를 .NET Reflector나 Visual Studio 2013의 F12(Go To Definition) 기능을 이용해 들어가 보면 우리가 사용한 생성자 정의를 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>[SecuritySafeCritical]</span> public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : this(path, mode, access, share, 0x1000, FileOptions.None, Path.GetFileName(path), false) { } </pre> <br /> 그렇군요. 우리가 호출한 FileStream 생성자는 SecuritySafeCritical에 속하므로 SecurityTransparent의 AccessCritical 메서드에서 정상적으로 호출할 수 있었습니다.<br /> <br /> 그다음, FileStream.Handle의 get 메서드 정의를 볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public virtual IntPtr Handle { [<span style='color: blue; font-weight: bold'>SecurityCritical</span>, SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { // ... } } </pre> <br /> 오호... get_Handle 메서드는 SecurityTransparent측에서 호출이 불가능한 SecurityCritical에 속하는 군요. 그래서 이 상태에서 빌드하고 실행하면 Handle 속성을 접근하는 단계에서 보안 에러가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.MethodAccessException: Attempt by <span style='color: blue; font-weight: bold'>security transparent</span> method 'UntrustedLib.PlugInExtension.AccessCritical()' to access <span style='color: blue; font-weight: bold'>security critical method</span> 'System.IO.FileStream.get_Handle()' failed. at UntrustedLib.PlugInExtension.AccessCritical() in d:\...\UntrustedLib\PlugInExtension.cs:line 25 </pre> <br /> 즉, UntrustedLib 어셈블리를 제공하는 3rd-party들은 plug-in 제작시 자신의 어셈블리에 "[assembly: SecurityTransparent]" 특성을 적용해야 하고, 이 때문에 그 스스로 내부에서 SecurityCritical에 속한, 보안에 민감한 메서드를 호출할 수 없는 것입니다.<br /> <br /> 결국, SecurityTransparent의 강제성으로 인해 그 이후로 호출되는 모든 어셈블리들이 보안에 위반하는 코드를 호출할 수 없다는 것이 보장됩니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=845&boardid=331301885'>첨부된 파일은 위의 코드를 테스트한 프로젝트</a>입니다.)<br /> </p><br /> <br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1513
(왼쪽의 숫자를 입력해야 합니다.)