Ik ben bezig met het opzetten van een beveiligingsmodule voor een grote website waar een hoop data op komt te staan die wel enigszins veilig moet zijn. Natuurlijk is SSL mooier, maar dat valt echt buiten de mogelijkheden (helaas). Roeiend met de riemen die ik wel heb (een Win2K AS server met SQL-Server 2000 EP) heb ik het volgende zitten uitdokteren:
- user voert usernaam in op login1.asp
- client stuurt username naar ASP-script op server
- server controleert of user voorkomt en bepaalt ID van user
- server genereert random string van 40 karakters
- server redirect user naar login2.asp met de random string en userID in de URL
- user voert password in op login2.asp
- client maakt hash van ingevoerde password samen met random string
- client stuurt resultaat, samen met userID en de random string, naar de server
- server haalt password op op basis van userID
- server gebruikt random string en password uit DB om een hash te produceren
- server controleert op client gecreerde hash met op server gecreerde hash
- indien OK: user heeft correct password ingevoerd
Welk algorithme kan ik het beste gebruiken voor het maken van de hash? Op dit moment heb ik een SHA-1 implementatie voor ASP en Jscript geschreven die lekker snel zijn. MD5 lijkt me echter ook voldoende. Is het snelheidsverschil erg groot tussen deze twee?
Nu komen we bij de volgende stap. Ik wil een eigen sessie-systeem ontwikkelen. Dat zou ongeveer zo moeten gaan:
- [ user is net gevalideerd ]
- server maakt sessieID van 40/60 karakters
- server slaat sessieID, userID en IP op in een sessietabel
- server maakt cookie aan bij user met daarin de sessieID
En hier komt een vraagje tussendoor. Op dit moment werk ik met Access, maar het systeem gaat uiteindelijk op SQL-Server 2000 EP draaien. Nu is de sessieID een string van 40 of 60 karakters. Ik kan die kolom de primary key maken, maar dan loop ik het (minimale en bijna verwaarloosbare) risico dat er een kere een sessieID gegenereerd wordt die al bestaat. Het probleem, lijkt mij, is eerder de grootte van de key. Het is nogal een flinke string, en als de server iedere keer in een tabel van X records de juiste ID op moet zoeken met zo’n joekel van een string…. (of werkt een index dat tegen?). Anyways. Het lijkt me makkelijker om een autonummer-kolom op te nemen die een ID-nummertje maakt voor iedere opvolgende sessieID. In de cookie op de client wordt dan deze ID-code en de sessieID opgeslagen. Voordeel is dat de ID-code een klein getalletje is dat veel sneller opgezocht kan worden in de tabel (lijkt me). Vervolgens kan de bijbehorende sessieID in de tabel vergeleken worden met die in de cookie.
- user bezoekt page X
- server haalt sessieID op uit cookie
- server haalt record op uit sessie-tabel mbv de sessieID
- server controleert of sessieIDs klopt en of IP overeen komt
- is IP plots gewisseld, dan is er de mogelijkheid dat een session ge-hijacked is
En dan? Hoe vaak komt het voor dat users gebruik maken van proxy’s die steeds van IP wisselen? Ik neem aan dat ik dan de FORWARDED_FOR header uit kan lezen om dat probleem te omzeilen?
Verder is er nog een uitgebreid rechtensysteem dat hier verder weinig toelichting nodig heeft omdat het er niet zoveel mee te maken heeft. Het gaat meer om de verificatie van een user.
Voor de duidelijkheid; de website gebruikt een soort throttling-systeem dat automatisch overschakeld naar hogere beveiligingsniveaus (kan ik instellen per groep waar gebruiker toe behoort). De genoemde beveiliging is uiteraard voor groepen met zeer veel rechten. Ik wil bovendien nog een feature toevoegen om de client steeds een vooraf afgesproken stringetje mee te laten sturen die op de server gevalideerd kan worden. Dit stringetje is dan vergelijkbaar met telebankieren bij bijv de postbank waarbij je een TAN-code moet opgeven bij het uitvoeren van acties (alleen gaat het hier automatisch). De server stuurt bijvoorbeeld een random string op naar de client die in X stukken geknipt wordt waarvan steeds een stukje teruggestuurd wordt. Alle stukken op? Dan stuurt de server een nieuwe random string.
Wat zie ik hier over het hoofd? Is het veilig en welke gaten zitten er eventueel in? Zijn er verder nog tips of dingen die ik kan gebruiken om het geheel te verbeteren?
- user voert usernaam in op login1.asp
- client stuurt username naar ASP-script op server
- server controleert of user voorkomt en bepaalt ID van user
- server genereert random string van 40 karakters
- server redirect user naar login2.asp met de random string en userID in de URL
- user voert password in op login2.asp
- client maakt hash van ingevoerde password samen met random string
- client stuurt resultaat, samen met userID en de random string, naar de server
- server haalt password op op basis van userID
- server gebruikt random string en password uit DB om een hash te produceren
- server controleert op client gecreerde hash met op server gecreerde hash
- indien OK: user heeft correct password ingevoerd
Welk algorithme kan ik het beste gebruiken voor het maken van de hash? Op dit moment heb ik een SHA-1 implementatie voor ASP en Jscript geschreven die lekker snel zijn. MD5 lijkt me echter ook voldoende. Is het snelheidsverschil erg groot tussen deze twee?
Nu komen we bij de volgende stap. Ik wil een eigen sessie-systeem ontwikkelen. Dat zou ongeveer zo moeten gaan:
- [ user is net gevalideerd ]
- server maakt sessieID van 40/60 karakters
- server slaat sessieID, userID en IP op in een sessietabel
- server maakt cookie aan bij user met daarin de sessieID
En hier komt een vraagje tussendoor. Op dit moment werk ik met Access, maar het systeem gaat uiteindelijk op SQL-Server 2000 EP draaien. Nu is de sessieID een string van 40 of 60 karakters. Ik kan die kolom de primary key maken, maar dan loop ik het (minimale en bijna verwaarloosbare) risico dat er een kere een sessieID gegenereerd wordt die al bestaat. Het probleem, lijkt mij, is eerder de grootte van de key. Het is nogal een flinke string, en als de server iedere keer in een tabel van X records de juiste ID op moet zoeken met zo’n joekel van een string…. (of werkt een index dat tegen?). Anyways. Het lijkt me makkelijker om een autonummer-kolom op te nemen die een ID-nummertje maakt voor iedere opvolgende sessieID. In de cookie op de client wordt dan deze ID-code en de sessieID opgeslagen. Voordeel is dat de ID-code een klein getalletje is dat veel sneller opgezocht kan worden in de tabel (lijkt me). Vervolgens kan de bijbehorende sessieID in de tabel vergeleken worden met die in de cookie.
- user bezoekt page X
- server haalt sessieID op uit cookie
- server haalt record op uit sessie-tabel mbv de sessieID
- server controleert of sessieIDs klopt en of IP overeen komt
- is IP plots gewisseld, dan is er de mogelijkheid dat een session ge-hijacked is
En dan? Hoe vaak komt het voor dat users gebruik maken van proxy’s die steeds van IP wisselen? Ik neem aan dat ik dan de FORWARDED_FOR header uit kan lezen om dat probleem te omzeilen?
Verder is er nog een uitgebreid rechtensysteem dat hier verder weinig toelichting nodig heeft omdat het er niet zoveel mee te maken heeft. Het gaat meer om de verificatie van een user.
Voor de duidelijkheid; de website gebruikt een soort throttling-systeem dat automatisch overschakeld naar hogere beveiligingsniveaus (kan ik instellen per groep waar gebruiker toe behoort). De genoemde beveiliging is uiteraard voor groepen met zeer veel rechten. Ik wil bovendien nog een feature toevoegen om de client steeds een vooraf afgesproken stringetje mee te laten sturen die op de server gevalideerd kan worden. Dit stringetje is dan vergelijkbaar met telebankieren bij bijv de postbank waarbij je een TAN-code moet opgeven bij het uitvoeren van acties (alleen gaat het hier automatisch). De server stuurt bijvoorbeeld een random string op naar de client die in X stukken geknipt wordt waarvan steeds een stukje teruggestuurd wordt. Alle stukken op? Dan stuurt de server een nieuwe random string.
Wat zie ik hier over het hoofd? Is het veilig en welke gaten zitten er eventueel in? Zijn er verder nog tips of dingen die ik kan gebruiken om het geheel te verbeteren?