import random import hashlib def generoiSalasana(y): mjono ="" allM = "qwertyuiopasdfghjklzxcvbnm!#%&QWERTYUIOPASDFGHJKLZXCVBNM123456789" for x in range(y): mjono+=allM[random.randrange(0,len(allM))] return mjono def hashattu(mjono): return hashlib.sha512( str(mjono).encode("utf-8") ).hexdigest() print (generoiSalasana(16)) print (hashattu(generoiSalasana(16)))
Jos verkkopalvelu generoi käyttäjälle automaattisesti salasanan käyttäjätunnuksen luonnin yhteydessä, eikä anna mahdollisuutta vaihtaa salasanaa, vaan sähköpostiin lähetetty resetointisalasana generoi uuden, onko tässä muuta tarvetta ihmeemmin validoinnille, jos kirjautumislomakkeen salasana-kentän max-value -arvoksi määrittää tuon salasanan pituuden, eli tässä esimerkkikoodissani 16.
Jos tuon SHA512-cryptatun salasanan vie tietokantaan, ja lomakkeisiin vielä ohjelmoi, että haittakoodit eivät mene läpi, niin eikö se aika turvallista ole poislukien, että joku resetoisi jonkun toisen käyttäjän salasanan ja lukisi tämän sähköpostista resetointilinkin ja resetoisi sen sitä kautta?
Tuossa ylemmässä koodissa vain tuo hashattu ja alkuperäinen generoitu salasana ei ole sama. Seuraavassa lopullinen koodi, jossa salasanan generointimetodi palauttaa taulukossa molemmat, jolloin ne ovat samat.
import random import hashlib def generoiSalasana(y): mjono ="" arr = [] ins = "qwertyuiopasdfghjklzxcvbnm!#%&QWERTYUIOPASDFGHJKLZXCVBNM123456789" for x in range(y): mjono+=ins[random.randrange(0,len(ins))] arr.append(mjono) arr.append(hashlib.sha512( str(mjono).encode("utf-8") ).hexdigest()) return arr print (generoiSalasana(16))
Tallenna tietokantaan NULL-arvo, jos salasanaa ei syötetä rekisteröitymisen yhteydessä. Tyhjänpäiväisten salasanojen generointi kantaan on aika outo ratkaisu.
Salasanan enimmäispituutta ei tule koskaan rajoittaa eikä varsinkaan 16 merkkiin. Totta kai aivan absurdit syötteet (lue: mahdolliset hyökkäysyritykset) voi silti seuloa pois rajoittamalla salasanan pituuden vaikka 100, 500 tai 1000 merkkiin.
muuskanuikku kirjoitti:
Tallenna tietokantaan NULL-arvo, jos salasanaa ei syötetä rekisteröitymisen yhteydessä. Tyhjänpäiväisten salasanojen generointi kantaan on aika outo ratkaisu.
Ei sinne nyt ihan NULLiakaan voi syottaa, kun todennuksessa pääsääntoisesti kolmea eri tyyppiä niitä, millä tunnistautuminen tapahtuu
1.jotain, jonka vain tämä käyttäjä tietää, kuten salasana, PIN-koodi tai salausavain
2.jotain, jonka vain tämä käyttäjä omistaa, kuten sähkopostiosoite
3.jotain, jonka vain tämä käyttäjä osaa
Metkaa, että www:n historian alusta lähtien verkkopalveluissa pääsääntoisesti vieläkin käytossä tuo käyttäjätunnus/salasana-yhdistelmä täällä ohjelmointiputkassakin on yksivaiheinen tunnistautuminen, joku tuollainen lohkoketjusysteemiin perustuva autentikointi-järjestelmä voisi olla tulevaisuudessa se, johon on siirtyminen meillä kaikilla ehkä, kunhan ne nyt kehittyvät ensin niin varmaan jokin suuri teknologiayhtio maailmalla ottaa käyttoon sellaisen ensimmäisenä. Ennen pitkää verkkopalveluiden tarjoajien pitää ottaa kantaa tuohon käyttäjätunnus/salasana -systeemiin, kun se ei ole mitenkään turvallinen pääsääntoisesti.
Googlella ja Facebookilla on monella muullakin kriittisemmällä palvelulla kaksivaiheinen tunnistautuminen mahdollista ottaa käyttoon, ja rahalaitokset ovat ainoita, missä olen tormännyt Suomessa kolmivaiheiseen tunnistautumiseen.
Voisi olla mielekäs harjoitus pohtia tuollaista turvallista kaksivaiheista tunnistautumista ja sen ohjelmointia ja käytännon implementointia.
Jos tietokantaan tallentaa pelkän NULLin, kuten totesit, niin sittenhän sisään pääsee kuka tahansa, ketä tietää jonkun toisen käyttäjätunnuksen, eihän se niin voi olla. Kyllä sinne nyt jonkin SHA-512 bittisen cryptatun tokenin tai samalla salausalgoritmilla salatun merkkijonon voisi viedä. Jos vielä salasanan pituus pakotetaan olemaan tarpeeksi kookas, niin vaikka joskus joku saisikin HASHit tietokannasta, ei välttämättä ihan heti saa auki kaikkien käyttäjien salasanoja selkokieliseksi.
Sitten vielä randomin suolan jos lisää salasanaan, josta käyttäjän ei tarvitse tietää mitään, niin sekin parantaa turvallisuutta.
Tässä on aika monta ongelmaa.
1. Käyttämäsi sha512 (eli 512-bittinen SHA-2) sellaisenaan ei ole sopiva algoritmi salasanan käsittelyyn. Se on idealtaan nopea tiivistealgoritmi, jolloin jos hyökkääjä saa tiivisteitä tietoonsa, niitä voi yrittää murtaa kokeilemalla sopivalla laitteistolla. Salasanan käsittelyssä yleensä käytetään murtovarmuuden parantamiseksi tarkoituksella hidasta algoritmia kuten Bcrypt tai Argon2.
2. Salasanan tiivisteen laskentaan tulee käyttää myös ns. suolaa eli satunnaista (käyttäjäkohtaista) mutta salasanasta erillistä tietoa. Tämän tarkoitus on, että jos jälleen hyökkääjä saa tiivisteitä tietoonsa, niistä ei pysty tunnistamaan, onko käyttäjillä keskenään sama salasana, eikä yhden murtaminen auta muiden murtamisessa. Yleensä salasanoille sopivissa algoritmeissa on myös suolan käyttö kätevästi mukana.
3. Salasanan arpomiseen ei pidä käyttää tavallisia ennustettavia satunnaislukugeneraattoreita. Pythonin dokumentaatiokin kertoo tämän random-moduulista: ”The pseudo-random generators of this module should not be used for security purposes.” Ongelma on se, että sopivissa olosuhteissa hyökkääjä voi saada selville generaattorin siemenluvun tai ylipäänsä voi kokeilla vain niitä salasanoja, jotka kyseinen satunnaislukugeneraattori tuottaa eri siemenluvuilla. Yksi realistinen esimerkki on vaikkapa se, että joku kopioisi tuon koodisi ohjelmaan, jossa sattumoisin asetetaan siemenluku vakioksi jonkin muun käyttötarkoituksen vuoksi. Eli salasanan yhteydessä pitää ehdottomasti käyttää kryptografisesti toimivaa satunnaislukugeneraattoria, Pythonissa yksinkertaisimmillaan os.urandom tai secrets-moduuli.
4. Generoidussa salasanassa ei ole syytä käyttää erikoismerkkejä tai ylipäänsä mitään käyttäjälle epäselvää. Generoitu salasana voi aivan hyvin sisältää pelkkiä pieniä kirjaimia, jolloin käyttäjän on helppo lukea ja hahmottaa salasana. Kannattaa mieluummin lisätä pituutta kuin outoja merkkejä. Salasanaan mahtuvan satunnaisuuden voi laskea helposti: 16-merkkinen salasana 65 merkin aakkosilla on vähemmän satunnainen kuin 21-merkkinen salasana 26 merkin aakkosilla (abcdefghijklmnopqrstuvwxyz), ja jälkimmäisestä ei tarvitse soitella tekniseen tukeen, että olikohan tässä o, O vai 0. Voi miettiä, onko noin pitkä satunnainen salasana edes tarpeen vai riittäisikö 10–15 merkkiä kuitenkin.
5. Salasanan tarkastuksen yhteydessä ei pidä olla mitään validointia salasanan sisällöstä, ja pituuden voi rajoittaa selvien hyökkäysten estämiseksi vaikka 500 merkkiin, kuten muuskanuikku esitti.
6. Salasana olisi yleensä hyvä pystyä vaihtamaan itse. Jos haluaa yrittää parantaa tietoturvaa, salasanan sisältöä voi juuri ja juuri rajoittaa esim. estämällä käyttäjätunnuksen tai sähköpostiosoitteen käytön salasanan osana, mutta muuten sisältöä ja pituutta ei pidä rajoittaa tai ”validoida”. Erilaisia arvioita voi esittää käyttäjälle siitä, onko salasana vahvan oloinen, mutta tämä on viime kädessä arvailua. Esimerkiksi erikoismerkkien ja numeroiden käytöstä ei ole selvää hyötyä, vaan niillä on valitettavasti opetettu käyttäjät keksimään salasanoja, jotka ovat vaikeita käyttäjälle mutta viime kädessä yhtä helppoja tietokoneella murrettaviksi. Jos varsinainen kirjallisuus aiheesta ei kiinnosta, aihetta käsittelee mm. tämä sarjakuva.
Kohdat 1 ja 2 toki ovat hieman vähemmän ongelmallisia generoitujen salasanojen kanssa, mutta kannattaa silti tehdä asiat oikein, jos ei ole selviä perusteita tehdä muulla tavalla. Esimerkiksi jos myöhemmin palveluun tuleekin mahdollisuus käyttää omaa salasanaa, joko (a) pitää muistaa kirjoittaa uudestaan salasanojen käsittely tai (b) systeemiin jää tietoturva-aukko, kun myöhempi koodari olettaa, että homma on varmaan jo alunperin tehty fiksusti.
Eli jos nyt pitäisi arpoa väliaikainen salasana, sen voisi tehdä esimerkiksi näin:
import math, secrets def generoi_salasana(merkit = "abcdefghijklmnopqrstuvwxyz", bittejä = 64): pituus = math.ceil(bittejä / math.log(len(merkit), 2)) return "".join(secrets.choice(merkit) for i in range(pituus))
Tiivistealgoritmin toteutukseen Pythonilla en ota kantaa, mutta varmasti löytyy valmiita kirjastoja.
Jere Sumell kirjoitti:
Jos tietokantaan tallentaa pelkän NULLin,
Varmaan muuskanuikku ajatteli, että käyttäjä voisi kuitenkin itse vaihtaa salasanan, kuten yleensä palveluissa toimitaan. NULL tarkoittaisi sitä, että salasanaa ei ole asetettu eli tilillä ei voi kirjautua. Myös täällä asetetaan NULL tiivisteeksi, kun käyttäjältä puuttuu salasana, ja silloin ei voi kirjautua, koska minkään salasanan oikea tiiviste ei ole NULL.
Jere Sumell kirjoitti:
muuskanuikku kirjoitti:
Tallenna tietokantaan NULL-arvo, jos salasanaa ei syötetä rekisteröitymisen yhteydessä. Tyhjänpäiväisten salasanojen generointi kantaan on aika outo ratkaisu.
Ei sinne nyt ihan NULLiakaan voi syottaa, kun todennuksessa pääsääntoisesti kolmea eri tyyppiä niitä, millä tunnistautuminen tapahtuu
1.jotain, jonka vain tämä käyttäjä tietää, kuten salasana, PIN-koodi tai salausavain
2.jotain, jonka vain tämä käyttäjä omistaa, kuten sähkopostiosoite
3.jotain, jonka vain tämä käyttäjä osaa
Höpö höpö. Itse sanoit että käyttäjälle laitetaan reset-linkki. Salainen osa on tällöin siinä linkissä, ja palvelimella se tallennetaan kannassa eri paikkaan kuin käyttäjän salasana. Salainen reset-linkki kuuluu tuohon kolmanteen kategoriaan.
Metabolix kirjoitti:
Jere Sumell kirjoitti:
Jos tietokantaan tallentaa pelkän NULLin,
Varmaan muuskanuikku ajatteli, että käyttäjä voisi kuitenkin itse vaihtaa salasanan, kuten yleensä palveluissa toimitaan. NULL tarkoittaisi sitä, että salasanaa ei ole asetettu eli tilillä ei voi kirjautua. Myös täällä asetetaan NULL tiivisteeksi, kun käyttäjältä puuttuu salasana, ja silloin ei voi kirjautua, koska minkään salasanan oikea tiiviste ei ole NULL.
Voi siihen tallentaa myös tyhjän merkkijonon tai vaikka sanan "kissa". Mikään näistäkään ei ole kelvollinen tiiviste eli ei aiheuta tietoturvariskiä salasanan vuotamisen muodossa. NULL-arvo on kaikista loogisin, koska se on selkeä ilmoitus siitä, ettei salasanaa ole tallennettu lainkaan.
Kiitos Metabolix valaisevasta vastauksesta!
Tuota bcryptia pythonissa voisi alkaa käyttämäänkin, siinähän on metodit suolan ja hashauksen lisäämiseksi ja metodi sellaisen salasanan tarkistukseen, jossa on suola.
Varmaankin järkevintä olisi, että käyttäjä saisi itse valikoida heti alkuun jonkin salasanan, ja tosiaan jotain rajoituksia tai vaatimuksia voisi tehdä salasanan suhteen. Tämä, että käyttäjä saa itse määritellä salasanan, on perinteisempi ratkaisu, ja helpommin myos muistettava käyttäjän toimesta.
Metabolix kirjoitti:
import math, secrets def generoi_salasana(merkit = "abcdefghijklmnopqrstuvwxyz", bittejä = 64): pituus = math.ceil(bittejä / math.log(len(merkit), 2)) return "".join(secrets.choice(merkit) for i in range(pituus))
Tuo vaikuttaa järkevämmältä satunnaisen salasanan luonti-metodilta, mitä omani, ja mielestäni argumentoit ihan mielekkäästi ratkaisusi.
pitänee ottaa selvää, onko tuo bcrypt -erillinen kirjasto, joka pitää ensin asentaa, jos sitä hyodyntaisi.
Aihe on jo aika vanha, joten et voi enää vastata siihen.