09 Okt

Steam Login analysiert

Es ist unsicher, auf Websites Passwörter beim Login im Klartext über den Äther zu schicken. Meistens begegnet man diesem Problem mit SSL/TLS-Verschlüsselung, also HTTPS. Die Entwickler der Steamwebsite (https://store.steampowered.com/login/) haben jedoch einen zusätzlichen Kniff eingebaut: Sie verschlüsseln das Passwort vor der Übertragung vom Browser zum Server mit RSA.

RSA ist ein asymmetrisches Verschlüsselungsverfahren, d.h. es gibt nicht nur einen Schlüssel mit dem sowohl ver- als auch entschlüsselt werden kann, sondern einen privaten und einen öffentlichen Schlüssel. Was der öffentliche verschlüsselt, kann nur vom privaten entschlüsselt werden.

Steam macht sich dieses Prinzip zu nutze, indem es den privaten Schlüssel für sich behält, und den öffentlichen Schlüssel an den Client schickt, sodass dieser damit das Passwort verschlüsseln kann. Nur der Server kann dann das Klartext-Passwort bestimmen. Das Anfragen des öffentlichen RSA-Schlüssels geschieht nach dem Klick auf den Anmelden-Button:

new Ajax.Request( 'https://store.steampowered.com/login/getrsakey/',
  {
    method: 'post',
    parameters: {
      username: username
    },
    onSuccess: OnRSAKeyResponse,
    onException: function( req, e ) { throw e; }
  }
);

Das Ergebnis des Ajax Requests sieht dann ungefähr so aus:

{  
  "success" : true,
  "publickey_mod" : "B9275F ... E8C0D9",
  "publickey_exp" : "010001",
  "timestamp" : "18026000000",
  "steamid" : "765611980555xxxxx"
}

Wenn der öffentliche Schlüssel vom Server zurückgeschickt wurde (aufgeteilt in den Modul publickey_mod und den Exponent publickey_exp), kann das Passwort verschlüsselt an Steam geschickt werden:

var pubKey = RSA.getPublicKey( results.publickey_mod, results.publickey_exp );
// ...
var encryptedPassword = RSA.encrypt( password, pubKey );
new Ajax.Request( 'https://store.steampowered.com/login/dologin/',
{
  method: 'post',
  parameters: {
    username: username,
    password: encryptedPassword,
    twofactorcode: getAuthCode( results ),
    emailauth: form.elements['emailauth'].value,
    loginfriendlyname: form.elements['loginfriendlyname'].value,
    captchagid: form.elements['captchagid'].value,
    captcha_text: form.elements['captcha_text'].value,
    emailsteamid: form.elements['emailsteamid'].value,
    rsatimestamp: results.timestamp,
    remember_login: ( form.elements['remember_login'] && form.elements['remember_login'].checked ) ? 'true' : 'false'
  },
  onSuccess: OnLoginResponse,
  onException: function( req, e ) { throw e; }
}

Die Steam-Server können nun das eingegebene Passwort wiederherstellen, indem sie es mit ihrem privaten Key entschlüsseln. (Wenn das Passwort in der Datenbank nur gehasht vorliegt, muss das gerade entschlüsselte Klartext-Passwort dann auch noch gehasht werden.) Nun wird das tatsächliche Ergebnis des Login-Versuchs zurückgeliefert:

{
  "success": false,
  "requires_twofactor": false,
  "message": "SteamGuard",
  "emailauth_needed": true,
  "emaildomain": "xxx.de",
  "emailsteamid": "765611980555xxxxx"
}

In diesem Fall ist das Login am eingeschalteten SteamGuard gescheitert, der den Benutzer zusätzlich über einen per Mail versendeten Code verifiziert. Fehler kann es auch wegen benötigter Captcha-Eingabe oder Zwei-Faktor-Autorisierung geben. Im Erfolgsfall werden Cookies gesetzt und eine Transfer-URL zurückgegeben, mit der sich zusätzlich noch auf https://steamcommunity.com angemeldet wird:

{
  "success": true,
  "requires_twofactor": false,
  "login_complete": true,
  "transfer_url": "https:\/\/steamcommunity.com\/login\/transfer",
  "transfer_parameters": {
    "steamid": "765611980555xxxxx",
    "token": "196351 ... 6D73BA",
    "auth": "977576 ... c7ca0e",
    "remember_login": false,
    "webcookie": "01CF01 ... 70604C",
    "token_secure": "17BA82 ... 21EB5E"
  }
}

Relevante JavaScript-Dateien für den Login-Vorgang sind:

  • jsbn.js – eine JS-Implementierung von java.math.BigInteger
  • rsa.js – die RSA-Verschlüsselung, Hex- und Base64-De/Encoder, der PKCS1PAD2-Algorithmus
  • login.js – Controller-Code für die GUI und Ajax-Calls

Die Implementierung der Algorithmen erfolgte durch Tom Wu an der Standford University.

Update (11.10.2014):

Eine berechtigte Frage bei der Verwendung von RSA ist die folgende: Wieso wird das Passwort verschlüsselt, wenn sowieso HTTPS zum Einsatz kommt, der Transportweg also schon ausreichend gesichert ist? Zusätzlichen Schutz gegen Keylogger oder bösartige Software auf den Steam-Servern bietet diese Implementierung jedenfalls nicht.

Eine Erklärung liefert Valve selbst:

As to the security benefits you are correct that SSL is already quite secure on its own. This doesn’t provide any new security between you and the web server your SSL connection terminates at. However, the RSA does have several benefits like making the web server itself unable to easily see the plaintext data and making that possible only at the backend server deeper in our network that does the actual account authorization. Additionally it’s useful in making it harder on phishers who have lists of usernames/passwords they want to attempt to validate against our system. The cost for them automating lots of logons goes up due to requiring crypto work on their end (note: we have a number of layers of protection against scripted mass logon attempts, this is just one new small benefit).

Massta sagt:

Toller Beitrag, vielen vielen Dank!

K sagt:

„The cost for them automating lots of logons goes up due to requiring crypto work on their end“

RSA ist kein langsamer Algorithmus. Die Aussage is einfach B$. Lieber direkt im Browser nen bcrypt und dann kann auch Valve selber das Passwort nicht mehr entschlüsseln.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.