Forumet - (Relativ) sessions- och loginsäkerhet utan SSL

(Relativ) sessions- och loginsäkerhet utan SSL

98 0 14
Jag sitter och knåpar på en CMS-/forummjukvara, och till skillnad från UM-teamet skulle jag vilja ha lite input på dess säkerhet. Det jag främst är intresserad av är login- och sessionssäkerhet.

Angående login

För att minimera risken för stulna loginuppgifter fungerar login lite annorlunda jämfört med de flesta interwebbsidor:
1. Klienten talar om för servern vilket användarnamn som ska användas.
2. Servern svarar med användarens salt, samt ett nonce, som dessutom lagras i sessionen.
3. Klienten skickar sha512(nonce + sha512(salt + lösenord))* till servern.
4. Servern utför själv samma beräkning, och tillåter login om hasharna matchar.
5. Ett nytt nonce genereras för varje loginförsök, misslyckat eller ej.

På så vis skickas inte lösenordet i klartext, vilket innebär att användare som kör samma lösenord på olika sajter kan sova lugnt.

Det spelar heller ingen roll om en angripare skulle fånga upp hashen, eftersom nonce ser till att varje loginhash är unik, även med samma lösenord.

Angående registrering/lösenordsbyte

1. Servern skickar genererar ett nytt salt åt klienten.
2. Klienten hashar lösenordet med saltet, och skickar tillbaka hashen och saltet.
3. Servern lagrar hashen och saltet, för användning vid framtida inloggningar.

Här blir vi tyvärr av med egenskapen att vi inte bryr oss om någon sniffar hashen; om en angripar får tag i denna hash kan han använda den för att logga in genom att skicka hash(nonce + sniffad_lösenordshash) vid steg 3 i inloggningsprocessen.

Dock skickas lösenordet fortfarande aldrig i klartext, och antalet tillfällen då en angripare kan snappa upp en användbar hash begränsas kraftigt (man loggar in rätt mycket oftare än man byter lösenord.)

Angående sessioner

All sessionsdata lagras på servern. Varje session identifieras med ett 128-bits sessions-ID som lagras hos klienten mha. en cookie. Vid varje request undersöker servern om klientens IP har ändrats, och avslutar isåfall omedelbart sessionen. Hade UM gjort detta hade den senaste hackerattacken inte kunnat inträffa.
Vid utloggning avslutas sessionen och all relaterad data rensas.
Användare som inte är inloggade loggas automatiskt in som användaren "Anonymous," med UID 0 och en given uppsättning standardrättigheter.

Adress & källkod

Sajten återfinns på http://www.weeaboo.se
Källkoden återfinns i ett GIT-träd på http://natsuki.weeaboo.se:8080/~valderman/fridomar-ng.git

Nu vill jag ha lite input och kommentarer på detta. Uppenbara fel med koncept/implementation? Saker som kan förbättras?

*: I själva verket sha512(nonce + sha512(nonce + sha512(salt + sha512(salt + lösenord)))) eftersom sha512(salt + text) bara ger hälften av den säkerhetsfaktor som väntas av en 512-bits hash. (se Schneier; Applied Cryptography för mer information)
KID_IS_BACK:

1. Klienten talar om för servern vilket användarnamn som ska användas.
2. Servern svarar med användarens salt, samt ett nonce, som dessutom lagras i sessionen.
3. Klienten skickar sha512(nonce + sha512(salt + lösenord))* till servern.
4. Servern utför själv samma beräkning, och tillåter login om hasharna matchar.
5. Ett nytt nonce genereras för varje loginförsök, misslyckat eller ej.


Nu är jag verkligen ingen säkerhetsexpert, men det låter ju som en väldigt bra metod. Varför använder sig inte fler inloggningssystem av det? komplicerat är det ju inte. Någon måste ju ha tänkt på det förut.

Av ren nyfikenhet, vad är det för skillnad på nonce och salt? Som jag uppfattat det så är enda skillnaden i ditt fall att varje nonce är unikt, medan saltet bara är unikt för varje användare?

Jag ska kika igenom källkoden när jag orkar. Jag tror att det kan vara rätt lärorikt.
Åtta:

Nu är jag verkligen ingen säkerhetsexpert, men det låter ju som en väldigt bra metod. Varför använder sig inte fler inloggningssystem av det? Så komplicerat är det ju inte. Någon måste ju ha tänkt på det förut.


