.NET Framework Security Code Access Security-應用程式篇

 
.NET Framework Security
Code Access Security- 應用程式篇
 
 
漫談程式安全性
 
   DOS 時代,應用程式的安全性並未受到重視,這是因為當時個人電腦的使用者通常只有一位,加上電腦的不普及,應用程式多半對於安全性不會有太多的著墨,充其量也只是做到帳號的分級而已。在進入 Windows 時代後,這個情形並未有重大的改善,直到網路時代的來臨,安全性日趨受到重視,尤其在網路入侵及病毒橫行的今天,電腦不灌上防毒軟體及防火牆的話,簡直與拿刀擱在脖子上無異。雖然 Windows 提供了完整的帳號管理及權限控制,但因使用習慣的關係,縱使 Windows 在安裝時就提醒使用者,不要以 Administrator 做為日常操作電腦的帳號,還是有大多數人違背此一準則,這使得大多數的軟體操作都在不受限的 OS 中操作,這會有什麼壞處呢?舉一個簡單的例子,你撰寫了一套軟體,該軟體使用了許多外購的組件,當你更新其中一個組件時,你如何確認該組件沒有做不該做的事?一個利用 Google 來搜尋網路資料的組件,必定得在擁有網路存取權限下運作,但你如何肯定,該組件不會存取 Google 外的網站呢?不會將使用者的資料傳給其它網站呢?這個場景披露出在安全性管理中的一大漏洞,這點在現今的中大型軟體架構中更顯得駭人,身為程式設計師的我們,該如何防堵這個漏洞呢?
 
CAS ,以 Code 為對象的安全機制
 
  在上面的例子中,我們明顯看出問題是出在安全性管理的細度上,原有的角色與使用者管控機制已難以防堵此一漏洞,必須將安全性管理落實到更細的點上,也就是程式碼本身。 .NET Framework 提供了 CAS(Code Access Security) 機制,允許程式設計師針對程式碼進行更細膩的軟體控制,以上面的例子來說, CAS 可以控制該組件只能存取 Google 網站,當該組件存取其它網站時將會引發安全性例外,不只如此, CAS 同時也可以限制該組件不能存取 Registr y ,IO,環境變數、資料庫,甚至是使用Unmanaged程式碼等等,在.NET Framework 2.0更可以限制其不能存取特定的記憶體區塊。概觀來看,CAS填補了角色與使用者安全機制的缺口,提供了更細膩的安全性管控,那麼CAS是如何運作的呢?CAS建立在安全的基礎函式庫上,也就是Secure Library概念,用存取網路為例,當應用程式需要存取網路時,會利用.NET Framework所提供的Socket、Web、TCP等網路組件來存取網路,而這些組件會要求對應的權限,如DncPermission、WebPermission、SocketPermission等等,因此假如設計者在呼叫該組件時降低了這些權限或是拒絕存取,Socket、Web、TCP等網路組件在要求權限時就會產生例外,藉此建立以Code為對象的安全機制。以上的說明點出了一個重點,假如不是使用.Net Framework內建的網路組件呢?那麼CAS是否就崩潰了呢?你的憂慮是正確的,CAS建立於安全的基礎函式庫上,假如組件跨越了基礎函式庫,那麼他就不在CAS的控制範圍內了,後面針對此問題會有更深入的討論。
 
建構安全的應用程式
 
  在軟體日漸複雜及大型化的今日,一個應用程式不再是單一軟體公司所獨力完成的,其中或多或少都使用到了其它協力廠商所提供的函式庫或是組件,這加快了軟體的開發速度,也增加了軟體安全性管理的困難度,如上面的例子,你如何確認所使用的組件是安全的呢?又該如何防堵組件的不正常運作呢?諸如此類的安全性憂慮,在在的提醒程式設計師,該是正視應用程式安全性問題了,我們不能再將這些問題留給 OS 與使用者去解決,而是要起而行的將安全性加到設計軟體的流程中了,讓應用程式主體能在安全、穩固的環境中執行。要做到這點,程式設計師在使用一個組件時,應該審慎的思考,該組件所需擁有的權限與該防堵的權限,做出適當的調整。舉個例子來說,一個 SMTP 組件應該擁有存取網路的權限,但不應該擁有存取網頁、寫入特定目錄以外的目錄、讀取環境變數、存取資料庫、存取非指定網路 IP Port 的權限。
 
建構安全的組件
 
  除了由應用程式角度來思考外,組件設計者也必須從組件角度來思考,舉例來說,你寫了一個 Email 的組件,其中呼叫了 Windows API 來傳送電子郵件,因此該組件必須要擁有執行 Unmanaged 程式碼的權限,身為一個組件設計者,你得建立一個新的 Permissio n ,並要求呼叫者必須擁有此一權限才能呼叫,避免惡意的呼叫者藉由你的組件來攻擊別人。
 
