Share via


ASP.NET-Sicherheit

Schutz vor Hacking für Ihre ASP.NET-Anwendungen

Adam Tuliper

Nahezu täglich wird in den populären Medien von neuen Hackingangriffen auf Websites berichtet. Angesichts der konstanten Angriffe durch prominente Hackergruppen fragen sich Entwickler, ob diese Gruppen erweiterte Techniken für ihre kriminellen Tätigkeiten verwenden. Obwohl einige der modernen Angriffe ziemlich komplex sein können, sind die effektivsten Angriffe häufig bemerkenswert simpel – und werden seit Jahren verwendet. Glücklicherweise ist es gewöhnlich überraschend einfach, sich vor diesen Angriffen zu schützen.

Ich werde einige der häufigsten Typen von Hackerangriffen in zwei Artikeln betrachten. In diesem ersten Artikel behandele ich die SQL-Einschleusung und die Manipulierung von Parametern. Im zweiten Teil, der in der Januarausgabe erscheinen wird, lege ich den Schwerpunkt auf siteübergreifende Skripts und die Fälschung von siteübergreifenden Anforderungen.

Falls Sie sich fragen, ob nur große Sites Hackerangriffen ausgesetzt sind, dann ist die Antwort einfach: Alle Entwickler müssen sich in ihren Anwendungen Gedanken in Bezug auf Hackerangriffe machen. Es ist Ihre Aufgabe, die Anwendungen zu schützen. Die Benutzer erwarten dies von Ihnen. Auch kleine Anwendungen im Internet sind Angriffsversuchen ausgesetzt, da eine große Zahl automatischer Hackingprogramme verfügbar ist. Nehmen Sie an, dass die Benutzer- oder Kundentabelle gestohlen wurde, und dass die in diesen Tabellen enthaltenen Kennwörter auch für andere Anwendungen verwendet werden. Natürlich wird empfohlen, dass Benutzer stets verschiedene Kennwörter verwenden. In der Praxis tun sie dies jedoch nicht. Sie möchten sicher nicht, dass Sie die Benutzer darüber informieren müssen, dass Ihre Anwendung den Diebstahl von Daten ermöglicht hat. Der kleine Blog eines meiner Freunde, in dem er seine Reise zum Mount Everest beschrieben hatte, wurde gehackt. Alles wurde gelöscht, und das ohne erkennbaren Grund. Ohne Schutzmaßnahmen ist praktisch keine Anwendung sicher.

Außer in den Fällen, in denen ein Netzwerk physisch von allen externen Kommunikationsgeräten getrennt ist, besteht stets die Gefahr, dass externe Personen in das Netzwerk eindringen: aufgrund von Fehlern bei der Proxykonfigurierung; über Remote Desktop Protocol (RDP)- oder Virtual Private Network (VPN)-Angriffe; mittels Remoteausführung von Code, der von einem internen Benutzer durch den einfachen Besuch einer Webseite ausgeführt wird; mithilfe vermuteter Kennwörter; aufgrund unzureichender Firewallregeln; über Wi-Fi (der Schutz von WiFi-Netzwerken kann in den meisten Fällen von Angreifern umgangen werden, die mit ihrem Auto auf dem Parkplatz Ihres Unternehmens parken); mithilfe von Social-Engineering-Tricks, die Benutzer veranlassen, sensible Daten mitzuteilen; und über andere Eingangspunkte. Wenn eine Umgebung nicht vollständig von der Außenwelt getrennt ist, sollte niemals davon ausgegangen werden, dass sie absolut sicher ist.

Da ich Ihnen nun ausreichend Angst eingejagt habe (jedenfalls hoffe ich das), und Sie auch der Ansicht sind, dass die Bedrohung durch Hackerangriffe sehr real ist und alle Ihre Anwendungen dieser Gefahr ausgesetzt sind, sollten wir damit beginnen, die Angriffe besser zu verstehen und darüber sprechen, wie Sie diese verhindern können!

SQL-Einschleusung

