김경윤 (redstrato@elasticware.com)
부분 신뢰 코드
여러분이 네트워크에서 배포되는 코드를 작성한다면 항상 코드 액세스 보안을 염두에 두어야 한다. 어떤 영역에서는 실행 권한을 가질 수 있고 다른 영역에서는 그렇지 않을 수도 있기 때문이다. 이럴 때는 미리 코드가 해당 권한을 가지는지 확인하고 거기에 따라 적절한 조치를 해주면 된다. 이는 CodeAccessPermission의 파생 클래스를 이용해 확인할 수 있다. 예를 들어 로컬 머신상의 파일에 대한 접근을 하려 한다면다음과 같이 하면 된다.
앞 코드가 내 컴퓨터 영역에서 실행된다면 아무런 문제가 없을 것이나 ‘인터넷’인 경우에는 SecurityException이 발생해 catch {} 내의 코드를 실행하게 될 것이다.
코드 액세스 보안은 스택에 기초하며 어셈블리 단위로 이뤄진다. 지난 호에서 우리는 윈폼 응용 프로그램에서 웹에 있는 컨트롤을 가져와서 사용한 예제를 보았다. RemoteAssembly Consumer.exe라는 어셈블리가 웹 서버 또는 네트워크에 공유되어 있는 DragAnd DropListBox.dll 어셈블리를 로드해 컨트롤을 사용했다.
만약 DragAndDropListBox.dll의 코드내에 제한된 리소스에 대한 접근을 시도하는 코드가 있었다면 역시 예외가 발생했을 것이다. 런타임의 명령 포인터(instruction pointer)가 가리키는 코드의 권한 검사를 하다가 코드가 인터넷에서 배포된 어셈블리에 위치하는 경우에 필요한 권한이 없으면 예외가 발생할 것이다.
다음과 같이 닷넷 프레임워크에서는 데이터베이스, 네트워크, 파일 I/O, 레지스트리 등의 리소스에 대한 퍼미션을 관리하는 클래스를 제공하고 있다.
제약 사항과 극복 방법
닷넷에서는 관리되는 코드가 제한된 리소스에 접근하기 위한 훌륭한 방법들을 제공하는데 Common Dialog, IsolatedStorage 등이 그것이다.
파일 I/O
앞서 보았듯이 웹이나 네트워크에서 배포된 코드는 로컬 파일 시스템에 대한 접근이 제한된다. 하지만 방법은 있다. OpenFileDialog, SaveFileDialog를 사용하는 것이다. 언뜻 이해가 가지 않을 수 있겠지만, 다이얼로그를 띄운다는 것은 사용자가 해당 응용 프로그램이 접근할 수 있는 파일을 정한다는 것을 의미한다. 이에 따라 해당 응용 프로그램은 암시적으로 해당 파일에 대한 접근 권한을 가지게 된다. 그러나 여기에도 몇 가지 제약사항은 있다. 해당 파일의 경로를 얻기 위한 코드는 당연히 제한된다.
앞과 같이 OpenFileDialog의 RestoreDirectory와 FileName 등의 속성이 제한되며 FileName 속성 대신에 OpenFile() 메쏘드를 사용한다.
IsolatedStorage
IsolatedStorage는 응용 프로그램의 상태 정보를 저장하는 데 필요한 저장소를 제공하는 매우 유용한 클래스이다. 닷넷 환경에서는 응용 프로그램 도메인이라는 개념을 도입하여 하나의 프로세스내에서 여러 응용 프로그램을 실행할 수가 있다. IsolatedStorage는 바로 응용 프로그램 도메인 단위로 격리된 저장소를 제공한다. 지난 호에서 소개한 ‘Return of Rich Client : Code Access Security and Distribution Features in .NET Enhance Client-Side Apps’라는 아티클의 예제 중 재미있는 게임이 하나 있는데, 그 예제 코드에서 게임의 상태를 저장할 때 IsolatedStorage를 이용했다. 여기서는 간단한 코드로 설명하겠다. 다음은 응용 프로그램의 상태를 저장하기 위한 파일 스트림을 제공하는 메쏘드다. 이 메쏘드를 통해 얻은 파일 스트림은 기존의 스트림 객체와 동일하게 사용할 수 있다.
다음의 코드는 앞의 윈폼 응용 프로그램의 일부로서 폼의 상태를 저장하는 메쏘드다.
여기서 using은 지시어가 아닌 문(statement)으로서 괄호안에서 생성된 객체는 using문이 끝날 때 자동으로 객체의 Dispose()를 호출한다. 주로 사용된 후 바로 해제해야 할 리소스에 쓰인다. 객체는 반드시 Idisposable을 구현하는 클래스의 인스턴스여야 한다.
웹 서비스의 이용
부분적으로 신뢰되는 코드는 네트워크 리소스 이용에도 제한을 받는다. 소켓 등을 직접 사용할 수 없고, 웹 서비스 이용에도 제한을 받는다. 하지만 코드 베이스, 즉 다운로드된 URL의 웹 서비스는 이용 가능하다. 간단한 예제를 통해 확인해 보자.
비주얼 스튜디오 닷넷(VS.NET)에서 C# ASP.NET 웹 서비스 프로젝트(CalcService)를 하나 생성해 다음과 같이 웹 메쏘드 Add를 추가한다.