識別,是安全控制的第一道防線
 
  識別,是安全控制的第一道防線,不管是建構安全的應用程式或是組件,辨識組件或應用程式的來源是首要任務, .NET Framework 提供幾種識別組件來源的方式。
名稱
說明
Security.Permissions.PublisherIdentityPermission
以發行者為識別的權限管理。
Security.Permissions.SiteIdentityPermission
Site 為識別的權限管理。
Security.Permissions.StrongNameIdentityPermission
StrongName 為識別的權限管理。
Security.Permissions.UrlIdentityPermission
Url 為識別的權限管理。
Security.Permissions.ZoneIdentityPermission
Zone 為識別的權限管理。
不管使用何種識別方式, Strong Name( 強式名稱 ) 是最基本的要求,安全的應用程式應該避免使用未擁有 Strong Name 的組件。
 
內建 Permission 一覽
 
 .NET Framework 內建了許多 Permission 供設計師使用,見表一。
表一    .Net Framework 內建的 Permission 物件
名稱
說明
Data.Odbc.OdbcPermission
使用 ADO.NET ODBC Provider 的權限。
Data.OleDb.OleDbPermission
使用 ADO.NET OLE DB Provider 的權限。
Data.SqlClient.SqlClientPermission
使用 ADO.NET SQL Client Provider 的權限。
Data.OracleClient.OraclePermission
使用 ADO.NET Oracle Provider 的權限。
Drawing.Printing.PrintingPermission
列印功能的權限控制。
Messaging.MessageQueuePermission
MSMQ 的權限控制。
Net.DnsPermission
存取 DNS 的權限。
Net.SocketPermission
使用 Socket 的權限。
Net.WebPermission
存取 Web 的權限。
Security.Permissions.EnvironmentPermission
存取環境變數的權限。
Security.Permissions.FileDialogPermission
使用檔案對話框的權限。
Security.Permissions.FileIOPermission
存取檔案的權限。
Security.Permissions.IsolatedStoragePermission
Isolated Storage 存取的權限。
Security.Permissions.ReflectionPermission
使用 Reflection 的權限。
Security.Permissions.RegistryPermission
存取 Registry 的權限。
Diagnostics.EventLogPermission
存取事件記錄的權限。
Diagnostics.PerformanceCounterPermission
存取效能計數器的權限。
DirectoryServices.DirectoryServicesPermission
存取 Activate Directory 服務的權限。
ServiceProcess.ServiceControllerPermission
控制服務 (Services) 的權限。
Security.Permissions.SecurityPermission
一般性的安全權限,如 Reflection Unmanaged Code 等等。
Security.Permissions.UIPermission
UI 的存取權限。
Web.AspNetHostingPermission
ASP.NET Host 的權限。
PrincipalPermission
Windows 帳號為識別的權限管理。
PrintingPermission
印表機的存取權限。
由於篇幅的關係,筆者無法於此篇文章中一一介紹這些 Permission 物件的用途及用法,僅選出其中幾個較常用的來介紹。這些 Permission 至少接受一個 SecurityAction 的參數,用來指定該 Permission 所允許的範圍,其定義如表 2
2
說明
可套用的地方
LinkDemand
驗證僅發生於 JIT 編譯時,因此能得到較高的效率,但此一模式不適用於擁有介面的類別,因為直接由介面呼叫可以跨越此一限制。
類別、方法、屬性
InheritanceDemand
要求繼承者必需擁有此權限。
Demand
CAS 要求此權限。
Deny
拒絕此權限。
PermitOnly
除此權限外,拒絕其它權限的要求。
Assert
這是一個特殊的權限,能在呼叫者未擁有對應權限時執行該權限所限制的動作,使用此一權限必須擁有 Assertion 權限。
RequestMinimum
只要求此一權限,其它的權限將被拒絕。
Assembly
RequestOptional
RequestMinimum 搭配使用,也就是說, RequestMinmum 要求 FileIOPermission ,除了 FileIOPermission 之外的權限要求都會被拒絕,假如以 RequestOptional 要求一個 SocketPermission 的話,那麼這個 Assembly 就允許兩種權限, FileIOPermission SocketPermission
RequestRefuse
拒絕此一權限的要求。
 
