Tutorials

https voor inloggen zonder https

Je wil data veilig oversturen, maar zonder https? Makkelijk!

Pagina 1

Wat je wil...

Wat je wil is een veilige website met een veilige login.
Hoe je je gebruikers ingelogd houdt is niet mijn probleem. Er zijn zoveel meningen als er mensen zijn, dus daar ga ik het niet over hebben.
Er zit echter een heel belangrijk punt tussen voordat je ingelogd bent, en erna: inloggen.
De meeste mensen denken er niet over na ( de meeste mensen hebben zelfs wachtwoorden niet eens gehasht in hun db ), maar als je een flinke website hebt draaien die een gewilde dienst draait, is dit interessant.

Waar heb ik het over!? Over hacken.

In dit geval, over de zogenaamde man-in-the-middle attack. De vervelendste attack die er is. Ik zal uitleggen waarom!

The middle
Onder "the middle" wordt alles verstaan tussen client en server. Je zou zeggen dat dat niets is, maar in feite is het heel veel: internet. Je moet eens weten hoeveel er voor nodig is om een simpele request te maken naar een server vanuit je webbrowser.

The beginning
Ofwel de client :) Daar komt de logindata vandaan.

The end
De server, waar het veilig is als het eenmaal ontvangen is.

The man
Een sneaky vies mannetje met een heel groot vangnet die alles opvangt wat tussen The beginning en The end reist.

Een voorbeeldje...
Het loginformulier op phphulp.nl:
<form method="post" action="/inloggen/">
<input type="hidden" name="login_referer" value="nieuws/">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" name="submit" value="ok">
</form>


Zoals je ziet worden er 4 waarden opgestuurd naar /inloggen/. Die 4 waarden (plain text) worden onderschept door een Man-in-the-middle. In die 4 waarden zit precies (precies!!!) wat iemand nodig heeft om in te loggen.
Klaar.
Man-in-the-middle kan nu inloggen op jouw account.
Balen
Pagina 2

Fokken met the man-in-the-middle

Een grote grote fout van veel mensen is dat ze denken dat ze er iets aan kunnen doen, als volgt:

* wachtwoord encrypted in de db
* wachtwoord reversen (abc->cba) en dan opsturen
* username+wachtwoord encrypted in de db

Deze manieren (en nog meer) hebben natuurlijk absoluut geen zin, want alles wordt gedaan door de server. WAT er over wordt gestuurd is niet interessant, als het je maar inlogt. Dus alles dat er over wordt gestuurd is goed, hoe het ook gebeurt. De server ontrafelt het wel en logt je in.

Een grote fout (maar al in de buurt) is client side encryption. En het is zo makkelijk als het klinkt. Met Javascript het wachtwoord encrypten en dan overgooien.
Nadeel: javascript moet aanstaan.
Groter nadeel: wat overgestuurd wordt maakt niet uit!! De server snapt het nogsteeds! WAT je ook met het wachtwoord doet, de server moet het snappen en het gaat van de client naar de server, dus man-in-the-middle is nog steeds blij!

De oplossing is gelukkig zo makkelijk als het probleem :)

Maar eerst iets over het man-in-the-middle principe...
De bot in het midden vangt data op en stuurt het toch door naar de server. Misschien wordt het niet opgevangen, maar alleen afgeluisterd.
Hoe dan ook, de bot doet niks met de data die verstuurd wordt om in te loggen. Niet op dat moment! De data wordt gemaild naar Mr. H. Acker en die gaat vervolgens met die data in zn mailtje inloggen op jouw phphulp.nl account.
En dat is heel belangrijk: inloggen met dezelfde data!

Komt-ie...

Een paar puntjes:
* Op een of andere manier moet het wachtwoord ANDERS over de lijn dan het in de db staat. Dat is geen probleem, dat kunnen we al.
* De waarde(n) die overgestuurd worden, moeten nutteloos zijn voor the man-in-the-middle wanneer ie het wil gebruiken.
* Misschien moeten we elke keer gewoon iets anders versturen... Dan snapt ie het vast niet meer...

