Ich habe ein paar ähnliche Fragen gesehen, die nicht ganz genau meinen genauen Anwendungsfall zu adressieren, und ich denke, ich habe die Antwort herausgefunden, aber ich bin ein totaler Noob, wenn es um Sicherheit geht , RSA und so ziemlich alles, was damit verbunden ist. Ich habe eine grundlegende Vertrautheit mit den Konzepten, aber all die tatsächlichen Implementierungen, die ich bis zu diesem Zeitpunkt gemacht habe, drehen sich nur darum, den Code von jemand anderem zu bearbeiten, anstatt meinen eigenen Code zu erstellen. Wie auch immer, hier ist, wo ich bin:SSL über Javascript
Ich weiß, dass Javascript eine inhärent schlechte Stelle ist, um Verschlüsselung zu tun. Jemand könnte Man-in-the-Middle Ihre Antwort geben und den JS beschädigen, so dass Sie am Ende unverschlüsselte Daten über die Leitung senden. Es sollte über eine HTTPS-SSL/TLS-Verbindung erfolgen, aber diese Art von Hosting kostet Geld und so tun die offiziellen signierten Zertifikate, die realistisch mit der Verbindung gehen sollten.
aber sagen, dass ich glaube, die Art, wie ich tun werde dies die Man-in-the-Middle-Schwäche von JS Verschlüsselung aufgrund der Tatsache umgeht, dass ich immer nur eine Sache, die Verschlüsselung (ein Passwort-Hash) für einen RESTful-Service-Aufruf und dann nur mit diesem Passwort-Hash, um Anfragen vom Client zu signieren, um sie als vom Benutzer stammend zu authentifizieren, den die Anfragen beanspruchen. Das bedeutet, dass der JS nur dafür verantwortlich ist, einen Passwort-Hash einmal bei der Erstellung des Benutzerkontos zu verschlüsseln, und wenn der Server diese Chiffre nicht entschlüsseln kann, dann weiß er, dass sie bereits vergeben wurde.
Ich werde auch einige Client-Informationen speichern, insbesondere die $_SERVER['REMOTE_ADDR']
, um sicherzustellen, dass jemand nicht M-i-t-M die Registrierung selbst austauschen.
Ich verwende PHP openssl_pkey_
Funktionen, um einen asymmetrischen Schlüssel und die Cryptico Bibliothek auf der Client-Seite zu generieren. Mein Plan ist, dass der Benutzer eine "Vorregistrierungs" -Anfrage an den REST-Dienst sendet, was dazu führt, dass der Server einen Schlüssel erzeugt, den privaten Schlüssel und die Client-Informationen in einer durch die E-Mail-Adresse indizierten Datenbank speichert und dann antwortet mit dem öffentlichen Schlüssel.
Der Client verschlüsselt dann den Kennwort-Hash des Benutzers mit dem öffentlichen Schlüssel und sendet ihn als weiteren Anforderungstyp an den REST-Dienst, um die Registrierung abzuschließen. Der Server würde den Kennwort-Hash entschlüsseln und speichern, die Client-Informationen und den privaten Schlüssel ungültig machen, so dass keine weiteren Registrierungen unter Verwendung dieser Informationen durchgeführt werden könnten, und dann mit einem 200
Statuscode antworten.
Um sich anzumelden, würde ein Benutzer seine E-Mail-Adresse und sein Passwort eingeben, das Passwort würde während der Registrierung gehashed, an den Anfragetext angehängt und erneut gehashed, um eine Anfrage an einen Anmeldeendpunkt zu signieren, der anhängen würde den gespeicherten Hash-Code in den Anfragetext und hasht ihn, um die Signatur gegen die in der Anfrage zu validieren und so den Benutzer zu authentifizieren. Weitere Datenanforderungen an den Dienst würden demselben Authentifizierungsprozess folgen.
Fehle ich irgendwelche grellen Löcher? Ist es möglich, den $_SERVER['REMOTE_ADDR']
Wert auf etwas bestimmtes zu spoofen? Ich brauche nicht die IP-Adresse, um genau zu sein oder die gleiche wie wenn der Benutzer sich anmeldet, ich muss nur wissen, dass der gleiche Rechner, der 'vorregistriert' hat und einen öffentlichen Schlüssel bekommen hat, die Registrierung abgeschlossen hat und nicht Hijacker, der die Registrierung für sie unter Verwendung eines erschnüffelten öffentlichen Schlüssels abschließt. Natürlich, wenn sie das schaffen, haben sie das Konto bei der Erstellung über die Wiederherstellung hinaus gehackt und der berechtigte Benutzer wäre nicht in der Lage, die Registrierung mit seinem eigenen Passwort abzuschließen, was auch in Ordnung ist.
Bottom line, kann jemand immer noch meinen Service hacken, wenn ich für einen echten SSL-Host abzweigen? Habe ich Javascripts Schwächen als Verschlüsselungswerkzeug umgangen?
Während ich meinen Code schreibe und debugge, poste ich ihn hier, wenn jemand ihn benutzen will.Bitte lassen Sie mich wissen, wenn ich meine Seite für Angriffe offen lasse.
Dies sind die Funktionen, die Clientanforderungen anhand des Hashs in den Headern validieren, den privaten Schlüssel generieren, in der Datenbank speichern, mit dem öffentlichen Schlüssel antworten und den Kennwort-Hash entschlüsseln und überprüfen.
public function validate($requestBody = '',$signature = '',$url = '',$timestamp = '') {
if (is_array($requestBody)) {
if (empty($requestBody['signature'])) { return false; }
if (empty($requestBody['timestamp'])) { return false; }
if ($requestBody['requestBody'] === null) { return false; }
$signature = $requestBody['signature'];
$timestamp = $requestBody['timestamp'];
$requestBody = $requestBody['requestBody'];
}
if (($requestBody === null) || empty($signature) || empty($timestamp)) { return false; }
$user = $this->get();
if (count($user) !== 1 || empty($user)) { return false; }
$user = $user[0];
if ($signature !== md5("{$user['pwHash']}:{$this->primaryKey}:$requestBody:$url:$timestamp")) { return false; }
User::$isAuthenticated = $this->primaryKey;
return $requestBody;
}
public function register($emailAddress = '',$cipher = '') {
if (is_array($emailAddress)) {
if (empty($emailAddress['cipher'])) { return false; }
if (empty($emailAddress['email'])) { return false; }
$cipher = $emailAddress['cipher'];
$emailAddress = $emailAddress['email'];
}
if (empty($emailAddress) || empty($cipher)) { return false; }
$this->primaryKey = $emailAddress;
$user = $this->get();
if (count($user) !== 1 || empty($user)) { return false; }
$user = $user[0];
if (!openssl_private_decrypt(base64_decode($cipher),$user['pwHash'],$user['privateKey'])) { return false; }
if (md5($user['pwHash'].":/api/preRegister") !== $user['session']) { return false; }
$user['session'] = 0;
if ($this->put($user) !== 1) { return false; }
$this->primaryKey = $emailAddress;
User::$isAuthenticated = $this->primaryKey;
return $this->getProfile();
}
public function preRegister($emailAddress = '',$signature = '') {
if (is_array($emailAddress)) {
if (empty($emailAddress['signature'])) { return false; }
if (empty($emailAddress['email'])) { return false; }
$signature = $emailAddress['signature'];
$emailAddress = $emailAddress['email'];
}
if (empty($emailAddress) || empty($signature)) { return false; }
$this->primaryKey = $emailAddress;
$response = $this->makeUserKey($signature);
if (empty($response)) { return false; }
$response['emailAddress'] = $emailAddress;
return $response;
}
private function makeUserKey($signature = '') {
if (empty($signature)) { return false; }
$config = array();
$config['digest_alg'] = 'sha256';
$config['private_key_bits'] = 1024;
$config['private_key_type'] = OPENSSL_KEYTYPE_RSA;
$key = openssl_pkey_new($config);
if (!openssl_pkey_export($key,$privateKey)) { return false; }
if (!$keyDetails = openssl_pkey_get_details($key)) { return false; }
$keyData = array();
$keyData['publicKey'] = $keyDetails['key'];
$keyData['privateKey'] = $privateKey;
$keyData['session'] = $signature;
if (!$this->post($keyData)) { return false; }
$publicKey = openssl_get_publickey($keyData['publicKey']);
$publicKeyHash = md5($keyData['publicKey']);
if (!openssl_sign($publicKeyHash,$signedKey,$privateKey)) { return false; }
if (openssl_verify($publicKeyHash,$signedKey,$publicKey) !== 1) { return false; }
$keyData['signedKey'] = base64_encode($signedKey);
$keyData['rsa'] = base64_encode($keyDetails['rsa']['n']).'|'.bin2hex($keyDetails['rsa']['e']);
unset($keyData['privateKey']);
unset($keyData['session']);
return $keyData;
}
Ich sah gerade [diese] (http://stackoverflow.com/questions/5724650/ssl-alternative-encrypt-password-with-javascript-submit-to-php-to-decrypt?rq=1) in mein verwandter Feed, und der Unterschied hier ist, dass die Anfrage mit einem verschlüsselten Passwort nur einmal pro Benutzer gesendet werden kann, nur damit der Server weiß, welcher Wert verwendet wird, um die Hashes zukünftiger Anfragen zu isolieren. Wenn es jemand geschafft hat, seine IP zu fälschen, um mit dem echten Nutzer zu spielen, könnten sie den Benutzernamen beanspruchen, aber niemals ein Konto entführen, das nach der Registrierung verwendet wird ... Ich denke ... – citizenslave
Ich nehme die beiden Aspekte an bin unwohl mit ist; 1. Nein, wir können $ _SERVER ['REMOTE_ADDR'] nicht vertrauen, in einer Reverse-Firewall eines Unternehmens könnten alle Benutzer die gleiche IP (nach außen) haben, abhängig von ihrer Konfiguration. 2. In einem Nicht-SSL-Szenario können Sie nicht darauf vertrauen, dass die Leitung nicht gehört wird (besonders jetzt, da wir wissen, dass eine "Attacke" jemand in ihrem Firewall-Netzwerk sein könnte). Das soll nicht heißen, dass ich noch das komplette Angriffsszenario habe. –
Danke für die schnelle Antwort. Gemeinsame IPs in einem privaten Netzwerk sind ein echtes Problem bei der Verwendung von '$ _SERVER ['REMOTE_ADDR']', aber es scheint mir immer noch der einzige Angriff zu sein, den öffentlichen Schlüssel aus der Vorregistrierungsantwort zu schnüffeln, um ihn zu verwenden verschlüsseln Sie Ihr eigenes Passwort anstelle des Benutzers und spoofen Sie die endgültige Registrierungsanfrage. Dann hätten Sie die Kontrolle über das Konto, aber der Benutzer hätte das nie. Ich bin nicht begeistert davon, diesen Weg offen zu halten, aber ich kann damit leben, vor allem wenn die einzige Möglichkeit, die entfernte Adresse zu fälschen, darin besteht, sich im selben privaten Netzwerk zu befinden. Nicht perfekt, aber akzeptabel. – citizenslave