小試身手,一個簡單的範例
 
  .NET Framework 內建了這麼多的 Permissio n ,我該在何時使用,又該如何使用她們來建構安全的應用程式呢?這個答案其實很簡單,只要你能接受撰寫應用程式時順便將安全性列入考量,那麼使用她們就只是直覺性的反應罷了,舉個例子來說,當你拿到了一個Assembly,該Assembly擁有一個OpenFile的函式,其會讀取一個檔案,分析其內容,傳回一個有意義的結果,照理來說,這個函式不應該有刪除檔案、或是存取其它目錄的權利,但不幸的是這個函式內容如程式1。
程式1
public void OpenFile(string fileName)
{
     System.IO.File.Delete(fileName);
}
不管是惡意還是疏忽,這個結果絕不是設計師所想要的結果,那麼該如何避免此結果呢?程式 2 使用的 FileIOPermission 可以避免這個結果的發生。
程式 2
[System.Security.Permissions.FileIOPermission(System.Security.Permissions.SecurityAction.PermitOnly, Read = @"D:\Temp")]
private void OpenFile()
{
     ClassLibrary6.Class1 c = new ClassLibrary6.Class1();
     c.OpenFile(@"D:\Temp\0050.gif");               
}
當應用程式呼叫程式 2 的函式時,該函式將被限制在只能讀取 D:\Temp 目錄中的檔案,刪除、或是讀取其它目錄的動作都會被拒絕,這達到了我們的目的。
 
使用 .NET Framework Configuration 工具
 
  除了直接於程式中管控外,使用者也可以直接於本機上的 .NET Framework 設定工具中管理權限,兩者的不同之處在於程式內管控是主動防禦,不會因為使用者的疏忽導致安全性漏洞,但缺點則是彈性較低。反之 .NET Framework 設定工具採取的是被動式防禦,優點是彈性高,缺點就是容易因使用者疏忽導致破洞,當然!兩者並存是最好的方式。該工具位於系統管理工具中,執行畫面如圖 1
1
.NET Framework Security Code Access Security-應用程式篇_第1张图片
.NET Framework 內建三個原則:企業、電腦及使用者,每個原則下管理著一群 Code Group( 程式碼群組 ) ,每個程式碼群組繫結一個權限集合,整個展開如圖2。
圖2
.NET Framework Security Code Access Security-應用程式篇_第2张图片
以前一節的例子來說,假設 OpenFile 函式是位於 Class1 類別中,而 Class1 是位於 ClassLibrary6 這個 Assembly 中,由於 ClassLibrary6 是位於本機電腦中,因此要調整此 Assembly 權限的方式是在電腦中的程式碼群組內的 My_Computer_Zone 中新增一個程式碼群組,如圖 3
3
.NET Framework Security Code Access Security-應用程式篇_第3张图片
輸入程式碼群組名稱後點選下一步選擇過濾條件。
4
.NET Framework Security Code Access Security-應用程式篇_第4张图片
ClassLibrary6 是一個具備 Strong Name( 強式名稱 ) Assembly ,因此此處就可以選擇以強式名稱做為過濾條件。
5
.NET Framework Security Code Access Security-應用程式篇_第5张图片
點選下一步後,開始指派權限集合給這一個程式碼群組, .Net Framework 內建了數個權限集合供使用者套用,不過此處我們選擇建立一個新的權限集合。
6
.NET Framework Security Code Access Security-應用程式篇_第6张图片
輸入集合名稱後,接著要選擇要指派的權限,此處只允許 ClassLibrary6 讀取 D:\Temp 目錄下的檔案,如圖 7
7
.NET Framework Security Code Access Security-應用程式篇_第7张图片
接著還得賦與可執行的權限給這個集合,否則將無法執行此一 Assembly ,如圖 8
8
.NET Framework Security Code Access Security-應用程式篇_第8张图片
完成後還得做另一個動作讓此程式碼群組正常運作,點選 ClassLibrary6 這個 Code Group ,選擇內容,勾選如圖 9 的選項。
9
.NET Framework Security Code Access Security-應用程式篇_第9张图片
這個動作是將此 Code Group 限制於本身,不繼承 My_Computer_Zone 的完全信任權限。完成後 ClassLibrary6 就只能夠讀取 D:\Temp 中的檔案,無法寫入,也無法存取其它權限所限制的資源,如 Socket Registry 等等。
 
限制網站存取
 
   前面提了一個網站存取的例子,那麼我們該如何利用內建的 Permission 來達到限制網站存取的目的呢?程式 3 利用了 WebPermission 將該函式限制於只能存取 http://www.hinet.net 網站。