Sannolikt för att det kräver en tvåstegsprocess - servern måste veta vilken användares salt som ska skickas. Detta sker i min implementation genom en XMLHTTPRequest, och själva hashningen sköts av JavaScript. Klienter som har JavaScript avaktiverat (Links, många mobila läsare, folk med NoScript) eller saknar ordentligt stöd för XMLHttpRequest (IE6 och äldre) måste antingen få en egen login metod, eller helt och hållet utelämnas från inloggandet. Det är också mer jobb bakom än att bara skicka namn + lösenord.

Åtta:

Så komplicerat är det ju inte. Någon måste ju ha tänkt på det förut.


Challenge-response-autentisering, brukar det kallas.

Åtta:

Av ren nyfikenhet, vad är det för skillnad på nonce och salt? Som jag uppfattat det så är enda skillnaden i ditt fall att varje nonce är unikt, medan saltet bara är unikt för varje användare?


Saltet är till för att en angripare som fått tag i databasen inte ska kunna använda regnbågstabeller, dvs. en färdigberäknad mappning av strängar till hashvärden, för att hitta folks lösenord. Att slå upp lösenord med regnbågstabeller går väldigt fort, men att generera dem tar väldigt lång tid. En angripare måste alltså generera en ny sådan tabell för varje användare som han vill knäcka.

Ett nonce, däremot, används för att göra kommunikationen unik; om en angripare fångar upp hash(salt + lösenord) kan han använda det för att logga in. Om han däremot fångar upp hash(nonce + hash(salt + lösenord)) kan han bara använda det för att logga in om exakt samma nonce skulle dyka upp igen. Sannolikheten att det ska inträffa är en på 2^255 med ett 256-bits nonce. Om du gör tusen miljarder loginförsök per sekund kommer du att ha lyckats med din fejklogin efter ungefär 4,4*10^58 år, dvs. sisådär en 2*10^47 år efter universums slut.

Spana också in:

KID_IS_BACK:

Detta sker i min implementation genom en XMLHTTPRequest, och själva hashningen sköts av JavaScript. Klienter som har JavaScript avaktiverat (Links, många mobila läsare, folk med NoScript) eller saknar ordentligt stöd för XMLHttpRequest (IE6 och äldre) måste antingen få en egen login metod, eller helt och hållet utelämnas från inloggandet.


Så mycket förstod jag också, och jag måste säga att jag blev mycket förvånad över att se det. Jag trodde att du hyste stort hat mot allt vad ajax heter? Varför använder du dig av javascript för att logga in ö.h.t.? Det innebär ju bara att du måste bygga två olika inloggningsmetoder för de som har javascript inaktiverat.

KID_IS_BACK:

För överskådlig dokumentation av koden kan du köra make doc och sedan titta på doc/haddock/index.html.


nevon@loltop:~/fridomar-ng$ make all
ghc -o rpc.cgi -static --make -O2 -isrc src/rpc.hs
make: ghc: Command not found
make: *** [rpc] Error 127


Saknar jag dependencies måhända? Du kan inte tänka dig att dela ut koden direkt, utan att man ska måsta använda sig av en makefile och krångla. Jag tänker mig en fin liten tarball med alla filer ready to go efter lite databaskonfigurering. [rolleyes]
Åtta:

Jag trodde att du hyste stort hat mot allt vad ajax heter? Varför använder du dig av javascript för att logga in ö.h.t.?


AJAX i onödan är satans verk. I vissa fall är det dock motiverat. Hur skulle du implementera GMail utan AJAX? Hur skulle du hasha lösenord tillsammans med salt och nonce hos klienten?

Åtta:

Det innebär ju bara att du måste bygga två olika inloggningsmetoder för de som har javascript inaktiverat.


Eller helt enkelt inte tillåta inloggning utan javascript. De mobila enheter som är tillräckligt avancerade för att det ska vara någon poäng att logga in med stöder javascript, IE6 och äldre är på utgående i vilket fall och den som är så paranoid att han inte vågar tillåta javascript att köras på en viss sida är sannolikt för paranoid för att skicka sitt lösenord till sidan i vilket fall.

Åtta:

Saknar jag dependencies måhända? Du kan inte tänka dig att dela ut koden direkt, utan att man ska måsta använda sig av en makefile och krångla. Jag tänker mig en fin liten tarball med alla filer ready to go efter lite databaskonfigurering. [rolleyes]


Det kan bli svårt. Alltihop är skrivet i Haskell och avsett att köras från kompilerade binärer (eftersom väldigt få webbhotell har en Haskelltolk installerad.)