Worum handelt es sich? Die SQL-Einschleusung ist ein Angriff, bei dem ein oder mehrere Befehle in eine Abfrage eingeschleust werden, um eine neue Abfrage zu erstellen, die der Entwickler nie beabsichtigt hat. Dies erfolgt fast immer, wenn dynamisches SQL verwendet wird, d. h., wenn Sie Zeichenfolgen im Code verketten, um SQL-Anweisungen zu bilden. Die SQL-Einschleusung kann im Microsoft .NET Framework-Code vorkommen, wenn Sie eine Abfrage oder einen Prozeduraufruf bilden. Sie kann auch im serverseitigen T-SQL-Code vorkommen, wie z. B. im Fall von dynamischem SQL in gespeicherten Prozeduren.

Die SQL-Einschleusung ist besonders gefährlich, da sie nicht nur für Abfragen und das Bearbeiten von Daten verwendet werden kann. sondern auch zum Ausführen von Datenbankbefehlen, die nur nach Datenbankbenutzern oder nach Berechtigungen des Datenbankdienstkontos begrenzt sind. Wenn Ihr SQL-Server für die Ausführung als Administratorkonto konfiguriert ist, und Ihre Anwendung der Systemadministratorrolle zugewiesen ist, sollten Sie besonders besorgt sein. Angriffe mittels SQL-Einschleusung können Systembefehle ausführen, die folgende Aktionen durchführen:

  • Installieren von Hintertüren
  • Übertragen einer vollständigen Datenbank über Port 80
  • Installieren von Netzwerkschnüfflern, um Kennwörter und andere sensible Daten zu stehlen
  • Stehlen von Kennwörtern
  • Enumerieren des internen Netzwerks, einschließlich des Scannens der Ports anderer Computer
  • Herunterladen von Dateien
  • Ausführen von Programmen
  • Löschen von Dateien
  • Integrieren in ein Botnet
  • Abfragen zur automatischen Vervollständigung von Kennwörtern, die im System gespeichert wurden
  • Erstellen neuer Benutzer
  • Erstellen, Löschen und Bearbeiten von Daten; Erstellen und Ablegen von Tabellen

Diese Liste ist nicht vollständig, und die Gefahr wird nur durch die vorhandenen Berechtigungen eingegrenzt – und die Kreativität des Angreifers. 

Die SQL-Einschleusung ist schon so lange ein Thema, dass ich häufig gefragt werde, ob sie immer noch ein Problem ist. Die Antwort ist "aber sicher", und Angreifer verwenden diese Methode sehr häufig. Nach Denial of Service (DoS)-Angriffen stellt die SQL-Einschleusung die am häufigsten verwendete Angriffsmethode dar.

Wie wird sie genutzt? Die SQL-Einschleusung wird im allgemeinen durch Eindringen über eine Webseite oder mittels der Manipulierung von Parametern genutzt. Dies beinhaltet in der Regel nicht nur Formulare oder URIs – Cookies, Header usw. sind ebenfalls Angriffen ausgesetzt, wenn die Anwendung diese Werte in einer nicht sicheren SQL-Anweisung verwendet (ich werde dies an späterer Stelle in diesem Artikel behandeln).

Schauen wir uns ein Beispiel für eine SQL-Einschleusung mittels der Manipulierung eines Formulars an. Dies ist ein Szenario, das ich häufig in Produktionscode angetroffen habe. Ihr Code ist vielleicht nicht exakt gleich. Entwickler verwenden diese Möglichkeit jedoch häufig, um die Anmeldeinformationen zu prüfen.

Dies ist die dynamische SQL-Anweisung für den Abruf der Anmeldedaten des Benutzers:

