Veröffentlicht: 19. Jan 2006
Von Dr. Holger Schwichtenberg
Sicherheit wird immer wichtiger. In der Version 2.0 der .NET-Klassenbibliothek dürfen daher Neuerungen zur Verbesserung der Sicherheit nicht fehlen. Dr. Holger Schwichtenberg hat den erweiterten Namensraum System.Security für die dotnetpro untersucht.
|
|
Diesen Artikel können Sie dank freundlicher Unterstützung von dotnetpro auf MSDN Online lesen. dotnetpro ist ein Partner von MSDN Online. Ausgabe 11/2005
|
|
Mit der Version 2.0 der .NET-Klassenbibliothek verbessert Microsoft die Sicherheit im Framework und in eigenen Anwendungen. Der Namensraum System.Security enthält nun völlig neue Unternamensräume:
-
den Namensraum System.Security.Cryptography.Pkcs zur Unterstützung des Public-Key Cryptography Standards (PKCS);
-
System.Security.AccessControl zum Auslesen und Setzen von Zugriffsrechtelisten auf Windows-Systembausteine wie Dateien, Ordner, Registry-Einträge und Active-Directory-Objekte;
-
System.Security.Authentication für SSL-Authentifizierung;
-
darüber hinaus Erweiterungen in System.Security, System.Security.Cryptography, System.Security.Cryptography.Xml, System.Security.Permissions und System.Security.Policy.
Auf dieser Seite
Zugriffsrechtelisten
Kontenname und SID
Beispiel: DACL auslesen
Beispiel: DACL setzen
SecureString
Verschlüsselung von XMLDokumenten
Fazit
Der Autor
Zugriffsrechtelisten
Der neue Namensraum System.Security.AccessControl enthält zahlreiche Klassen zur Verwaltung von Zugriffsrechtelisten in Form von Windows-Sicherheitsdeskriptoren (SD). Ein Sicherheitsdeskriptor besteht aus
-
dem Security Identifier (SID),
-
einer Discretionary ACL (DACL), welche die Zugriffsrechte beschreibt,
-
einer System ACL (SACL), welche die Überwachungseinstellungen enthält.
Eine Access Control List (ACL) besteht aus Access Control Entries (ACE). Ein ACE wiederum enthält folgende Informationen:
-
Trustee: der SID des Benutzers beziehungsweise der Gruppe;
-
AceType: Der Typ ist entweder ACCESS_ALLOWED_ACE (0) oder ACCESS_DENIED_ACE (1). ACEs in einer SACL haben immer den Typ SYSTEM_AUDIT_ACE;
-
AccessMask : Die AccessMask definiert die Rechte. Für jeden Objekttyp gibt es unterschiedliche Rechte. Jedes Recht ist dabei ein Bit beziehungsweise eine Kombination von Bits in diesem Long-Wert. Eine AccessMask besteht meist aus der Addition mehrerer Zugriffsrechte;
-
AceFlags : Über die AceFlags wird dieVererbung der Rechte gesteuert.
Für jede Art von Windows-Systembausteinen, die Sicherheitsdeskriptoren besitzen, bietet der Namensraum System.Security.AccessControl eine Klasse an, die von System.Security.AccessControl.ObjectSecurity abgeleitet ist, siehe Abbildung 1. Beispielsweise dient System.Security.AccessControl.FileSecurity dazu, die ACLs einer Datei im Dateisystem zu lesen und zu verarbeiten. Über die gesamte .NET-Klassenbibliothek verteilt findet man Klassen, die eine Methode GetAccessControl() besitzen, die ein von der Klasse ObjectSecurity abgeleitetes Objekt liefert. Beispiele für solche Klassen sind:
Abbildung 1 Vererbungshierarchie der Klassen in System.Security.AccessControl, gezeichnet mit dem Class Designer von Visual Studio 2005.
Kontenname und SID
Der Namensraum System.Security.AccessControl verwendet Klassen aus System.Security.Principal zur Darstellung der Berechtigungsträger, von Benutzern und von Gruppen. System.Security.Principal unterstützt die beiden in Windows bekannten Bezeichner für Berechtigungsträger:
-
Prinzipalname; zum Beispiel "ITVisions\hs", durch die Klasse System.Security.Principal.NTAccount;
-
Security-Identifier, etwa S-1-5-21-565061207-3232948068-1095265983-500, durch die Klasse System.Security.Principal.SecurityIdentifier.
Jeder Benutzer und jede Benutzergruppe besitzt einen so genannten Security Identifier (kurz: SID), der den Benutzer beziehungsweise die Gruppe eindeutig identifiziert. Ein SID ist ein Zahlen-Array variabler Länge. In Textform wird der SID mit einem beginnenden "S" dargestellt. Listing 1 zeigt die Umwandlung eines Prinzipalnamens in einen SID und umgekehrt mithilfe der Methode Translate() in der Klasse IdentityReference, welche die Basisklasse für NTAccount und SecurityIdentifier ist.
Tabelle 1
| Umfang der Neuerungen in System.Security. |
|
Namensraum
|
Klassen in .NET 1.0
|
in .NET 1.1
|
in .NET 2.0 Alpha
|
in .NET 2.0 Beta 2
|
|
System.Security
|
11
|
11
|
13
|
19
|
|
System.Security.Principal
|
0
|
0
|
8
|
47
|
|
System.Security.AccessControl
|
0
|
0
|
21
|
0
|
|
System.Security.AccessControl.Interop
|
0
|
0
|
0
|
2
|
|
System.Security.Authentication
|
57
|
57
|
73
|
79
|
|
System.Security.Cryptography
|
0
|
0
|
26
|
25
|
|
System.Security.Cryptography.Pkcs
|
2
|
2
|
19
|
20
|
|
System.Security.Cryptography.X509Certificates
|
21
|
21
|
38
|
39
|
|
System.Security.Cryptography.Xml
|
36
|
36
|
44
|
48
|
|
System.Security.Permissions
|
25
|
25
|
36
|
35
|
|
System.Security.Policy
|
5
|
5
|
5
|
10
|
In Windows eingebaute Benutzer und Gruppen besitzen einen so genannten Well-Known-Security-Identifier. .NET 2.0 stellt eine Auflistung System.Security.Principal.WellKnownSidType bereit, die man zum Instanzieren der Klasse SecurityIdentifier einsetzen kann. Man umgeht damit die sprachspezifischen Unterschiede des Betriebssystems, etwa Guests/Gäste.
Demo.Print("Administratoren: " +
new System.Security.Principal.
SecurityIdentifier(System.Security.
Principal.WellKnownSidType.
BuiltinAdministratorsSid, null).Value);
Einige eingebaute Benutzer und Gruppen beinhalten den SID der Domäne in ihrem eigenen SID. In diesem Fall muss bei der Instanzierung der Klasse SecurityIdentifier der Domänen-SID mit angegeben werden. Leider schweigt sich die Dokumentation in der Beta 2 darüber aus, woher man den Domain-SID mit .NET-Methoden bekommt. Auch im Internet findet man noch kein Beispiel dafür.
Eine andere Möglichkeit zum Zugriff auf eingebaute Benutzer und Gruppen besteht in der Verwendung der in der Security Descriptor Definition Language (SDDL) definierten Abkürzungen für die eingebauten Benutzer und Gruppen - siehe Tabelle 2.
Demo.Print("Administratoren: " +
new System.Security.Principal.
SecurityIdentifier("BA").Value);
Tabelle 2
| SDDL-Abkürzungen für eingebaute Benutzer und Gruppen. |
|
SDDL- Abkürzung
|
Bedeutung
|
|
"AO"
|
Account operators
|
|
"AN"
|
Anonymous logon
|
|
"AU"
|
Authenticated users
|
|
"BA"
|
Built-in administrators
|
|
"BG"
|
Built-in guests
|
|
"BO"
|
Backup operators
|
|
"BU"
|
Built-in users
|
|
"CA"
|
Certificate server administrators
|
|
"CG"
|
Creator group
|
|
"CO"
|
Creator owner
|
|
"DA"
|
Domain administrators
|
|
"DC"
|
Domain computers
|
|
"DD"
|
Domain controllers
|
|
"DG"
|
Domain guests
|
|
"DU"
|
Domain users
|
|
"EA"
|
Enterprise administrators
|
|
"ED"
|
Enterprise domain controllers
|
|
"WD"
|
Everyone
|
|
"PA"
|
Group Policy administrators
|
|
"IU"
|
Interactively logged-on user
|
|
"LA"
|
Interactively logged-on user
|
|
"LG"
|
Local guest
|
|
"LS"
|
Local service account
|
|
"SY"
|
Local system
|
|
"NU"
|
Network logon user
|
|
"NO"
|
Network configuration operators
|
|
"NS"
|
Network service account
|
|
"PO"
|
Printer operators
|
|
"PS"
|
Personal self
|
|
"PU"
|
Power users
|
|
"RS"
|
RAS servers group
|
|
"RD"
|
Terminal server users
|
|
"RE"
|
Replicator
|
|
"RC"
|
Restricted code
|
|
"SA"
|
Schema administrators
|
|
"SO"
|
Server operators
|
|
"SU"
|
Service logon user
|
Beispiel: DACL auslesen
Listing 2 zeigt die Ausgabe der einzelnen Access Control Entries (ACEs) der AccessControl List einer Datei im Dateisystem. Zunächst wird die Methode GetAccessControl() in der Klasse System.IO.File genutzt, um den kompletten Sicherheitsdeskriptor in Form eines FileSecurity-Objekts zu erhalten.
GetOwner() liefert schließlich den Besitzer der Datei. Als Parameter ist anzugeben, in welcher Form, SecurityIdentifier oder NTAccount, man die Information erhalten möchte.
Demo.Print("Besitzer SID: " +
objFS.GetOwner(typeof(System.Security.
Principal.SecurityIdentifier)).Value);
Demo.Print("Besitzer Name: " +
objFS.GetOwner(typeof(System.Security.
Principal.NTAccount)).Value);
Das FileSecurity-Objekt liefert über GetAccessRules() die einzelnen ACEs der DACL in Form von FileSystemAccessRule-Objekten. GetAuditRules() würde die SACL-ACEs liefern. Auch hierbei ist wieder die gewünschte Form für den Berechtigungsträger anzugeben. Bei GetAccessRules() kann angegeben werden, ob nur die expliziten - erster Boolean-Parameter - und/oder die vererbten ACEs in der Ergebnismenge enthalten sein sollen. Die expliziten ACEs erscheinen immer zuerst in der Liste.
Beispiel: DACL setzen
Listing 3 zeigt das Ergänzen einer ACE zu einer ACL. Wieder erfolgt der Zugriff über die statische Methode GetAccessControl() der File-Klasse. Neue FileSystemAccessRule-Objekte können mithilfe von NTAccount-Objekten oder SecurityIdentifier-Objekten erzeugt werden.
Als weitere Parameter werden die zu vergebenden Rechte, zum Beispiel: FileSystemRights.Read, FileSystemRights.Write, FileSystemRights.Modify, FileSystemRights.FullControl sowie der Berechtigungstyp AccessControlType.Allow oder AccessControlType.Deny bei der Instanzierung genannt.
SecureString
Die Klasse SecureString legt eine Zeichenkette in verschlüsselter Form im Hauptspeicher ab, sodass ein Schutz für sensible Daten, beispielsweise Kennwörter, besteht. Damit wird in .NET 2.0 das Sicherheitsproblem eliminiert, das dadurch entsteht, dass der Entwickler keinen Einfluss darauf hat, wann erzeugte Zeichenketten der Garbage Collection unterworfen und im Speicher überschrieben werden. Angreifer könnten so durch ein Speicherabbild sensible Informationen erhalten.
Neben der verschlüsselten Ablage unterscheidet sich SecureString von System.String dadurch, dass ein einmal gesetzter Wert nur so lange noch verändert werden kann, etwa über AppendChar(), RemoveAt()und SetAt(), bis die Zeichenkette explizit über MakeReadOnly() als nicht mehr veränderbar gekennzeichnet wird. Außerdem lässt sich der Wert mittels Clear() aus dem Hauptspeicher entfernen. SecureString verwendet intern das Data Protection API (DAPI), das die Schlüsselverwaltung übernimmt.
Hauptaufgabe der Klasse SecureString ist die Übergabe der verschlüsselten Kennwortzeichenkette an eine Klasse, die ein Kennwort erwartet.
Listing 5 zeigt die Verwendung von SecureString zum impersonifizierten Start eines Prozesses. Leider sind die Klassen in der .NET Framework-Klassenbibliothek, die einen SecureString akzeptieren, noch rar. Die Rückumwandlung eines SecureString-Objekts in eine normale Zeichenkette ist nicht trivial.
Man benötigt dazu die Klasse System.Runtime.InteropServices.Marshal, siehe Listing 6.
Verschlüsselung von XMLDokumenten
Neu in .NET 2.0 ist auch die Unterstützung für die Verschlüsselung von XML-Dokumenten beziehungsweise Dokumententeilen gemäß dem W3C Encryption Standard. Bisher enthielt der Namensraum System.Security.Cryptography.Xml nur Klassen für das Signieren von XML-Dokumenten. Für die Verschlüsselung hinzugekommen sind jetzt unter anderem EncryptedXml, EncryptedData und CipherData. Als Verschlüsselungsverfahren können alle Klassen aus System.Security.Cryptography eingesetzt werden, zum Beispiel das symmetrische Private-Key-Verfahren DES und das asymmetrische Public-Key-Verfahren RSA.
Die Listings 5 und 6 zeigen die Ver- und Entschlüsselung eines XML-Fragments mit einem privaten Schlüssel - siehe Abbildung 2. Der Schlüssel wird in Listing 5 erzeugt und in einer Textdatei gespeichert.
Abbildung 2XML-Dokumentvor der Verschlüsselung.
Beim Verschlüsseln muss der Entwickler die Methode EncryptXml() in einem EncryptedXml-Objekt mit dem zu verschlüsselnden XML-Element und einer Instanz des dem Schlüssel entsprechenden Verschlüsselungsproviders, hier: TripleDESCryptoServiceProvider, füttern. Zum Einfügen der verschlüsselten Daten in das Dokument wird ein EncryptedData-Objekt benötigt, das wiederum die verschlüsselten Daten über ein CipherData-Objekt erhält. Am Ende wird das ursprüngliche Element durch das verschlüsselte Element ersetzt. Das Zieldokument enthält an Stelle des ursprünglichen Elements ein Element mit Namen <EncryptedData>, wie Abbildung 3 zeigt. Das Entschlüsseln ist etwas einfacher, wie Listing 6 zeigt: Hier lädt man das <EncryptedData>- Element in eine Instanz der Klasse EncryptedData. Zusammen mit dem Verschlüsselungsprovider ist das EncryptedData-Objekt Parameter für die Methode DecryptData in einem EncryptedXml-Objekt. Zuletzt wird <Encrypted-Data> durch das ursprüngliche Element, hier <Zugangsdaten>, ersetzt.
Abbildung 3 XML-Dokument nach der Verschlüsselung.
Fazit
Die neuen Sicherheitsfunktionen in .NET 2.0 bieten sinnvolle Ergänzungen. Andere Teile der Klassenbibliothek, zum Beispiel verschlüsselte Sektionen in XMLKonfigurationsdateien (siehe [2]), bauen bereits auf den neuen Sicherheitsfunktionen auf. Die verschlüsselten Zeichenketten werden allerdings von Microsoft bisher viel zu wenig als Eingabeparameter in anderen Klassen, etwa ADO.NET-Verbindungsobjekten, verwendet.
[1] XML Encryption Standard des W3C, www.w3.org/TR/2002/REC-xmlenc-core-20021210/
[2] Holger Schwichtenberg, Sargnägel für die Registry, .NET 2.0: System.Configuration und System.Console, dotnetpro 9/2005, Seite 140 ff.
[3] .NET-Klassenreferenz, www.dotnetframework.de/go.aspx?clref
Der Autor
Dr. Holger Schwichtenberg ist freiberuflicher Softwarearchitekt, Autor und Sprecher auf Veranstaltungen wie TechEd, OOP, MS IT Forum, BASTA!, STC, VS 2005 und iX-Konferenz. Er bietet Beratung und Schulungen zu allen Bereichen von .NET 1.x und .NET 2.0 an. Sie erreichen ihn unter hs@IT-Visions.de.