GOED PLAN!
Maar...
Pagina 3

The man is niet dom en ook een client

...the man in the middle heeft ook een browser en kan naar phphulp.nl surfen om te zien wat ze met het inlog formulier gedaan hebben!
En wat ziet ie daar!! Die rotzakken hebben een encryptie progje (JAVASCRIPT!!!) om data te sleutelen en dan over te sturen.

Het leuke van javascript is dat je de bron kan zien. Dus alles wat de browser kan met javascript op het moment dat Henk op de account van Henk inlogt, kan Mr H. Acker ook, op het moment dat hij op de account van Henk inlogt. De browser, server of website weet namelijk niet wie de gebruikers is voordat ie is ingelogd. Iedereen moet de kans krijgen om in te loggen op zijn/haar account, dus doet Javascript voor iedereen zn ding.

Het probleem? Het is vast duidelijk... Mr H. Acker heeft nog steeds de data die nodig is om in te loggen! Hoe het gemaakt wordt staat namelijk open&bloot op de website!!

Zoals ik al zei is de oplossing geweldig gemakkelijk...

The man-in-the-middle heeft geen idee wat er ingevuld wordt op de computer van de client. Hij weet wel wat er over de lijn gaat (dus ontvangen wordt door de server). Hij weet ook hoe data versleuteld wordt aan de client kant (door software op de website: JS).

We moeten dus iets maken dat iets doet met de inlogdata (username + wachtwoord) zodat het random wordt... Zie daar het magische woord: random!
Als je de ene keer A over de lijn stuurt om in te loggen op de account van Henk, en de andere keer B om in te loggen op de account van Henk, en de andere keer C... Nja het spreekt voor zich: structuur is ver te zoeken.

En dat is de bedoeling. Wat er over de lijn gaat moet onzin zijn. Wat er over de lijn gaat moet steeds iets anders zijn. En willekeurig. Het moet kloppen als het inlogproces 1 sec duurt, maar ook als het 3 sec duurt (dus stuur aub geen timestamp oid mee!!).

Wat we mee gaan sturen is een stukje random data! En heel belangrijk: GEEN wachtwoord! Een stukje random data kan namelijk iedereen maken! OOK Mr H. Acker met zn webbrowser.

De oplossing:
Pagina 4

De oplossing

We hebben drie onderdelen van ons veilige inlogsysteem:
* formulier
* encryptiesoftware
* server (php, sql, db, jouw feestje)

Ik ga er even van uit dat je je wachtwoorden als volgt opslaat in SQL:

`password` = MD5(SHA1(CONCAT(`username`,':',PLAIN_PWD)))

Dat betekent dat je username en password (PLAIN) samengooit met een ':' ertussen en dan SHA1't en dan MD5't.
Dat zorgt er voor dat `password` voor ELKE user anders is (ook al heeft elke user 'oele' als plain wachtwoord).



Het formulier

<form id="login" method="post" action="" onsubmit="return do_js_enc();">
<div><input type="hidden" name="passphrase" id="passphrase" value="" /></div>

<p>Username</p>
<p><input type="text" name="username" id="username" value="user_pass_md5" /></p>

<p>&nbsp;</p>

<p>Password</p>
<p><input type="password" name="password" id="password" value="pindakaas" /></p>

<p>&nbsp;</p>

<input type="submit" value="login" />
</form>


Het inloggen zelf verandert niet. Je vult gewoon je wachtwoord en je username in... En je klikt op login of ramt op enter.



De encryptiesoftware

<script type="text/javascript" src="/_inc/prototype.js"></script>
<script type="text/javascript" src="/_inc/md5.js"></script>
<script type="text/javascript" src="/_inc/sha1.js"></script>
<script type="text/javascript">
<!--//

var salt = '< ? php echo $_SESSION['js_enc_login']['salt']; ? >';

var do_js_enc = function()
{
	$('passphrase').value = hex_md5( $('username').value.toLowerCase() + salt + hex_md5(hex_sha1($('username').value.toLowerCase()+":"+$('password').value)) );
	$('password').value = '';

	return true;
}