string loginSql = string.Format("select * from users where loginid= '{0}

  ' and password= '{1} '"", txtLoginId.Text, txtPassword.Text);

Dies bildet die SQL-Anweisung:

    select * from dbo.users where loginid='Administrator' and  
    
        password='12345'

An und für sich ist dies kein Problem. Nehmen Sie jedoch an, die Feldeingabe im Formular sieht aus wie diejenige, die in Abbildung 1 gezeigt wird.

Malicious Input Instead of a Valid Username
Abbildung 1 Bösartige Eingabe anstelle eines gültigen Benutzernamens

Diese Eingabe bildet diese SQL-Anweisung:

    select * from dbo.users where loginid='anything' union select top 1 *
    
      from users --' and password='12345'

In diesem Beispiel wird die unbekannte Anmelde-ID "anything" (alles) eingeschleust, die an und für sich keine Datensätze zurückgeben würde. Diese Ergebnisse werden in der Folge jedoch mit dem ersten Datensatz in der Datenbank kombiniert, während "--" den gesamten Rest der Abfrage auskommentiert, sodass dieser ignoriert wird. Der Angreifer kann sich nun nicht nur anmelden, er kann auch den Datensatz eines gültigen Benutzers an den aufrufenden Code zurückgeben, ohne dass er einen gültigen Benutzernamen kennt.

Ihr Code entspricht möglicherweise diesem Szenario nicht genau. Es ist jedoch wichtig, sich bei der Analyse Ihrer Anwendungen zu überlegen, woher die Werte im Allgemeinen stammen, die in Abfragen eingeschlossen werden, darunter:

  • Formularfelder
  • URL-Parameter
  • In der Datenbank gespeicherte Werte
  • Cookies
  • Header
  • Dateien
  • Isolierter Speicher

Nicht alle dieser Quellen sind möglicherweise sofort ersichtlich. Warum sind beispielsweise Header ein mögliches Problem? Wenn Ihre Anwendung Daten von Benutzerprofilen in Headern speichert, und diese Werte in dynamischen Abfragen verwendet werden, dann könnten Sie Angriffen ausgesetzt sein. Dies alles können Quellen für Angriffe sein, wenn Sie dynamisches SQL verwenden.

Webseiten, die Suchfunktionalität enthalten, können eine sehr leichte Beute für Angreifer darstellen, da sie eine direkte Möglichkeit bereitstellen, Einschleusungen zu versuchen.

Die Suchfunktionalität kann einem Angreifer in einer anfälligen Anwendung beinahe die Funktionalität eines Abfrage-Editors bereitstellen.

Die Menschen sind sich in den letzten Jahren der Sicherheitsprobleme stärker bewusst geworden, sodass die Systeme im Allgemeinen von vornherein sicherer sind. Beispielsweise wurde die Systemprozedur xp_cmdshell in SQL Server 2005 und höher deaktiviert (einschließlich SQL Express). Sie sollten jetzt jedoch nicht glauben, dass Angreifer keine Befehle mehr auf Ihrem Server ausführen können. Wenn das Konto, das Ihre Anwendung für die Datenbank verwendet, über ausreichende Berechtigungen verfügt, kann ein Angreifer einfach den folgenden Befehl einschleusen, um die Option wieder zu aktivieren:

    EXECUTE SP_CONFIGURE 'xp_cmdshell', '1'

Wie verhindern Sie die SQL-Einschleusung? Besprechen wir zunächst, wie Sie dieses Problem nicht beheben sollten. Ein häufiger Ansatz für das Beheben von Fehlern in klassischen ASP-Anwendungen besteht im Ersetzen von Bindestrichen und Anführungszeichen. Leider wird dieser Ansatz immer noch häufig für .NET-Anwendungen verwendet, häufig als einziger Schutzmechanismus:

string safeSql = "select * from users where loginId = " + userInput.Replace("—-", "");

safeSql = safeSql.Replace("'","''");

safeSql = safeSql.Replace("%","");

Dieser Ansatz setzt Folgendes voraus:

  1. Sie haben jede einzelne Abfrage korrekt mit diesen Aufruftypen geschützt. Er ist davon abhängig, dass der Entwickler daran denkt, diese Inlineprüfungen überall einzuschließen, anstatt ein Muster zu verwenden, das diesen Schutz per Voreinstellung bietet – auch, wenn der Entwickler das ganze Wochenende mit Programmieren verbracht hat und ihm der Kaffee ausgeht.
  2. Sie haben den Typ jedes einzelnen Parameters geprüft. Es ist in der Regel nur eine Frage der Zeit, bis der Entwickler beispielsweise vergisst zu prüfen, ob es sich beim Parameter einer Webseite wirklich um eine Zahl handelt, und anschließend diese Zahl in einer Abfrage für z. B. eine Produkt-ID verwendet, ohne die Zeichenfolge zu prüfen, da es sich ja schließlich um eine Zahl handelt. Was passiert jedoch, wenn ein Angreifer die Produkt-ID ändert und die Abfragezeichenfolge anschließend wie hier gezeigt gelesen wird:
URI: http://yoursite/product.aspx?productId=10

Ergebnisse

    select * from products where productid=10

Anschließend schleust der Angreifer Befehle wie die folgenden ein:

URI: http://yoursite/product.aspx?productId=10;select 1 col1 into #temp; drop table #temp;

Welche Ergebnisse

    select * from products where productid=10;select 1 col1 into #temp; drop table #temp;

Eine Katastrophe! Sie haben gerade eine Einschleusung in einem Integer-Feld durchgeführt, die weder mittels einer Zeichenfolgenfunktion gefiltert noch auf den Typ geprüft wurde. Dies wird ein direkter Einschleusungsangriff genannt, da keine Anführungszeichen erforderlich sind und der eingeschleuste Teil direkt in der Abfrage ohne Anführungszeichen verwendet wird. Sie können nun sagen "Ich stelle stets sicher, dass alle meine Daten geprüft werden". Dadurch wird der Entwickler jedoch dafür verantwortlich, jeden einzelnen Parameter manuell zu prüfen. Dies ist sehr fehleranfällig. Warum beheben Sie dies nicht auf die korrekte Weise, indem Sie ein besseres Muster für die gesamte Anwendung verwenden?

Wie sieht also der korrekte Weg aus, um die SQL-Einschleusung zu verhindern? Dies ist für die meisten Datenzugriffsszenarien ziemlich einfach. Der Schlüssel besteht in der Verwendung von parametrisierten Aufrufen. Sie können dynamisches SQL sicher verwenden, wenn Sie diese Aufrufe parametrisieren. Dies sind die Grundregeln:

  1. Stellen Sie sicher, dass Sie nur Folgendes verwenden:
    • Gespeicherte Prozeduren (ohne dynamisches SQL)
    • Parametrisierte Abfragen (siehe Abbildung 2)

Abbildung 2 Parametrisierte Abfrage

using (SqlConnection connection = new SqlConnection(  ConfigurationManager.ConnectionStrings[1].ConnectionString))

{

  using (SqlDataAdapter adapter = new SqlDataAdapter())

  {

    // Note we use a dynamic 'like' clause

    string query = @"Select Name, Description, Keywords From Product

                   Where Name Like '%' + @ProductName + '%'

                   Order By Name Asc";

    using (SqlCommand command = new SqlCommand(query, connection))

    {

      command.Parameters.Add(new SqlParameter("@ProductName", searchText));

      // Get data

      DataSet dataSet = new DataSet();

      adapter.SelectCommand = command;

      adapter.Fill(dataSet, "ProductResults");

      // Populate the datagrid

      productResults.DataSource = dataSet.Tables[0];

      productResults.DataBind();

    }

  }

}
  • Parametrisierte Aufrufe gespeicherter Prozeduren (siehe Abbildung 3)

Abbildung 3 Parametrisierter Aufruf gespeicherter Prozeduren

//Example Parameterized Stored Procedure Call

string searchText = txtSearch.Text.Trim();

using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ConnectionString))

