Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Passwörter mit SHA1 absichern

Geschrieben von: Christoph Wille
Kategorie: Sicherheit

This printed page brought to you by AlphaSierraPapa

Daß man bestimmte Teile einer Website nur für authentifizierte Benutzer zugänglich macht, ist mittlerweile bereits ein üblicher Vorgang. Das Einloggen der Benutzer findet (meist) über ein ASP Formular statt, das nicht über SSL abgesichert ist - und dabei wird das Passwort im Klartext über das Netzwerk übertragen - zur freien Entnahme sozusagen. Der heutige Artikel zeigt, wie man den potentiellen Mitlesern die Arbeit erschweren kann.

Ich möchte gleich vorweg sagen, daß die heute vorgestellte Methode - das Verwenden eines Hash-Algorithmus - nach wie vor Sicherheitsprobleme in sich birgt. Das "einzige", das man gewinnt ist, daß das Passwort nicht mehr geknackt werden kann, wenn man den Netzwerktraffic mitprotokolliert. Technisch ist es aber dennoch möglich, sich als der jeweilige Benutzer auszugeben. Um auch das zu verhindern, müßte man ein zu NT ähnliches Challenge-Response System aufbauen. Dies ist zwar aufwendiger, aber durchführbar.

Für heute bleiben wir aber beim Secure Hash Algorithmus SHA1 (spricht sich wie der ebensolche von Persien aus). Dieser generiert aus einem String einen 160 Bit langen Hash, der eindeutig ist - und zweitens nur in eine Richtung funktioniert: hat man den Hash, kann man daraus das Passwort nicht mehr errechnen. Wenn man also am Client das Passwort durch den Algorithmus schickt, kann jeder, der den Netzwerktraffic protokolliert, nichts mehr damit anfangen.

SHA1 verwenden

Jetzt widmen wir uns der Frage, wie man diesen Algorithmus implementieren muß - gar nicht! Es gibt nämlich die Website JavaScript MD5, auf der man eine JavaScript Implementierung zur Gratis-Verwendung downloaden kann. Obwohl dort immer nur von Client-Side Anwendungen geredet wird, kann man diese ohne Umwege sofort am Server einsetzen (simpletest.asp):

<html>
<head>
<title>SHA1 Simple Test</title>
</head>
<body bgcolor="#ffffff">

<form method="post" action="<%=Request.ServerVariables("SCRIPT_NAME")%>">
<input type="text" name="txtText2Scramble" value="SHA1 ist 160 Bit">&nbsp;
<input type="submit">
</form>

<!-- #include file="serversidesha1.asp"-->
<%
str2Scramble = Trim(Request.Form("txtText2Scramble"))
If "" <> str2Scramble Then
  Response.Write "<pre>" & calcSHA1(str2Scramble) & "</pre>"
End If
%>

</body>
</html>

Ich habe mir erlaubt, eine kleine Includedatei zu erstellen, die auf die sha1.js verweist, die man von JavaScript MD5 downgeloadet hat. Es ist nur ein server-side Wrapper der einfachsten Art:

<script language="JScript" runat="server" src="sha1.js">
</script>

In sha1.js findet sich auch die Implementierung der Funktion calcSHA1, die ich in meinem bescheiden kurzen server-side Code aufrufe. Als Ausgabe erhalte ich einen 40 Zeichen langen String, der die hexadezimale Repräsentation des Hashes darstellt.

Die Login Seite

Der Algorithmus funktioniert also, Zeit ihn bei einem Login Formular einzubauen. Um ein realitätsnahes Beispiel zu erstellen, enthält der heutige Download auch eine Demodatenbank, mit einer Tabelle Logins. Diese hat zwei Spalten: Username und Password. Der Praxisbezug? Auch in der Datenbank speichere ich nur den Hash des Passwortes - dadurch kann ein Hacker nirgends an das Passwort herankommen. Nachteil? Wenn ein Benutzer sein Passwort vergisst, kann man ihm nur ein neues zuweisen - was eigentlich auch deutlich sicherer ist (siehe Windows 2000 Benutzeraccounts).

