In dem ersten Teil der Dokumentation haben wir uns mit den Grundlagen des Security Konzeptes des .NET Frameworks beschäftigt und grundlegende Begriffe wie "principal", "caller", "AppDomain", "persmissions" und "role-based security" eruiert.
Im heutigen Teil wollen wir das Beispiel vortfahren und uns um ein neues Feature in der Microsoft Welt kümmern, der Code Access Security. Die Code Acces Security ist ein neues Verfahren in dem dem Entwickler und dem späterem Netzwerkadministrator, viele Möglichkeiten eröffnet werden, Sicherheitsmechanismen in deren Enterprise Umgebung zu implementieren.
Code Access Security
Generell kann man sich unter "code access security" folgendes vorstellen: "Code access security ist ein Mechanismus um den Zugriff den ein QuellCode auf geschütze Bereiche (oder Operationen) hat oder haben kann, zu kontrollieren." Code Access Security im .net Framework bietet folgende Features:
- Definieren von "permissions" und "permission sets", die die Rechte für verschiedene System Ressourcen darstellen.
- Einfache administration durch den Administrator, indem "permission sets" auf "code groups" gemappt werden.
- Code kann die "permissions" abfragen die er benötigt um auf der Maschine ausgeführt werden zu können. (auch solche die nützlich währen)
- Erteilen von "permissions" für jedes .net Assembly das geladen wird, basierend auf den "permissions", welche durch "security policies" eingestellt werden, die der Code hat und auf den Rechten die die Operation hat.
- Ermöglicht QuellCode Rechte zu "demanden" (erzwingen) welche ein spezifischer "caller" hat.
- Ermöglicht QuellCode Digitale Signaturen für den "caller" Prozess zu erzwingen, damit nur spezielle "caller" von bestimmten Organisationen den geschützten Code ausführen kann.
- Ermöglicht Restriktionen auf den Code während der Ausführung zu setzen, indem die "permissions" die ein "caller" hat mit den "permissions" verglichen werden, die ein "caller" haben muss.
Die Hauptfunktionalität der "code access security" (überprüfen, was der Code darf), wird durch den sogenannten "Security stack walk" ausgeführt. Dabei wird überprüft ob ein "caller" die "permissions" hat um den Code mit seinen Anforderungen durch "permissions" ausführen zu können. Hat der "caller" eine bestimmte "permission" nicht die von dem Code erwartet und dem "Call Stack" erwartet wird, wird eine "security exception" ausgelöst. Hierbei ist eine saubere Fehlerbehandlung umumgänglich!
In dem späteren Sample werde ich dies nicht implementieren, da Sie durch dieses Beispiel sehen sollen wie "code access security" funktioniert und wie Sie Rechte überschreiben können. Jede Anwendung die für die Common Language Runtime (CLR) bestimmt ist, muss mit der in der CLR inkludierten "security engine" interagieren. Wenn eine Anwendung ausgeführt wird, evaluiert die CLR ein "set of permission". Abhängig von den "permissions" läuft die Anwendung korrekt durch oder es wird ein "security exception" Fehler ausgelöst. Die lokalen Sicherheitsrichtlinien auf einem einzelnem Computer sind dabei von grosser Bedeutung. Abhängig davon wird von der CLR entschieden welche Rechte ein Programm erhalten kann. Das ist der Einstiegspunkt für die armen Administratorenm ;-) (Enterprise-, Computer-, Assembly- Policies, Code Groups). Da diese Grundeinstellungen auf jedem Computer anders sein können, können Sie nicht davon ausgehen, das Ihr QuellCode immer die benötigten Rechte hat um korrekt ausgeführt werden zu können! Das ist im Gegensatz zur WinDNA Entwicklung grundlegend anders.
Um sicheren QuellCode zu entwickeln sind folgende drei Punkte umumgänglich zu implemntieren:
- Typensicheren QuellCode zu entwickeln. Nach dem Common Type System (CTS)
- Imperative oder Deklarative Syntax zu implementieren (ich hoffe Sie erinnern sich noch!)
- "permission" für Ihren Code abzufragen. Damit wird er Runtime bekannt gemacht welche Rechte Ihr Code benötigt um ausgeführt werden zu können. Dabei ist wichtig zu verstehen, das die CLR dem Code nicht mehr Rechte geben kann als durch die Grundeinstellung der CLR möglich ist.
Abfragen von "permission"
Dabei handelt es sich um den Vorgang, um der CLR mitzuteilen was Ihr QuellCode benötigt um seine Aufgaben durchführen zu können. Sie legen also fest, welche "permissions" Ihre Anwendung benötigt und welche sie von vornherein verweigern wollen. Ein Beispiel ist hier das Recht um auf ein bestimmtes Verzeichnis zuzugreifen und veränderungen darin zu machen. Um eine "saubere" Anwendunge zu liefern müssen Sie sich natürlich im vornherein klar darüber sein was Ihre Anwendung alles machen wird! Das Abfragen von "permissions" wird auf Assembly Level und deklarativ durch Attribute gemacht. Wenn Sie das Assembly erzeugen werden die "permission requests" im Assembly Manifest gespeichert. Bei der Ausführung eines solchen Assemblies wird durch die CLR das Assembly Manifest ausgelesen und fügt "security policies rules" hinzu um entscheiden zu können welche "permissions" dem Assembly erteilt werden können.
Folgende wichtigen "permission requests" kurz in einer Übersicht:
- RequestMinimum: "permissions" die Ihr QuellCode haben muss, um korrekt ausgeführt werden zu können.
- RequestOptional: "permissions" die Ihr QuellCode benutzen kann, ohne die er aber troztdem "lebensfähig" (lauffähig) ist.
- RequestRefuse: "permissions" die niemals Ihrem QuellCode zugeteilt werden dürfen, sogar dann wenn eine "security policy" dies Recht erlauben würde.
Sample:
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "FullTrust")]
Hierbei wird im Assembly eingestellt das zur Ausführung dieses Assemblies die "permissions" aus dem "permission set: 'FullTrust'" benötigt werden.
Für unsere Admins:
Wie bereits erwähnt zeichnet sich das .net Framework und die Code-Access Security dadurch aus, dass Assemblies durch einen Adminisrtator eingestellt werden können und das Netzwerk weit. Dazu liefert Microsoft ein Tool namens .NET Admin Tool mit. Das Tool für die Admins und Coder liegt unter X:WINNTMicrosoft.NETFrameworkv1.0.xxxxmscorcfg.msc und sieht so aus:
Abbildung 1: Das .NET Framework Admin Tool. Schön zu erkennen sind die "Permission Sets" die auf verschiedenen Level angewendet werden können
Abbildung 2: Anlegen eines neuen "permission sets" namens TobiTest
Abbildung 3: Festsetzen der einzelnen "permissions"
Sample für Permissions auf QuellCode Ebene (Methoden)
ACHTUNG ich benutze den QuellCode von Security in .NET Teil 1!!! Ich erweitere das Sample und rufe in dem Event Form1_Load() eine Methode namens MyFunction() auf die das TextFeld in der Form ausfüllt. In dem QuellCode zu der Methode MyFunction() sehen Sie, dass ich zwei "permissions" für die Methode festgelegt habe: PrincipalPermission und FileIOPermission. Wie Sie sehen haben wir Entwicklier die Möglichkeit auf Methoden Ebene "permissions" zu "granten". Das schliesst ein, das wir bestimmte "permissions" erwarten ...SecurityAction.Demand, ... oder das wir "permissions" verwehren können ... SecurityAction.Deny, ...
Was aber an der Stelle noch viel heftiger ist, ist das wir bestimmte "permissions" überschreiben können! Achtung, das ist natürlich ziemlich gefährlich, und nachdem ich Sie darauf hingewiesen haben (you != "Denn sie wissen nicht was sie tun!") , bleibt es Ihnen überlassen wann Sie so etwas einsetzen. Nun zum QuellCode:
private void Form1_Load(object sender, System.EventArgs e){ textBox1.Text = MyFunction().ToString(); } [PrincipalPermission(SecurityAction.Demand, Role=@"TobiTest")] [FileIOPermission(SecurityAction.Deny, All=@"D:SecTestLog.txt")] private string MyFunction() { /*StreamWriter TextStream = new StreamWriter(@"D:SecTestLog.txt"); TextStream.WriteLine("Dieses TextDokument dürfte nicht erzeugtv werden: {0}", DateTime.Now); TextStream.Close();*/ MakeLog(); return "Deklarativer Check für die Rolle TobiTest wahr erfolgreich. Diese Funktion kann von Ihnen ausgeführt werden!"; } [FileIOPermission(SecurityAction.Assert, All=@"D:SecTestLog.txt")] //[FileIOPermission(SecurityAction.Demand, All=@"D:SecTestLog.txt")] public void MakeLog() { StreamWriter TextStream = new StreamWriter(@"D:SecTestLog.txt"); TextStream.WriteLine("Dieses Log File wurde erzeugt am: {0}", DateTime.Now); TextStream.Close(); } |
...
private
void Form1_Load(
object sender, System.EventArgs e)
{
textBox1.Text = MyFunction().ToString();
}
[PrincipalPermission(SecurityAction.Demand, Role=@"TobiTest")]
[FileIOPermission(SecurityAction.Deny, All=@"D:SecTestLog.txt")]
private string MyFunction()
{
/*StreamWriter TextStream = new StreamWriter(@"D:SecTestLog.txt");
TextStream.WriteLine("Dieses TextDokument dürfte nicht erzeugtv werden: {0}", DateTime.Now);
TextStream.Close();*/
MakeLog();
return "Deklarativer Check für die Rolle TobiTest wahr erfolgreich. Diese Funktion kann von Ihnen ausgeführt werden!";
}
[PrincipalPermission(SecurityAction.Demand, Role=@"TobiTest")]
Ich zwinge die Runtime den QuellCode nur dann auszuführen wenn der User in der Rolle "TobiTest" ist. (wie Role-based Security funktioniert? Lesen Sie bitte den ersten Teil dieses Tutorials)
Als nächstes setze ich die FileIOPermission:
[FileIOPermission(SecurityAction.Deny, All=@"D:SecTestLog.txt")]
Damit gebe ich der Methode die Eigenschaft mit, dass sie nicht in das oben angegebene Verzeichnis schreiben darf (und natürlich auch nicht die TextDatei)
In Methoden Scope sehen Sie, dass ich ein bischen QuellCode auskommentiert habe. Warum? Das ist der ultimative Beweis dafür das die FileIOPermission auch wirksam ist. Entfernen Sie einfach die Kommentarzeichen und Sie bekommen eine SecurityException.
Als dann ruft die Methode eine weitere Method namens MakeLog() auf. In dieser Methode erzeuge ich eine LogDatei in dem Verzeichnis D:SecTest. Wenn jetzt der QuellCode ausgeführt würde, würde natürlich eine FileIOPermission Exception auftreten da der "Caller" ausgehend von der Methode MyFunction() keine Rechte dazu hat. Um dies zu umgehen benutze ich die Methode Assert um der Methode MakeLog() die Rechte auf dieses Verzeichnis wieder zu gewähren und das LogFile erzeugen zu können:
[FileIOPermission(SecurityAction.Assert, All=@"D:SecTestLog.txt")]
//[FileIOPermission(SecurityAction.Demand, All=@"D:SecTestLog.txt")]
public void MakeLog()
{
StreamWriter TextStream = new StreamWriter(@"D:SecTestLog.txt");
TextStream.WriteLine("Dieses Log File wurde erzeugt am: {0}", DateTime.Now);
TextStream.Close();
}
Zum Schluss kann man sagen dass die neuen Security Features multiple Möglichkeiten bieten um aktuelle Sicherheitsaspekte in Firmen bestmöglich durchzusetzen. Microsoft hat das Thema Security schon seit längerer Zeit im Blickfeld und wird in Zukunft einige neue Produkte anbieten wie den ISA Server der in Verbindung mit dem IIS 6.0 weitere neue Features bieten wird.