{

  using (SqlDataAdapter adapter = new SqlDataAdapter())

  {

    // Note: you do NOT use a query like: 

    // string query = "dbo.Proc_SearchProduct" + productName + ")";

    // Keep this parameterized and use CommandType.StoredProcedure!!

    string query = "dbo.Proc_SearchProduct";

    Trace.Write(string.Format("Query is: {0}", query));

    using (SqlCommand command = new SqlCommand(query, connection))

    {

      command.Parameters.Add(new SqlParameter("@ProductName", searchText));

      command.CommandType = CommandType.StoredProcedure;

      // Get the data.

      DataSet products = new DataSet();

      adapter.SelectCommand = command;

      adapter.Fill(products, "ProductResults");

      // Populate the datagrid.

      productResults.DataSource = products.Tables[0];

      productResults.DataBind();

    }

  }

}
  1.  2.   Dynamisches SQL in gespeicherten Prozeduren sollte parametrisierte Aufrufe von sp_executesql verwenden. Vermeiden Sie die Verwendung von exec, da es parametrisierte Aufrufe nicht unterstützt. Vermeiden Sie die Verkettung von Zeichenfolgen mit Benutzereingaben. siehe Abbildung 4.

Abbildung 4 Parametrisierter Aufruf von sp_executesql

    /*
    
    This is a demo of using dynamic sql, but using a safe parameterized query
    
    */
    
    DECLARE @name varchar(20)
    
    DECLARE @sql nvarchar(500)
    
    DECLARE @parameter nvarchar(500)
    
    /* Build the SQL string one time.*/
    
    SET @sql= N'SELECT * FROM Customer  WHERE FirstName Like @Name Or LastName Like @Name +''%''';
    
    SET @parameter= N'@Name varchar(20)';
    
    /* Execute the string with the first parameter value. */
    
    SET @name = 'm%'; --ex. mary, m%, etc. note: -- does nothing as we would hope!
    
    EXECUTE sp_executesql @sql, @parameter,
    
                          @Name = @name;