Ich habe die Seite zweigeteilt - eine Datei enthält das Formular, das angezeigt wird. Die andere enthält die Logik, um den Account zu überprüfen. Diese letztere Datei inkludiert das Formular, sodaß alles wie eine einzige Seite funktioniert - es läßt sich aber leichter verstehen, wenn man die Teile getrennt betrachtet.

Das Formular

Unser "Hauptproblem" ist das Formular, in dem der Benutzer den Benutzernamen und das Passwort eingibt. Ich muß nämlich bevor er das Formular abschickt, das Passwort mit SHA1 hashen. Und der einzige Weg dazu ist, client-seitiges JavaScript zu verwenden.

Ja, ich kenne die Einwände, die jetzt kommen, aber: wer Zutritt zu einem gesperrten Bereich möchte, muß leiden, ergo JavaScript einschalten.

Bevor ich Sie jetzt auf den Code von logonform.asp loslasse, noch ein Hinweis: der Validierungscode plus das Hashen des Passwortes passieren in der JavaScript Funktion Validate.

<html>
<head>
<title>Login</title>
<!--#include file="clientsidesha1.asp"-->
<script language="JavaScript">
var errfound = false;

function Validate() 
{
   errfound = false;
   myForm = document.frmLogon;

   if (!ValidLength(myForm.txtUsername.value,6)) 
      error(myForm.txtUsername,"Bitte geben Sie einen Benutzernamen ein!");
   if (!ValidLength(myForm.txtPassword.value,6)) 
      error(myForm.txtPassword,"Bitte geben Sie Ihr Passwort ein (> 6 ...");

   if (!errfound)
   {
      myForm.txtPassword.value = calcSHA1(myForm.txtPassword.value);
   }

   return !errfound;
}

//function to validate by length
function ValidLength(item, len) 
{
   return (item.length >= len);
}

// display an error alert
function error(elem, text) 
{
// abort if we already found an error
   if (errfound) return;
   window.alert(text);
   elem.select();
   elem.focus();
   errfound = true;
}
</script>
</head>
<body bgcolor="#ffffff">

<table width="100%">
<tr><td align="center">
<table border=1>
<tr><td bgcolor="#33ccff" valign="center" align="center" width="350">
<H1>Login</H1>

<% if bValidationFailed Then %>
<p><strong>Login fehlgeschlagen. Bitte erneut versuchen</strong></p>
<% End If %>
<p>
<form method="post" action="<%=Request.ServerVariables("SCRIPT_NAME")%>" 
       id=frmLogon name=frmLogon LANGUAGE="JAVASCRIPT" onsubmit="return Validate();">
<table>
<tr>
  <td>Username:</td><td><input type=text name="txtUsername" size="20"></td>
</tr>
<tr>
  <td>Password:</td><td><input type=password name="txtPassword" size ="20"></td>
</tr>
<tr>
  <td colspan=2>&nbsp;<br><center>
      <input type="submit" value="Enter secured area"></center></td>
</tr>
</table>
</form>
</p>

</td></tr></table>
</td></tr></table>

</BODY>
</HTML>

Der HTML Code für das Formular ist nichts besonderes, nur aus Sicherheitsgründen müßte man das ACTION Tag herausnehmen, daß niemand, der JavaScript ausgeschaltet hat, das Formular ohne Passwort-Hashing abschickt. Apropos Hash: SHA1 wird über die Datei clientsidesha1.asp eingebunden, die den gleichen Zweck wie sein server-seitiger Bruder erfüllt:

<script language="JavaScript" src="sha1.js"></script> 

Die Validate Funktion ist eigentlich keineswegs "Rocket Science". Es werden die Felder überprüft (willkürlich, hier kann der Leser die Überprüfung nach eigenen Gesichtspunkten gestalten), und danach das Passwort gehasht, und wieder zurück in das Passwortfeld geschrieben. Per POST-Back geht es dann zurück an den Server, an unsere Überprüfungslogik.

Überprüfung des Accounts