<화면 3> Form1

<화면 4> 추가된 웹 참조
윈도우 응용 프로그램 프로젝트를 하나 만들어서 <화면 3>과 같이 폼을 작성한다. 그런 다음 웹 서비스를 참조하기 위해 솔루션 탐색기의 ‘참조’를 마우스 오른쪽 버튼으로 클릭하여 ‘웹 참조’를 선택한다. 웹 참조 추가 다이얼로그의 주소 입력 박스에 웹 서비스의 WSDL 문서 URL(http://localhost/CalcService/Service1.asmx? WSDL)를 입력한다. 이 주소는 웹 서비스의 실행 페이지에서 ‘서비스 설명’을 클릭하여 이동한 페이지의 URL이다. 그리고 ‘참조 추가’를 클릭하면 <화면 4>처럼 해당 웹 서비스에 대한 참조를 확인할 수 있다.
일반적으로 웹 서비스를 이용하는 프로그램이라면 다음과 같이 바로 객체(프록시)를 생성해 사용하면 문제가 없겠지만 네트워크에서 배포된 상태라면 분명 예외가 발생할 것이다. 그리고 localhost 대신에 컴퓨터 이름을 사용하는 경우에도 예외가 발생한다.
이 윈폼 프로그램을 자신의 웹 서버에 올려놓고 실행해 보라. 주소를 http://kky/richtest/smartcalculator.exe(여기서 kky는 필자의 컴퓨터 이름)로 주었을 경우에는 <화면 5>와 같은 다이얼로그가 뜰 것이다.

<화면 5> Security Exception 다이얼로그
http://localhost/richtest/smartcalculator.exe로 주었을 경우에는 문제없이 잘 작동한다.
왜냐하면 calcService 객체의 기본 URL이 http://localhost/ CalcService/Service1.asmx이기 때문이다. 그러므로 calcService의 URL 속성 값을 현재 로드된 어셈블리의 코드베이스로 바꿔줘야 한다.
그리고 button1_Click()에서 ① 부분의 코드는 다음과 같이 바꿔줘야 한다.
그러면 코드는 해당 코드 베이스의 URL로 웹 서비스를 요청하게 되므로 보안 예외를 발생하지 않게 된다. 물론 그 URL에 참조하는 웹 서비스가 있어야 하는 것은 당연하다.
부분 신뢰 코드
여러분이 네트워크에서 배포되는 코드를 작성한다면 항상 코드 액세스 보안을 염두에 두어야 한다. 어떤 영역에서는 실행 권한을 가질 수 있고 다른 영역에서는 그렇지 않을 수도 있기 때문이다. 이럴 때는 미리 코드가 해당 권한을 가지는지 확인하고 거기에 따라 적절한 조치를 해주면 된다. 이는 CodeAccessPermission의 파생 클래스를 이용해 확인할 수 있다. 예를 들어 로컬 머신상의 파일에 대한 접근을 하려 한다면다음과 같이 하면 된다.
try {
FileIOPermission fileIOPerm =
newFileIOPermission(FileIOPermissionAccess.AllAccess, @”c:foo.txt”);
fileIOPerm.Demand();
} catch {
// 필요한 조치를 취한다.
}
FileIOPermission fileIOPerm =
newFileIOPermission(FileIOPermissionAccess.AllAccess, @”c:foo.txt”);
fileIOPerm.Demand();
} catch {
// 필요한 조치를 취한다.
}
앞 코드가 내 컴퓨터 영역에서 실행된다면 아무런 문제가 없을 것이나 ‘인터넷’인 경우에는 SecurityException이 발생해 catch {} 내의 코드를 실행하게 될 것이다.
코드 액세스 보안은 스택에 기초하며 어셈블리 단위로 이뤄진다. 지난 호에서 우리는 윈폼 응용 프로그램에서 웹에 있는 컨트롤을 가져와서 사용한 예제를 보았다. RemoteAssembly Consumer.exe라는 어셈블리가 웹 서버 또는 네트워크에 공유되어 있는 DragAnd DropListBox.dll 어셈블리를 로드해 컨트롤을 사용했다.
만약 DragAndDropListBox.dll의 코드내에 제한된 리소스에 대한 접근을 시도하는 코드가 있었다면 역시 예외가 발생했을 것이다. 런타임의 명령 포인터(instruction pointer)가 가리키는 코드의 권한 검사를 하다가 코드가 인터넷에서 배포된 어셈블리에 위치하는 경우에 필요한 권한이 없으면 예외가 발생할 것이다.
다음과 같이 닷넷 프레임워크에서는 데이터베이스, 네트워크, 파일 I/O, 레지스트리 등의 리소스에 대한 퍼미션을 관리하는 클래스를 제공하고 있다.
System.Security.CodeAccessPermission
System.Data.Common.DBDataPermission
System.Drawing.Printing.PrintingPermission
System.Messaging.MessageQueuePermission
System.Net.DnsPermission
System.Net.SocketPermission
System.Net.WebPermission
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.FileDialogPermission
System.Security.Permissions.FileIOPermission
System.Security.Permissions.IsolatedStoragePermission
System.Security.Permissions.PublisherIdentityPermission
System.Security.Permissions.ReflectionPermission
System.Security.Permissions.RegistryPermission
System.Security.Permissions.ResourcePermissionBase
System.Security.Permissions.SecurityPermission
System.Security.Permissions.SiteIdentityPermission
System.Security.Permissions.StrongNameIdentityPermission
System.Security.Permissions.UIPermission
System.Security.Permissions.UrlIdentityPermission
System.Security.Permissions.ZoneIdentityPermission
System.Data.Common.DBDataPermission
System.Drawing.Printing.PrintingPermission
System.Messaging.MessageQueuePermission
System.Net.DnsPermission
System.Net.SocketPermission
System.Net.WebPermission
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.FileDialogPermission
System.Security.Permissions.FileIOPermission
System.Security.Permissions.IsolatedStoragePermission
System.Security.Permissions.PublisherIdentityPermission
System.Security.Permissions.ReflectionPermission
System.Security.Permissions.RegistryPermission
System.Security.Permissions.ResourcePermissionBase
System.Security.Permissions.SecurityPermission
System.Security.Permissions.SiteIdentityPermission
System.Security.Permissions.StrongNameIdentityPermission
System.Security.Permissions.UIPermission
System.Security.Permissions.UrlIdentityPermission
System.Security.Permissions.ZoneIdentityPermission
제약 사항과 극복 방법
닷넷에서는 관리되는 코드가 제한된 리소스에 접근하기 위한 훌륭한 방법들을 제공하는데 Common Dialog, IsolatedStorage 등이 그것이다.
파일 I/O
앞서 보았듯이 웹이나 네트워크에서 배포된 코드는 로컬 파일 시스템에 대한 접근이 제한된다. 하지만 방법은 있다. OpenFileDialog, SaveFileDialog를 사용하는 것이다. 언뜻 이해가 가지 않을 수 있겠지만, 다이얼로그를 띄운다는 것은 사용자가 해당 응용 프로그램이 접근할 수 있는 파일을 정한다는 것을 의미한다. 이에 따라 해당 응용 프로그램은 암시적으로 해당 파일에 대한 접근 권한을 가지게 된다. 그러나 여기에도 몇 가지 제약사항은 있다. 해당 파일의 경로를 얻기 위한 코드는 당연히 제한된다.
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.ShowReadOnly = true
//openFileDialog.RestoreDirectory = true;
// throw SecurityException
if(openFileDialog.ShowDialog() == DialogResult.OK)
{
FileStream fs = (FileStream)openFileDialog.OpenFile();
//FileStream fs = new FileStream(openFileDialog.FileName);
// 예외 발생
openFileDialog.ShowReadOnly = true
//openFileDialog.RestoreDirectory = true;
// throw SecurityException
if(openFileDialog.ShowDialog() == DialogResult.OK)
{
FileStream fs = (FileStream)openFileDialog.OpenFile();
//FileStream fs = new FileStream(openFileDialog.FileName);
// 예외 발생
앞과 같이 OpenFileDialog의 RestoreDirectory와 FileName 등의 속성이 제한되며 FileName 속성 대신에 OpenFile() 메쏘드를 사용한다.
IsolatedStorage
IsolatedStorage는 응용 프로그램의 상태 정보를 저장하는 데 필요한 저장소를 제공하는 매우 유용한 클래스이다. 닷넷 환경에서는 응용 프로그램 도메인이라는 개념을 도입하여 하나의 프로세스내에서 여러 응용 프로그램을 실행할 수가 있다. IsolatedStorage는 바로 응용 프로그램 도메인 단위로 격리된 저장소를 제공한다. 지난 호에서 소개한 ‘Return of Rich Client : Code Access Security and Distribution Features in .NET Enhance Client-Side Apps’라는 아티클의 예제 중 재미있는 게임이 하나 있는데, 그 예제 코드에서 게임의 상태를 저장할 때 IsolatedStorage를 이용했다. 여기서는 간단한 코드로 설명하겠다. 다음은 응용 프로그램의 상태를 저장하기 위한 파일 스트림을 제공하는 메쏘드다. 이 메쏘드를 통해 얻은 파일 스트림은 기존의 스트림 객체와 동일하게 사용할 수 있다.
privateIsolatedStorageFileStream CreateSettingsStream()
{
IsolatedStorageFile store =
IsolatedStorageFile.GetUserStoreForDomain();
return new IsolatedStorageFileStream(“settings.txt”, FileMode.Create, store);
}
{
IsolatedStorageFile store =
IsolatedStorageFile.GetUserStoreForDomain();
return new IsolatedStorageFileStream(“settings.txt”, FileMode.Create, store);
}
다음의 코드는 앞의 윈폼 응용 프로그램의 일부로서 폼의 상태를 저장하는 메쏘드다.
private void SaveSettings()
{
// Restore so we don‘t store a zero size or location
WindowState = FormWindowState.Normal;
// Save the window location
using( Stream stream = CreateSettingsStream() )
using( StreamWriter writer = newStreamWriter(stream) )
{
WriteSetting(writer, Location);
WriteSetting(writer, ClientSize);
}
}
{
// Restore so we don‘t store a zero size or location
WindowState = FormWindowState.Normal;
// Save the window location
using( Stream stream = CreateSettingsStream() )
using( StreamWriter writer = newStreamWriter(stream) )
{
WriteSetting(writer, Location);
WriteSetting(writer, ClientSize);
}
}
여기서 using은 지시어가 아닌 문(statement)으로서 괄호안에서 생성된 객체는 using문이 끝날 때 자동으로 객체의 Dispose()를 호출한다. 주로 사용된 후 바로 해제해야 할 리소스에 쓰인다. 객체는 반드시 Idisposable을 구현하는 클래스의 인스턴스여야 한다.
using (expression | type identifier = initializer)
{
...
}
{
...
}
웹 서비스의 이용
부분적으로 신뢰되는 코드는 네트워크 리소스 이용에도 제한을 받는다. 소켓 등을 직접 사용할 수 없고, 웹 서비스 이용에도 제한을 받는다. 하지만 코드 베이스, 즉 다운로드된 URL의 웹 서비스는 이용 가능하다. 간단한 예제를 통해 확인해 보자.
비주얼 스튜디오 닷넷(VS.NET)에서 C# ASP.NET 웹 서비스 프로젝트(CalcService)를 하나 생성해 다음과 같이 웹 메쏘드 Add를 추가한다.
public class Service1 : System.Web.Services.WebService
{
...
[WebMethod]
public int Add(int a, int b)
{
{
...
[WebMethod]
public int Add(int a, int b)
{


return a+b;
}
}
}
}
윈도우 응용 프로그램 프로젝트를 하나 만들어서 <화면 3>과 같이 폼을 작성한다. 그런 다음 웹 서비스를 참조하기 위해 솔루션 탐색기의 ‘참조’를 마우스 오른쪽 버튼으로 클릭하여 ‘웹 참조’를 선택한다. 웹 참조 추가 다이얼로그의 주소 입력 박스에 웹 서비스의 WSDL 문서 URL(http://localhost/CalcService/Service1.asmx? WSDL)를 입력한다. 이 주소는 웹 서비스의 실행 페이지에서 ‘서비스 설명’을 클릭하여 이동한 페이지의 URL이다. 그리고 ‘참조 추가’를 클릭하면 <화면 4>처럼 해당 웹 서비스에 대한 참조를 확인할 수 있다.
일반적으로 웹 서비스를 이용하는 프로그램이라면 다음과 같이 바로 객체(프록시)를 생성해 사용하면 문제가 없겠지만 네트워크에서 배포된 상태라면 분명 예외가 발생할 것이다. 그리고 localhost 대신에 컴퓨터 이름을 사용하는 경우에도 예외가 발생한다.
private void button1_Click(object sender, System.EventArgs e)
{
if ( textBox1.Text == “” || textBox2.Text == “” )
return
localhost.Service1 calcService = new localhost.Service1(); // ①
int a = Convert.ToInt32(textBox1.Text);
int b = Convert.ToInt32(textBox2.Text);
int sum = calcService.Add(a, b);
textBox3.Text = sum.ToString();
}
{
if ( textBox1.Text == “” || textBox2.Text == “” )
return
localhost.Service1 calcService = new localhost.Service1(); // ①
int a = Convert.ToInt32(textBox1.Text);
int b = Convert.ToInt32(textBox2.Text);
int sum = calcService.Add(a, b);
textBox3.Text = sum.ToString();
}
이 윈폼 프로그램을 자신의 웹 서버에 올려놓고 실행해 보라. 주소를 http://kky/richtest/smartcalculator.exe(여기서 kky는 필자의 컴퓨터 이름)로 주었을 경우에는 <화면 5>와 같은 다이얼로그가 뜰 것이다.

http://localhost/richtest/smartcalculator.exe로 주었을 경우에는 문제없이 잘 작동한다.
왜냐하면 calcService 객체의 기본 URL이 http://localhost/ CalcService/Service1.asmx이기 때문이다. 그러므로 calcService의 URL 속성 값을 현재 로드된 어셈블리의 코드베이스로 바꿔줘야 한다.
private localhost.Service1 GetCalcService()
{
localhost.Service1 calcService = new localhost.Service1();
try
{
string appBase = AppDomain.CurrentDomain.BaseDirectory;
string site = System.Security.Policy.Site.CreateFromUrl(appBase).Name;
calcService.Url = calcService.Url.Replace(“//localhost/”, “//” + site + “/”);
}
catch
{}
return calcService;
}
{
localhost.Service1 calcService = new localhost.Service1();
try
{
string appBase = AppDomain.CurrentDomain.BaseDirectory;
string site = System.Security.Policy.Site.CreateFromUrl(appBase).Name;
calcService.Url = calcService.Url.Replace(“//localhost/”, “//” + site + “/”);
}
catch
{}
return calcService;
}
그리고 button1_Click()에서 ① 부분의 코드는 다음과 같이 바꿔줘야 한다.
localhost.Service1 calcService = GetCalcService();
그러면 코드는 해당 코드 베이스의 URL로 웹 서비스를 요청하게 되므로 보안 예외를 발생하지 않게 된다. 물론 그 URL에 참조하는 웹 서비스가 있어야 하는 것은 당연하다.
"Web" 카테고리의 다른 글
- 개발자의 새로운 키, 웹2.0 [1부] (0)2007/04/20
- 닷넷과 리치 클라이언트 이해 2 - 3 (0)2006/01/24
- 닷넷과 리치 클라이언트 이해 2 - 2 (0)2006/01/24
- 닷넷과 리치 클라이언트 이해 2 - 1 (0)2006/01/24
- 닷넷과 리치 클라이언트 이해 1 - 3 (0)2006/01/21

수안이의 컴퓨터 연구실



Leave your greetings.