Beachten Sie, dass Sie aufgrund der fehlenden Unterstützung für Parameter Folgendes NICHT verwenden dürfen: exec 'select .. ' + @sql

  1.  3.   Ersetzen Sie nicht einfach Bindestriche und Anführungszeichen in dem Glauben, Sie seien dann sicher. Wählen Sie konsistente Methoden für den Datenzugriff, wie bereits beschrieben, die SQL-Einschleusungen ohne manuelle Eingriffe des Entwicklers verhindern, und verwenden Sie diese durchgehend. Wenn Sie sich auf eine Escape-Routine verlassen und vergessen, diese an einer Stelle aufzurufen, sind Sie angreifbar. Darüber hinaus kann die Art der Implementierung der Escape-Routine Sie bereits angreifbar machen, wie z. B. im Fall von SQL-Kürzungsangriffen.
  2.  4.   Validieren Sie die Eingabe (wie im folgenden Abschnitt zur Manipulierung von Parametern beschrieben) mittels Typprüfungen und Casting. Grenzen Sie die Eingabe auf reguläre Ausdrücke ein, wie ausschließlich alphanumerische Daten, oder rufen Sie wichtige Daten aus bekannten Quellen ab. Vertrauen Sie nicht den Daten, die von der Webseite stammen.
  3.  5.   Überprüfen Sie die Berechtigungen für die Datenbankobjekte, um die Berechtigungen für die Anwendungsbenutzer einzuschränken und so weniger Angriffsfläche zu bieten. Gewähren Sie Berechtigungen für Aktualisieren, Löschen und Einfügen nur dann, wenn der betreffende Benutzer in der Lage sein muss, diese Vorgänge durchzuführen. Jede Anwendung sollte über eine eigene Anmeldung für die Datenbank mit eingeschränkten Rechten verfügen. Mein Open-Source-basierter SQL Server Permissions Auditor kann Sie dabei unterstützen. Informieren Sie sich unter sqlpermissionsaudit.codeplex.com.

Es ist sehr wichtig, die Tabellenberechtigungen zu überprüfen, wenn Sie parametrisierte Abfragen verwenden. Parametrisierte Abfragen erfordern, dass ein Benutzer oder eine Rolle über Berechtigungen für den Zugriff auf eine Tabelle verfügen. Ihre Anwendung ist möglicherweise zwar vor SQL-Einschleusungen geschützt. Was passiert jedoch, wenn eine andere Anwendung, die nicht geschützt ist, mit Ihrer Datenbank in Berührung kommt? Ein Angreifer könnte Ihre Datenbank dann abfragen. Daher sollten Sie sicherstellen, dass jede Anwendung über eine eigene, eingeschränkte Anmeldung verfügt. Sie sollten auch die Berechtigungen für Datenbankobjekte wie Ansichten, Prozeduren und Tabellen überprüfen. Gespeicherte Prozeduren erfordern nur Berechtigungen für die Prozedur selbst, nicht für die Tabelle. Dies gilt im Allgemeinen solange, wie kein dynamisches SQL innerhalb der gespeicherten Prozedur vorhanden ist. Die Sicherheit kann für diese daher etwas leichter verwaltet werden. Auch hierbei kann Sie mein SQL Server Permissions Auditor unterstützen.

Beachten Sie, dass das Entity Framework parametrisierte Abfragen im Hintergrund verwendet und daher in normalen Verwendungsszenarien vor SQL-Einschleusungen geschützt ist. Einige Entwickler bevorzugen es, Entitäten gespeicherten Prozeduren zuzuordnen, anstatt Tabellenberechtigungen für die dynamischen parametrisierten Abfragen zu erstellen. Für beide Ansätze gibt es gute Argumente. Die Entscheidung liegt bei Ihnen. Beachten Sie, dass Sie einige zusätzliche Sicherheitsmaßnahmen für Ihre Abfragen beachten sollten, wenn Sie ausdrücklich Entity SQL verwenden. Informationen hierzu finden Sie auf der MSDN Library-Seite "Security Considerations (Entity Framework)" (Sicherheitsmaßnahmen (Entity Framework)) unter msdn.microsoft.com/library/cc716760.