Vill du ändå kompilera det behöver du följande:
GHC 6.8.2(ej testat med 6.8.3 eller 6.10)
Crypto
HSQL
HSQL-MySQL
regex-base
regex-pcre
CGI

Sannolikt är alla dessa inkluderade i Windowsinstallationen av Haskell. Har dock ej testat detta.

Kör du Ubuntu eller Debian kan du installera alltihop såhär:
Kör först:
sudo apt-get install ghc6 libghc6-{cgi,hsql,hsql-mysql,regex-base,xhtml}-dev


Därefter laddar du ned regex-pcre, som av någon anledning inte finns i Debians repos. Packa upp till valfri katalog, cd:a in i katalogen, och kör:
runghc Setup configure
runghc Setup build
sudo runghc Setup install


Därefter ska det inte vara några problem att kompilera.
KID_IS_BACK:

Hur skulle du hasha lösenord tillsammans med salt och nonce hos klienten?


Ah, sant. Jag tänkte inte på att poängen försvinner om man sköter det på serversidan. [rolleyes]

KID_IS_BACK:

Eller helt enkelt inte tillåta inloggning utan javascript.


Det tror jag inte är en så ruskigt bra idé. Det är förvånandsvärt många människor som inte använder sig av javascript, eller än värre, använder sig av IE6. Även om du personligen skiter i huruvida dessa människor kan logga in så är det inte säkert att den som eventuellt väljer att använda sig av ditt CMS håller med. Då skulle jag påstå att det är bättre att skapa en alternativinloggningsmetod, men utforma det så att administratören kan välja att aktivera eller inaktivera det utefter hur säker/tillgänglig den vill att sidan ska vara.

KID_IS_BACK:

Alltihop är skrivet i Haskell och avsett att köras från kompilerade binärer


Ah. Det förklarade saken. Jag blev mycket förvirrad när jag utforskade filerna i /src.
KID_IS_BACK:

Här blir vi tyvärr av med egenskapen att vi inte bryr oss om någon sniffar hashen


Nu är kryptografi inte min starka sida, men här är ett lite mer bloatat förslag som löser det problemet (och några andra mindre problem) med publik nyckel-kryptografi.

Registrering

1. Servern skickar sin publika nyckel och ett slumpmässigt salt åt klienten.
2. Klienten beräknar hash(användarnamn, lösenord, salt, publik nyckel)
3. Klienten krypterar hashen med serverns publika nyckel och skickar det tillsammans med användarnamnet till servern.
4. Servern dekrypterar med sin privata nyckel och sparar hashen och saltet för framtida användning.

Inloggning

1. Klienten talar om sitt användarnamn till servern.
2. Servern skickar sin publika nyckel, saltet för användarnamnet och en slumpmässig nonce åt klienten.
3. Klienten beräknar hash(nonce, hash(användarnamn, lösenord, salt, publik nyckel))
4. Servern gör samma beräkning och släpper in klienten om beräkningarna överensstämmer.

Här är det skickade meddelandet vid registrering fullkomligt värdelöst om man inte har den privata nyckeln, alltså kommer någon som sniffar komunikationen inte se hashen.

Det går inte heller att göra "man in the middle"-attacker. Om attackeraren skickar sin egen publika nyckel istället för serverns - för att kunna dekryptera hashen - så kommer också hashen ändras och bli värdelös.

Andra fördelar med denna metod:
* Användarnamnet är med i hashen, vilket förhindrar en elak server att ge samma salt till flera användare och köra kollektiv bruteforce på deras hashar.

* Den publika nyckeln gör med mycket stor sannolikhet hashar unika på olika servrar. Om man bara har ett publikt salt så skulle någon kunna sätta upp en elak server som ger ut samma salt som en välkänd sida (ex. ungdomar.se) och få reda på hashen på folk som använder samma användarnamn och lösenord som på den kända sidan. (lite långsökt, jag vet, men det är trots allt inte allt för ovanligt)
Herr_Älg:

Nu är kryptografi inte min starka sida, men här är ett lite mer bloatat förslag som löser det problemet (och några andra mindre problem) med publik nyckel-kryptografi.


Blir inte det nästan lite av en reimplementering av SSL in sin omfattning?

Åtta:

Det är förvånandsvärt många människor som inte använder sig av javascript


Att använda NoScript är ett beslut man fattar, och som sagt, är man så paranoid så tror jag inte man gärna skickar sitt lösenord i klartext heller.