程式 3
[System.Net.WebPermission(SecurityAction.PermitOnly, ConnectPattern=@"http://www\.hinet\.net/")]
private void TestWeb(string url)
{
           System.Net.HttpWebRequest req =
 (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
}
想當然爾,所有存取 Hinet 網站以外的動作都會引發 SecurityException
 
限制存取 Registry
 
    使用 RegistryPermission 物件可以限制對於 Registry 的存取,如程式 4
程式 4
[RegistryPermission(SecurityAction.Deny,Read = @"HKEY_LOCAL_MACHINE\Software")]
private string TestRegistryPermission(string regPath,string key)
{
           RegistryKey r = Registry.LocalMachine.OpenSubKey(regPath);
           return (string)r.GetValue(key);                        
}
這樣該函式就無法存取 HKLM Software 下的所有 Registry 資訊。
 
限制使用 Reflection
 
  使用 ReflectionPermission 可以控制 Reflection 的使用範圍,如程式 5
程式 5
[System.Security.Permissions.ReflectionPermission(SecurityAction.Deny, Flags = ReflectionPermissionFlag.TypeInformation)]            
private void TestReflectionPermission(object target)
{
           MethodInfo m =
target.GetType().GetMethod("GetHello",
BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.IgnoreCase);
           m.Invoke(target,null);
}
TypeInformation 旗標代表著此函式無法使用 Reflection 來取得、或呼叫非公開函式。
 
限制 Socket 存取
 
   WebPermission 僅能夠管控網站的存取動作,不能管控 FTP SMTP 等存取動作, SocketPermission 可以達到此需求,如程式 6
程式 6
[System.Net.SocketPermission(SecurityAction.PermitOnly, Access = "Connect", Host = "61.219.38.89",Port = "80", Transport = "All")]
private void TestSocket(string ip)
{
           System.Net.Sockets.Socket socket =
new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,                                   System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp);
           socket.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(ip),80));
}
此函式無法存取 61.129.38.89 80 Port 以外的網路。
 
真的安全嗎 ?
 
  在前面提過, CAS 建立在安全的函式庫上,這代表著不安全的函式庫仍然有破壞應用程式的可能性,用程式 2 的例子來看,之所以刪除檔案會被拒絕的原因是 File 物件在刪除檔案前會先要求 FileIOPermission 來確認是否有足夠的權限刪除檔案,那麼假如該函式庫不是使用 File 物件,而是直接以 Unmanaged Code 呼叫 Windows API 來刪除檔案呢?答案是 FileIOPermission 無法防堵此行為,你必須加上另一個權限物件來避免這種情況。
程式 7
[System.Security.Permissions.FileIOPermission(System.Security.Permissions.SecurityAction.PermitOnly, Read = @"D:\Temp")]
[SecurityPermissionAttribute(SecurityAction.Deny, UnmanagedCode = false)]
private void OpenFile()
{
           ClassLibrary6.Class1 c = new ClassLibrary6.Class1();
           c.OpenFile(@"D:\Temp\0050.gif");                           
}
不過這個手法有一個漏洞,那就是當對象 Assembly 套用了 SuppressUnmanagedCodeSecurityAttribute 時,此限制將被忽略,要確實防堵此一漏洞的方式是在AssemblyInfo.cs中套用以下的Attribute。
[assembly: SecurityPermission(SecurityAction.RequestRefuse, UnmanagedCode = true)]      
你可能與我一樣好奇,在這種限制下, .NET Framework 函式庫中的 Unmanaged 呼叫還能正常運作嗎?答案是可以的。
 
Assert
 
  前面曾提過, Assert 可以讓被呼叫者執行呼叫者所沒有的權限,簡單的說就是呼叫者雖然不允許 Unmanaged Code 的執行,但被呼叫者使用了 Assert 來允許 Unmanaged Code 執行,那麼這個動作將會正常的運作,防堵此一漏洞的方法是在呼叫者的 AssemblyInfo.cs 中宣稱此一呼叫者禁止使用 Assert
[assembly: SecurityPermission(SecurityAction.RequestRefuse, UnmanagedCode = true , Assertion = true)]
 
.NET 2.0 Beta 2
 
   .NET 2.0 B2 中新增了幾個 Permissio n ,下表列出她們,日後有機會筆者再詳細介紹她們。
名稱
用途
SqlNotificationPermission
管理使用 Sql Server 2005 所新增的 Notification 功能權限。
SmtpPermission
SMTP 權限控制。
NetworkInformationPermission
管理使用 .NET B2 中新增的 NetworkInformation 中組件的權限。
GacIdentityPermission
Gac 為識別的權限管理。
DataProtectedPermission
使用 DDAPI 的權限。
 
讓安全性成為直覺思考的一部份
 
   不可否認,在程式中加入安全性控制是繁雜的工作,但是在現今的網路環境中,程式設計師實在不能再置身事外了,將安全性觀念加到寫程式的流程中,已經是不能再拖延的工作了。


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1184631


你可能感兴趣的:(framework)