Manipulieren von Parametern

Worum handelt es sich? Beim Manipulieren von Parametern handelt es sich um einen Angriff, bei dem Parameter geändert werden, um die erwartete Funktionalität der Anwendung zu ändern. Die Parameter können sich in einem Formular, in einer Abfragezeichenfolge, in Cookies, in einer Datenbank usw. befinden. Ich bespreche hier Angriffe mit webbasierten Parametern.

Wie wird dies ausgenutzt? Der Angreifer ändert die Parameter, um die Anwendung dazu zu bringen, eine nicht beabsichtigte Aktion auszuführen. Nehmen Sie an, Sie speichern den Datensatz eines Benutzers, indem Sie die Benutzer-ID aus der Abfragezeichenfolge auslesen. Ist dies sicher? Nein. Ein Angreifer kann eine URL in Ihrer Anwendung ähnlich wie in Abbildung 5 gezeigt manipulieren.

An Altered URL
Abbildung 5 Eine geänderte URL

Dadurch kann der Angreifer ein Benutzerkonto laden, das nicht beabsichtigt ist. Und allzu häufig ist es der Fall, dass Anwendungscode wie der folgende dieser Benutzer-ID blindlings vertraut:

// Bad practice!

string userId = Request.QueryString["userId"];

// Load user based on this ID

var user = LoadUser(userId);

Gibt es eine bessere Möglichkeit? Ja! Sie können die Werte von einer vertrauenswürdigeren Quelle auslesen, wie z. B. der Sitzung des Benutzers oder einem Mitgliedschafts- oder Profilanbieter, anstatt dem Formular zu vertrauen. 

Es gibt verschiedene Tools, die die Manipulierung weiterer Komponenten außer lediglich der Abfragezeichenfolge einfach machen. Ich empfehle Ihnen, einige der verfügbaren Entwicklungssymbolleisten für Webbrowser zu verwenden, um die ausgeblendeten Elemente auf Ihren Seiten anzuzeigen. Ich glaube, Sie werden überrascht sein, was Sie alles entdecken, und wie einfach Ihre Daten manipuliert werden können. Betrachten Sie die Seite für die Bearbeitung von Benutzern, die in Abbildung 6 gezeigt wird. Wenn Sie die ausgeblendeten Felder auf der Seite anzeigen, sehen Sie, dass die Benutzer-ID direkt in das Formular eingebettet wurde – ideal, um manipuliert zu werden (siehe Abbildung 7). Dieses Feld wird als primärer Schlüssel für den Datensatz für diesen Benutzer verwendet, dessen Manipulierung den Datensatz verändert, der wieder in der Datenbank gespeichert wird.

An Edit User Form
Abbildung 6 Formular für die Bearbeitung von Benutzern

Revealing a Hidden Field on the Form
Abbildung 7 Anzeigen eines ausgeblendeten Formularfelds

Wie verhindern Sie die Manipulierung von Parametern? Vertrauen Sie keinen Daten, die von Benutzern bereitgestellt werden, und validieren Sie die Daten, die Sie empfangen und von denen Entscheidungen abhängig sind. Im Allgemeinen ist es nicht wichtig, ob ein Benutzer den zweiten Vornamen ändert, der im Profil gespeichert ist. Es ist jedoch sicherlich wichtig, wenn die ausgeblendete ID im Formular geändert wird, die den Datensatzschlüssel für diesen Benutzer darstellt. In diesen Fällen sollten Sie vertrauenswürdige Daten von einer bekannten Quelle auf dem Server und nicht von der Webseite abrufen. Diese Informationen könnten in der Benutzersitzung direkt nach der Anmeldung oder im Mitgliedschaftsanbieter gespeichert werden.

Ein wesentlich besserer Ansatz, der Informationen aus der Mitgliedschaft anstelle von Daten aus dem Formular verwendet, ist der folgende:

// Better practice

int userId = Membership.GetUser().ProviderUserKey.ToString();

// Load user based on this ID

var user = LoadUser(userId);

Da Sie nun gesehen haben, wie wenig vertrauenswürdig Daten aus dem Browser sein können, betrachten wir einige Beispiele für die Validierung dieser Daten, um zur Bereinigung dieser Situation beizutragen. Dies sind einige typische Szenarien für Webformulare:

// 1. No check! Especially a problem because this productId is really numeric.

string productId = Request.QueryString["ProductId"];

// 2. Better check

int productId = int.Parse(Request.QueryString["ProductId"]);

// 3.Even better check

int productId = int.Parse(Request.QueryString["ProductId"]);

if (!IsValidProductId(productId))

{

    throw new InvalidProductIdException(productId);

}

Abbildung 8 zeigt ein typisches MVC-Szenario mit Modellbindung, in dem einfache automatische Konvertierungen durchgeführt werden, ohne dass Parameter ausdrücklich umgewandelt werden müssen.

Abbildung 8 – Verwenden der MVC-Modellbindung

[HttpPost]

[ValidateAntiForgeryToken]

public ActionResult Edit([Bind(Exclude="UserId")] Order order)

{

   ...

   // All properties on the order object have been automatically populated and 

   // typecast by the MVC model binder from the form to the model.

   Trace.Write(order.AddressId);

   Trace.Write(order.TotalAmount);

   // We don’t want to trust the customer ID from a page

   // in case it’s tampered with.

   // Get it from the profile provider or membership object

   order.UserId = Profile.UserId;

   // Or grab it from this location

   order.UserId = Membership.GetUser().ProviderUserKey.ToString();

   ...

   order.Save();}

   ...

   // etc.

}

Die Modellbindung ist eine hervorragende Funktion von Model-View-Controller (MVC), die die Prüfung von Parametern unterstützt, da die Eigenschaften im Bestellobjekt automatisch ausgefüllt und in die definierten Typen umgewandelt werden, basierend auf den Formulardaten. Sie können Datenanmerkungen für das Modell definieren und zahlreiche verschiedene Validierungen einschließen. Sie müssen jedoch die Eigenschaften, die ausgefüllt werden dürfen, eingrenzen. Auch hier gilt, dass Sie den Daten auf der Seite in Bezug auf wichtige Objekte nicht vertrauen dürfen. Eine gute Faustregel besteht darin, für jede Ansicht jeweils ein Ansichtsmodell bereitzustellen, sodass Sie in diesem Beispiel für die Bearbeitung die Benutzer-ID vollständig aus dem Modell ausschließen.

Beachten Sie, dass ich hier das [Bind(Exclude)]-Attribut verwende, um zu begrenzen, was MVC in mein Modell bindet, um zu steuern, welchen Objekten ich vertraue und welchen ich nicht vertraue. Dies stellt sicher, dass die Benutzer-ID nicht aus den Formulardaten stammt und daher nicht manipuliert sein kann. Die Modellbindung und die Datenanmerkungen sind nicht Gegenstand dieses Artikels. Ich habe dies hier nur kurz erwähnt, um Ihnen zu zeigen, wie die Parametertypisierung in Webformularen und in MVC funktionieren kann.

Wenn Sie auf der Webseite ein ID-Feld einfügen müssen, dem Sie "vertrauen", finden Sie unter dem Link für MVC-Sicherheitserweiterungen (mvcsecurity.codeplex.com) Informationen über ein Attribut, das Sie hierbei unterstützt.

Zusammenfassung

In diesem Artikel habe ich Ihnen zwei der häufigsten Arten von Angriffen auf Anwendungen gezeigt. Wie Sie sehen, können Angriffe jedoch verhindert oder zumindest begrenzt werden, indem Sie einige wenige Änderungen an Ihren Anwendungen vornehmen. Natürlich gibt es Varianten dieser Angriffe und weitere Möglichkeiten, wie Ihre Anwendungen ausgenutzt werden können. Ich werde in der nächsten Ausgabe zwei weitere Arten von Angriffen behandeln, die siteübergreifende Skripterstellung und die siteübergreifende Anforderungsfälschung.   

Adam Tuliper ist Softwarearchitekt bei Cegedim und entwickelt seit mehr als 20 Jahren Software. Er ist nationaler INETA Community Speaker und hält regelmäßig Vorträge auf Konferenzen und in .NET-Benutzergruppen. Sie finden ihn auf Twitter unter twitter.com/AdamTuliper. Seinen Blog finden Sie unter completedevelopment.blogspot.com oder secure-coding.com.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Barry Dorrans