Blog
Czy Twoje hasło wyciekło? Część praktyczna.
Wojciech Stróżyński 08.04.2019
W poprzednim artykule poznaliśmy podstawy pomagające zrozumieć dzisiejsze zadanie praktyczne. Poznaliśmy nawet wstępnie dokumentacje api strony https://haveibeenpwned.com dostępną o TUTAJ. Zachęcam do przeczytania poprzedniej części zanim zacznie się wykonywać zadanie praktyczne z tej części.
API – spróbujmy go użyć
Na początek zobaczmy jak działa udostępnione API. Sprawdźmy bazę pod kątem hasła „password”. Suma skrótu SHA-1 (taka funkcja jest wykorzystywana przez tę stronę) wynosi: 5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8.
Teraz tylko wystarczy wziąć pięć pierwszych znaków sumy i uformować odpowiednie zapytanie zgodne z dokumentacją API. Będzie ono wyglądało tak:
https://api.pwnedpasswords.com/range/5BAA6
Możesz śmiało wpisać ten adres w przeglądarkę i zobaczyć, co zwróci serwer. Swoją drogą – w taki sposób komunikują się aplikacje i serwisy internetowe za Twoimi plecami – po prostu wywołują takie dziwne adresy. W dużym uproszczeniu oczywiście 😉
Sprawdźmy jak wyglądają dane, które dostaliśmy:
...
1D2DA4053E34E76F6576ED1DA63134B5E2A:2
1D72CD07550416C216D8AD296BF5C0AE8E0:10
1E2AAA439972480CEC7F16C795BBB429372:1
1E3687A61BFCE35F69B7408158101C8E414:1
1E4C9B93F3F0682250B6CF8331B7EE68FD8:3645804
1F2B668E8AABEF1C59E9EC6F82E3F3CD786:1
20597F5AC10A2F67701B4AD1D3A09F72250:3
20AEBCE40E55EDA1CE07D175EC293150A7E:1
...
W odpowiedzi dostajemy tylko pozostałe znaki z wartości funkcji skrótu – nie ma sensu powtarzać początkowych. Po dwukropku znajduje się informacja o tym, ile razy hasło z którego została wygenerowana wartość znalazło się w danych, które wyciekły. Szukamy więc w tych danych ciągu pasującego do naszego porównując pozostałe znaki. W zbiorach tak małych jak ten wycinek w ramce wystarczy porównać tylko trzy ostatnie znaki – powinny być wystarczająco unikalne. Z drugiej strony już w tym momencie jesteśmy w stanie powiedzieć, która wartość należy do hasła „password”. Tak. Ta, która wyciekła ponad 3.5 miliona razy. To jasna informacja o tym, jak słabe jest to hasło, ale też o tym, jak bardzo niebezpiecznie popularne jest to hasło. Niepokojąco popularne.
Jak widać, samo API jest bardzo proste, ale jednak nawet ręczne przeszukanie bazy około 550 wartości może nam zająć chwilę. A przecież jest tyle haseł do sprawdzenia, bo jesteście świadomi niebezpieczeństw i w każdym serwisie używacie innego hasła. Prawda?
Dziś zamiast przeglądać zdjęcia śmiesznych kotów zautomatyzujemy sobie pracę, bo jednak mniej czasu zajmie napisanie takiego skryptu, niż sprawdzenie haseł całej rodziny na piechotę. A jeśli bardziej opłaca się automatyzować pracę, to trzeba to zrobić jak najszybciej!
Python w dłoń i do dzieła!
Jeśli nie masz środowiska Pythonowego, to na końcu tej części znajdziesz link do strony, na której jest cały kod napisany dziś, z możliwością uruchomienia go z konsoli. Najlepiej jednak na spokojnie przepisać ten kod samemu. Zaznaczam jednak, że ten kod nie jest w żaden sposób gotowy do bycia kodem produkcyjnym. Nie sprawdzam w nim praktycznie żadnych wyjątkowych sytuacji. Chodzi o napisanie bardzo prostego kodu do zautomatyzowania pewnej operacji. Jeśli chcesz użyć mojego skryptu na replit i wpisywać tam swoje prawdziwe hasło, to weź proszę pod uwagę, że nie mam pojęcia gdzie jest ta maszyna, pod czyją jest kontrolą i jakie dane gromadzi. Dla testów możesz tam sprawdzać hasła w stylu „password123”. Pod żadnym pozorem nie sprawdzaj tam jednak swojego prawdziwego hasła. To możesz robić tylko na swoim komputerze 🙂
Co będzie nam potrzebne? Hashlib, oraz Requests. Pierwsza powinna być już w Twoim systemie (jeśli masz zainstalowane środowisko do wykonywania skryptów Pythona), a drugą możemy pobrać korzystając z PIP. Poniżej kod do wklejenia w terminalu Linuksowym. Jeśli jeszcze nie mamy PIP możemy go zainstalować. Przyda się na później.
sudo apt install python3-pip
A następnie za pomocą PIP możemy pobrać wymaganą bibliotekę:
sudo pip3 install requests
Konwersja do SHA-1
Zacznijmy od pobrania hasła. Zdecydowałem, że najbardziej naturalne będzie pobranie go z pierwszego argumentu wywołania. Czyli nasz skrypt będziemy wywoływali o tak:
python3 main.py [hasło]
Poniżej znajdziesz kod sprawdzający liczbę argumentów, oraz pobierający ten z numerem [1]. Argument [0] zawsze zawiera sposób wywołania naszego programu/skryptu.
import sys
import hashlib
import requests
if len(sys.argv) != 2:
print("Usage: {0} [password]".format(sys.argv[0]))
password = sys.argv[1]
print("Passowrd: {0}".format(password))
W tym momencie w zmiennej password mamy już nasze hasło. Dla pewności wyświetlimy je na ekranie. Wyliczmy teraz jego funkcję skrótu korzystając z hashlib:
sha1 = hashlib.sha1(password.encode('utf-8'))
sha1_text = sha1.hexdigest().upper()
print("SHA-1: {0}".format(sha1_text))
Tutaj szybkie wyjaśnienie – musimy zakodować hasło w utf-8, bo takie jest wymaganie hashlib.
Wywołanie hexdigest() konwertuje hash do postaci znośnej dla człowieka – ciągu znaków a nie bajtów. Natomiast upper() powoduje, że do reprezentacji zostaną użyte tylko wielkie litery. Dalej oczywiście po prostu wyświetlamy wartość funkcji skrótu dla podanego hasła.
Następnym krokiem będzie przygotowanie danych, które będziemy potrzebowali do realizacji zapytania. Podzielimy więc wartość na head (5 pierwszych znaków), oraz tail (pozostałe znaki).
head = sha1_text[:5]
tail = sha1_text[5:]
print('Head {0}, tail: {1}'.format(head, tail))
Formowanie zapytania i analiza
Teraz możemy już uformować zapytanie i je wywołać
url = 'https://api.pwnedpasswords.com/range/' + head
res = requests.get(url)
if res.status_code != 200:
raise RuntimeError("Could not connect!")
Zmienna url zawiera zapytanie, które będziemy wysyłać.
Zmienna res zawiera pełną odpowiedź uzyskaną od serwera. Badając zmienną res sprawdzamy, czy zapytanie się udało. Kod 200 oznacza, że wszystko poszło dobrze. Inne znane kody to 404 – nieprawidłowy link, albo 500 – błąd serwera.
Naszym kolejnym zadaniem będzie sprawdzenie, czy w uzyskanych danych znajduje się tail wartości funkcji skrótu. Zrobimy to w sposób „wygodny” a nie wydajny. Zrobimy sobie słownik zawierający otrzymane wartości jako klucze a liczbę wycieków jako wartość. Spróbujmy:
hashmap = {}
for line in res.text.splitlines():
parts = line.split(":")
hashmap[parts[0]] = parts[1]
Dla każdej linii znajdującej się w odpowiedzi dokonujemy podziału na dwa elementy. Zmienna parts[0] zawiera tail otrzymany z serwera a parts[1] zawiera liczbę wycieków powiązanego z nim hasła. Teraz dodajemy te elementy do słownika (to taki kontener wiążący klucz z wartością ;-)). Dzięki temu będziemy mogli w prosty sposób sprawdzić, czy nasz słownik zawiera tail należący do wartości wyliczonej z naszego hasła. Jeśli tak, to możemy od razu odczytać liczbę wycieków. Jeśli nie, to jesteśmy bezpieczni 🙂
if tail in hashmap:
print("Leaked ".format(hashmap[tail]))
else:
print("Password is safe! Nice! :)")
No i w sumie tyle. Tak jak wspominałem wcześniej, to nie jest kod produkcyjny. To jest tylko przykład tego, jak można sobie zautomatyzować pracę nie tylko w biurze, ale też w domu. Jeśli robimy jakieś powtarzalne czynności (wyciągamy dane z dokumentów na przykład) to prawdopodobnie będziemy w stanie nasze działania zautomatyzować we własnym zakresie pisząc prosty skrypt w Pythonie. Do czego serdecznie zachęcam. Mój kod można też swobodnie modyfikować, bawić się nim, zmieniać i publikować po swoich zmianach. Do czego też zachęcam. Każda wrzuta na gitgubie pomoże Ci znaleźć nową pracę 🙂 Albo postawić pierwsze kroki w IT.
Całe rozwiązanie można znaleźć na Replit. Najlepiej jest wcisnąć F1 i wybrać polecenie Open Shell. Otworzy się małe okienko z terminalem Linkusowym. Tam wystarczy wpisać:
python3 main.py [hasło]
python3 main.py password123
I już wszystko powinno działać.
Moje hasło wyciekło! Co robić? Jak żyć?
Przede wszystkim nie wpadać w panikę. Najlepiej szybko zmienić hasło na bardziej bezpieczne – dłuższe i zawierające dziwne znaczki – na przykład: @DNYouHaveJustMadeMyDay!
Żartuje. Na powyższe już za późno, bo ja je używam. Jest jednak sporo innych możliwości 😉
Warto też przyjrzeć się swojemu codziennemu życiu i znaleźć miejsca, które prosty skrypt może zautomatyzować naszą pracę. Od tego są komputery 🙂