Sichere Anmeldungen aus Webseiten mit HMAC

Wer Anmeldungen aus einer Webseite ermöglich, muss sich mit vielen Angriffsszenarien beschäftigen, um die Webseite abzusichern. Die bekanntesten Angriffe können durch Mittel aus den Frameworks meistens schon (weitestgehend) blockiert werden. Dazu zählen z.B. SQL Injection, XSS und CSRF. Es gibt aber auch noch MITM, Man-in-the-middle-Attacken. Bei diesen befindet sich der Angreifer zwischen Ihnen und dem Server. Somit kann er jeden Datenverkehr abhören und nach belieben ändern. Selbst eine SSL-Verbindung bietet hier nicht immer den gewünschten Schutz.

Um diesen Angriff abwehren zu können, gibt es aber auch Mittel und Wege. Einer ist HMAC, Keyed-Hash Message Authentication Code. Dieser berechnet aus einem Schlüssel und einer Nachricht einen Hash, der trotz des öffentlich lesbaren Schlüssels sicher ist. Ein Rückschluss auf die Nachricht kann nicht erfolgen.

Wichtig bei MITM ist, dass es separate Aktionen im Client und Server geben muss. Somit kann der Angreifer keine Rückschlüsse auf den Inhalt ziehen.

Ich habe eine Implementierung für Client (Webseite mit Javascript auf Basis von ASP.NET MVC3 und jQuery) und Server (.Net) erstellt. Der schematische Ablauf wird in folgenden Bild skizziert.

HMAC Web Authentication

Bei der Implementierung der Clientseite habe die Textfelder für Benutzername und Passwort nicht in das Formular eingebunden. Somit werden diese Daten auch nicht an den Server geschickt. Das soll ja gerade vermieden werden. Stattdessen befinden sich im Formular zwei Hiddenfields, welche die Credentials enthalten. Den HMAC-Algorithmus stellt die Javascriptbibliothek jsSHA bereit. Diese kann frei verwendet werden und bietet unterschiedliche Hashalgorithmen.

[html]
http://../../Scripts/jquery-1.4.4.min.js
http://../../Scripts/sha.js

function loadToken(username) {
$.ajax({
url: „/Security/Token“,
data: { username: username },
async: false,
success:
function (data) {
$(„#token“).val(data);
}
});
}

function calcHMAC(message) {
var token = $(„#token“).val();
var hmacObj = new jsSHA(message, „ASCII“);
var hmac = hmacObj.getHMAC(token,
„ASCII“,
„SHA-256“,
„HEX“);
return hmac;
}

function login() {
//load values from fields
var username = $(„#user“).val();
var pwd = $(„#pass“).val();
//load token from server
loadToken(username);
//calculate hmac with token and user password
var hmac = calcHMAC(pwd, token);
//set hmac to hidden field
$(„#hash“).val(hmac);
//send form to server to validate the credentials
document.forms[0].submit();
}

Index

Ergebnis der Anmeldung: @TempData[„login“]

@Html.Hidden(„token“)
@Html.Hidden(„hash“)

Anmeldung
Username:

Password:

[/html]

Die Serverseite habe ich in einer stark vereinfachten Version erstellt. Diese dient nur zum Veranschaulichen der Verarbeitung. Natürlich muss die Logik in einer realen Umgebung angepasst werden. Im hier dargestellten Code muss das Passwort dem rückwärts gelesenen Benutzernamen entsprechen.

[csharp]
public class SecurityController : Controller
{
private static Dictionary tokens = new Dictionary();

///

/// Erzeugt den HMAC aus Token und dem Passwort.
///

/// Token für die Erstellung des Hashes
/// Mittel Benutzername wird das Passwort erzeugt.
/// berechneter HMAC für Passwort
private string CalculateHMAC(string token, string username)
{
HMACSHA256 hmac = new HMACSHA256();
string message = new string(username.Reverse().ToArray());
hmac.Key = ASCIIEncoding.ASCII.GetBytes(token);
byte[] plain = ASCIIEncoding.ASCII.GetBytes(message);
byte[] cipher = hmac.ComputeHash(plain);
return ByteToString(cipher);
}

///

/// Erstellt ein zufälliges Token.
///

/// Benutzername, für den das Token erstellt werden soll.
/// Token für Benutzer
private string GenerateToken(string user)
{
Random rnd = new Random();
return GenerateSHA256Hash(user + rnd.Next().ToString());
}

///

/// Erzeugt einen SHA256-Hashwert.
///

/// zu hashende Wert
/// Hash
private string GenerateSHA256Hash(string value)
{
SHA256 alg = SHA256.Create();
byte[] plain = ASCIIEncoding.ASCII.GetBytes(value);
byte[] cipher = alg.ComputeHash(plain);
return ByteToString(cipher);
}
///

/// Wandelt ein Bytearray in einen HEX-String um.
///

/// Bytearray
/// HEX-String
public string ByteToString(byte[] buff)
{
string sbinary = „“;

for (int i = 0; i val.Value == token).Key;
//HMAC ermitteln
var serverHash = CalculateHMAC(token, username);
//HMAC von client und Server vergleichen
if (hash.Equals(serverHash))
TempData.Add(„Login“, „Juhu, ich bin angemeldet.“);
else
TempData.Add(„Login“, „Du kommst hier nicht rein!“);
}

return View(„Index“);
}

///

/// Liefert ein Token (an den Client)
///

/// Benutzername
/// Token, welcher an den Client geliefert wird
public string Token(string username)
{
if (tokens.ContainsKey(username))
tokens.Remove(username);

var token = GenerateToken(username);

tokens.Add(username, token);

return token;
}
}
[/csharp]

Die Verarbeitung der Serverseite habe ich in einem Sequenzdiagramm zusammengestellt. Sequenzdiagramm der HMAC Authentifizierung

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s