Wir bekommen einen Benutzernamen und ein gehashtes Passwort geliefert, und da in unserer Datenbank genau die gleichen Informationen gespeichert sind, brauchen wir nur eine einfache SELECT Abfrage. Der notwendige Code ist in der Datei logon.asp zu finden:

<% @ Language=VBScript %>
<%
bValidationFailed = False
If Request.ServerVariables("CONTENT_LENGTH") > 0 Then
  strUsername = Trim(Request.Form("txtUsername"))
  strPassword = Trim(Request.Form("txtPassword"))
  
  Set conn = Server.CreateObject("ADODB.Connection")
  Set rs = Server.CreateObject("ADODB.Recordset")
  strPath = Server.MapPath("logindb.mdb")
  strConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strPath
  conn.Open strConnection
  rs.Open "select * from Logins where Username='" & strUsername & _
       "' AND Password='" & strPassword & "'", conn

  If not rs.EOF and not rs.BOF then
   ' beliebige Daten aus der Tabelle auslesen...
   Session("AccessGranted") = True
   rs.Close
   conn.Close
   Response.Redirect "ok.asp"
   Response.End
  End if

  Response.AppendToLog " Logon failed: " & strUsername & " " & strPassword
  bValidationFailed = True
End If
%>
<!--#include file="logonform.asp"-->

Dieser Code ist selbsterklärend, und einfach zu erweitern: zB mehr Daten aus der Logins Tabelle auslesen, andere Session Variablen setzen, oder ein anderes Redirect-Ziel vorgeben. Ich habe sogar daran gedacht, fehlgeschlagene Logins in das Log mitzuprotokollieren.

Ach ja, beinahe hätte ich es vergessen - die letzte Zeile inkludiert das Login-Formular, das ich in der vorangegangenen Sektion vorgestellt habe. Damit wäre unser sicherer Login realisiert.

Schlußbemerkung

Der SHA1 Algorithmus eignet sich nicht nur für dieses Szenario - was ist, wenn man bei einem Shop keine Cookies verwenden will, und eine Art SessionID im Querystring mitgibt? Hier kann der SHA1 Algorithmus ebenfalls helfen, denn minimalste Änderungen am Inputstring ergeben einen beinahe völlig anderen Hashwert. Andere Kundenwarenkörbe zu "erraten" wird damit beinahe unmöglich.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20010330.zip

Verwandte Artikel

Aber bitte mit Rijndael
http:/www.aspheute.com/artikel/20010528.htm
ASP-basierte Basic Authentication
http:/www.aspheute.com/artikel/20010521.htm
Benutzerverwaltung leicht gemacht: Teil 1
http:/www.aspheute.com/artikel/20020429.htm
CAPICOM One
http:/www.aspheute.com/artikel/20020115.htm
Ein einfacher Eventkalender für Projektteams
http:/www.aspheute.com/artikel/20020319.htm
Formular-basierte Basic Authentication
http:/www.aspheute.com/artikel/20010608.htm
Gegengifte für SQL Injection
http:/www.aspheute.com/artikel/20011031.htm
On Demand Zugriffsrechte für Web Sites vergeben
http:/www.aspheute.com/artikel/20011207.htm
Passwörter speichern - aber richtig!
http:/www.aspheute.com/artikel/20040105.htm
PGP-Verschlüsselung bei Dateien
http:/www.aspheute.com/artikel/20000920.htm
Sicherheitsaspekte bei der Gestaltung von ASP Sites ohne Cookies
http:/www.aspheute.com/artikel/20010601.htm
Unknackbare Verschlüsselung mit Onetime Pads
http:/www.aspheute.com/artikel/20010924.htm
Ver- und entschlüsseln von Texten mit PGP
http:/www.aspheute.com/artikel/20000921.htm
Verwendung von SSL Test-Certificates
http:/www.aspheute.com/artikel/20000630.htm
Verzeichnissicherheit mit NTFS und IIS Authentifizierung
http:/www.aspheute.com/artikel/20001109.htm

Links zu anderen Sites

JavaScript MD5
http://www.pajhome.org.uk/crypt/md5/index.html

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.