//-->
</script>

md5.js
sha1.js
prototype.js



De server

<?php

if ( isset($_POST['passphrase'], $_POST['username']) )
{
	header("content-type: text/plain");

//	print_r( $_POST );

	// salt from session (if exists)
	$salt = isset($_SESSION['js_enc_login']['salt']) ? $_SESSION['js_enc_login']['salt'] : '';
	unset( $_SESSION['js_enc_login']['salt'] );

	$sql = "SELECT * FROM js_encryption_login WHERE LOWER(username) = LOWER('" . $_POST['username'] . "') AND LOWER(MD5(CONCAT( LOWER(username), '" . $salt . "', password ))) = LOWER('" . $_POST['passphrase'] . "');";
// echo $sql . PHP_EOL . PHP_EOL;
	$qLogin = mysql_query( $sql ) or die(mysql_error());
	if ( 1 == mysql_num_rows($qLogin) )
	{
		echo "LOGIN SUCCESS!!".PHP_EOL;
		print_r( mysql_fetch_assoc( $qLogin ) );
	}
	else
	{
		echo ":( num_rows: " . mysql_num_rows($qLogin) . PHP_EOL;
		
	}

	exit;
}

?>


Zoals je ziet hebben we alleen 'username' en 'passphrase' nodig. Geen 'password'.


Een heel belangrijk onderdeel van de encryptiesoftware is de 'salt'.
Die komt uit een session... Maar wat is het en waar wordt het gemaakt? WAT het is, is weinig interessant. Waar het gemaakt wordt is wel heel belangrijk!!

The salt
<?php

$_SESSION['js_enc_login']['salt'] = str_shuffle( base64_encode( (string)rand(0,9000000) ) );

?>


Dat moet absoluut NIET VOOR het php process (inloggen). EN absoluut VOOR het formulier (en de scripts).


Zo ziet mijn pagina er uit:
1. het php process (controle op post data, etc)
2. salt maken
3. html openen en scripts (met salt) printen in <head>
4. formulier printen

Het php process gooit de salt weg als ie m heeft opgevangen. Als er geen salt in de session zit, wordt een lege string gebruikt als salt.



Uitleg
Er wordt een salt toegevoegd aan een md5 string die over de lijn gaat als passphrase. Die passphrase bestaat uit username, salt en password.
er staat letterlijk in hoe het password in de database staat. De gehele passphrase:

hex_md5( $('username').value.toLowerCase() + salt + hex_md5(hex_sha1($('username').value.toLowerCase()+":"+$('password').value)) )


Hoe het gemaakt wordt is niet spannend. Wat er mee gebeurt ook niet. En dat moet ook niet. Het is onherkenbaar en elke keer anders! Dat is belangrijk! Als een man-in-the-middle DIE string als passphrase zou stelen, kan ie m nog niet gebruiken! Waarom niet? Omdat ie niet weet welke salt erbij hoort... Omdat de salt die erbij hoort op de server staat en niet meegestuurd wordt of als cookie is opgeslagen.

Je kan 100 keer inloggen. 100 keer wordt de salt anders (hoogstwaarschinlijk :)) en dus de passphrase. En dezelfde 100 keer wordt de salt opgevangen en gebruikt in het php process om een zelfde soort passphrase te maken. Die passphrase wordt in SQL gemaakt omdat dat makkelijk is. Je kan ook alle data uit de tabel trekken die bij de user hoort en dan spul gaan vergelijken. Dit is gewoon stuk makkelijk en sneller!

Vragen zijn welkom.
Ik zal proberen het duidelijker uit te leggen als je wilt, maar meer kan ik niet in zo'n tut.

Active example: http://jouwmoeder.nl/projects/js_enc_login/ (de array die geprint wordt na inloggen is wat er in de sql tabel staat voor deze user, het is NIET post data!!)
Source: http://jouwmoeder.nl/projects/js_enc_login/source.php

Reacties

0
Nog geen reacties.