PINy, hasła – less is more

Będzie o PINach, a ogólniej o hasłach, bo PIN to specyficzny rodzaj hasła. Less is more to w tym przypadku mniej ograniczeń przekładających się na większe bezpieczeństwo. Zaczęło się od wpisu na Twitterze gdzie pokazano ograniczenia nakładane na PIN w pewnej aplikacji.

Zastanowiło mnie, czy da się policzyć o ile takie ograniczenia zmniejszą bezpieczeństwo, rozumiane jako przestrzeń możliwych poprawnych kombinacji PINu.

Na wstępie widać, że pierwsza cyfra PINu musi być inna niż 0 oraz 1. Już sam ten warunek zmniejsza przestrzeń poprawnych PINów o 20%. I to niezależnie od długości PINu.

Tu moja matematyka wysiada i sięgam po symulacje. Skrypt w Pythonie, który sprawdza wszystkie czterocyfrowe kombinacje i podaje, czy PIN jest poprawny (True) czy błędny (False) może wyglądać tak:

def is_valid(a, b, c, d):
    if a < 2:
        return False
    if ((a - b) == (b - c) or (b - c) == (c - d)):
        return False
    if (a == b) or (b == c) or (c == d):
        return False
    if ((b - a > 0) and (c - b > 0) and ((d - c > 0)) or (b - a < 0) and (c - b < 0) and (d - c < 0)):
        return False
    return True


for a1 in range(0, 10):
    for a2 in range(0, 10):
        for a3 in range(0, 10):
            for a4 in range(0, 10):
                result = is_valid(a1, a2, a3, a4)
                print(a1, a2, a3, a4, result)
python piny.py | grep -c True
5120

Jak widać, dla PINów o czterech cyfrach, wprowadzone ograniczenia zmniejszają przestrzeń dozwolonych PINów aż o niemal połowę.

Po co te ograniczenia i jak to zrobić poprawnie?

I teraz o motywacji. Jestem pewnien, że była ona szczytna i chodziło o podniesienie bezpieczeństwa. Jestem prawie pewien, że twórcy chodziło o wyeliminowanie prostych, schematycznych PINów. 0000, 1111, 1234 itp. Jak widać, w praktyce konsekwencje były dalej idące.

Najlepiej biorąc pod uwagę statystyki najczęściej występujących PINów i tworząc krótką listę PINów zabronionych. Wg statystyk PIN 1234 odpowiada za ponad 10% wystąpień wśród używanych PINów. 20 najczęściej używanych PINów jest używanych w 26% przypadków. Taka lista podniesie bezpieczeństwo, rozumiane jako „wymuszenie mało popularnego PINu” równie skutecznie, co proponowane ograniczenia. Zaś bezpieczeństwo rozumiane jako „ilość możliwych kombinacji” zostanie zmniejszone jedynie minimalnie.

Przypadki brzegowe

Jeśli przypuszczamy, że nasi klienci mają znacząco różne preferencje niż te z powyższych statystyk, robi się trudniej. W ogólności najlepiej walidować hasła na obecność zabronionych ciągów już w momencie ustalania hasła przez użytkownika. Zabronić można nazwy firmy, zasobu do którego jest chroniony dostęp, roku, czy popularnych ciągów znaków.

Jeśli jednak tego nie zrobiliśmy zawczasu, nadal nie wszystko stracone, choć robi się nieco ślisko. Nie znamy przecież PINów, co najwyżej mamy dostęp do hashy, więc ciężko będzie określić, których używają nasi klienci. Na ratunek przychodzi hashcat. Niezależnie od użytej funkcji hashującej, brute force krótkich PINów trwa moment. Jeśli korzystamy z bezpiecznych, wolno liczonych hashy, a użytkowników jest wielu, nie ma potrzeby robienia brute force wszystkich. Wystarczy analiza próbki statystycznej. Oczywiście taki audyt to operacja bardzo delikatna, więc nie zabieramy się za to bez stosownych umocowań i zachowania właściwej higieny.

UPDATE: Wersja uogólniona skryptu poniżej. Ilość cyfr w PIN regulowana ilością powtórzeń alfabetu w funkcji product. Dla dłuższych PINów warto skorzystać z pypy, które potrafi być szybsze.

import itertools
alphabet = list(range(0, 10))

def is_valid(i):
    if i[0] < 2:
        return False

    for n in range(0, len(i)-2):
        if i[n] - i[n+1] == i[n+1] - i[n+2]:
            return False

    for n in range(0, len(i)-1):
        if i[n] == i[n+1]:
            return False

    monotonic = True
    for n in range(0, len(i)-2):
        if (i[n] - i[n+1]) * (i[n+1] - i[n+2]) < 0:
            return True
    if monotonic:
        return False

    return True

for i in itertools.product(alphabet, alphabet, alphabet, alphabet):
    result = is_valid(i)
    print(i, result)

Szczepienia

Szczepienia i rejestracja na nie to ostatnio modny temat. Przynajmniej w mojej bańce. A to ktoś się szczepi, a to szuka terminu, a to udało mu się znaleźć wcześniejszy. Nie bez znaczenia jest fakt, że obecnie codziennie jakieś roczniki zaczynają się kwalifikować do szczepienia. W stosunku do początków szczepień, gdy szczepiono tylko wybrane grupy oraz seniorów to duża zmiana.

Szczepienia
Źródło: Thumbnail.ai

Jak wygląda rejestracja przez internet? Można/trzeba wybrać województwo i termin, czyli zakres dat. Także czy punkt stacjonarny, czy mobilny. Duży szacun za możliwość wyboru szczepionki. Wszytko to da się zrobić online przy pomocy profilu zaufanego. Ale jest też możliwość rejestracji przez internet dla tych, którzy profilu nie mają (nie testowałem).

Tyle dobrego. Klikanie na stronie przypomina wyścig. Kumpel uprzedził mnie, że nie ma czasu na sprawdzanie kalendarza itp. Trzeba wiedzieć zawczasu i od razu wybierać. Wtedy może zdążymy. Jeśli klikniemy źle, to niby jest opcja rezygnacji i wybrania ponownie. Nie testowałem.

Biada tym, którzy mieszkają na skraju województw. Muszą sprawdzać każde województwo osobno. Poza tym, wybór województwa jest za szeroki. Jako zmotoryzowany nie mam problemu z podjechaniem nawet powiedzmy 50 km. Ale miejscowości, zwłaszcza mniejszych i czasów dojazdu nie znam na pamięć. Na sprawdzanie na mapie gdzie jest Pcim Dolny czasu, jak wspominałem, nie ma. Zdecydowanie brakuje mi opcji „punkty szczepień w promieniu do N km”.

Sporym ułatwieniem jest strona szczepienia.github.io. Niestety, dane są tam odświeżane stosunkowo rzadko i dotyczą tylko wybranych miast. To projekt amatorski i w pewnym momencie pobieranie danych na potrzeby strony zostało zablokowane. Z pomocą tej strony może nie znajdziemy punktu, ale korzystanie z niej pozwala się zorientować w dostępności i terminach w danym województwie czy mieście.

Ponadto, wyżej wymieniona strona się rozwija. W momencie pisania wpisu nie było takiej możliwości, a obecnie pozwala ona na wyszukanie punktów i terminów w zadanym promieniu (10, 25, 50 oraz 100 km).

W całym systemie brakuje mi też opcji „dyspozycyjny”. Typu: daj mi znać min. 4h wcześniej, a w obrębie mojego miasta po dany typ szczepionki podjadę. Tak, niektórzy są w stanie przeorganizować sobie dzień na tę godzinę czy dwie. Zresztą w ogóle nie ma opcji push, gdzie system sam proponuje najbliższy termin dla wybranych parametrów.

Niemniej, da się zarejestrować przez internet i nie potrzeba do tego specjalnej wiedzy. Mogłoby to być zrobione lepiej, ale jest całkiem używalne. Wg statystyk ze strony, w ostatnich siedmiu dniach wykonywanych jest niemal 220 tys. szczepień dziennie. Biorąc pod uwagę, że od początku programu wykonano jedynie 12,6 mln szczepień, to ogromy przyrost. I więcej, niż zapowiadane pierwotnie przez rząd 850 tys. tygodniowo. We wpisie z okazji rozpoczęcia szczepień odnotowałem ówczesne dane. Robiliśmy 208 tys. szczepień. Tyle, że tygodniowo, nie dziennie.

Na koniec garść porad dla tych, którzy będą się rejestrować.

Przez panel nie da się zrobić wszystkiego tego, co potrafią obsługujący infolinię. W szczególności przyspieszyć terminu bez wcześniejszej rezygnacji z wybranego. Przydatne zwłaszcza dla tych mniej mobilnych, przywiązanych do miasta. Wystarczy wybrać jakiś termin w wybranej lokalizacji, a następnie parokrotnie spróbować znaleźć wcześniejszy. Można powtarzać.

Wcześniejsza rejestracja nie oznacza wcześniejszego terminu. W panelu pojawiają się nowe terminy, warto sprawdzać. Może być warto poczekać, bo coś się zwolni.

Jeśli komuś zależy na wcześniejszym terminie i konkretnej szczepionce, a jest mobilny, warto sprawdzić inne województwa. Różnice są znaczne. Jedyne o czym warto pamiętać w takiej sytuacji, to że będzie trzeba przyjechać w to samo miejsce także na drugą dawkę.

Pfizer i Moderna to praktycznie to samo. Ten sam typ szczepionki (mRNA), też dwie dawki, skuteczność taka sama.

UPDATE Dodatkowy akapit o szczepienia.github.io, rozszerzeone info o przyspierzaniu terminów.

hCaptcha na WordPress przeciwko spamowi

Jednym z powodów dla których umieściłem CAPTCHA[1] na blogu była chęć zmniejszenia ilości spamu w komentarzach. Dokładniej, ilości spamu do moderacji, bo i tak wszystkie komentarze przechodzą tu przez ręczną moderację, nim pojawią się na blogu.

Na początek drobne statystyki. WordPress pokazuje równe 230 komentarzy oznaczonych jako spam. Najstarszy z 24.10.2018. Do wczoraj[2] daje to 909 dni, czyli średnio ok. jednego spamu na cztery dni. Nie jest to dokładna statystyka, część mogłem kiedyś usunąć, zamiast oznaczyć jako spam. Zjawisko nie było też stałe w czasie. Mam wrażenie, że ostatnio się nasilało. Na pewno luty, marzec i kwiecień tego roku to większe ilości. Z kolei styczeń to tylko pięć spamów. Jeśli miałbym oceniać na oko, to stawiałbym bardziej średnio na spam co drugi dzień. Do przeżycia.

Rodzajów spamu też było kilka i pojawiał się falami. Były i polskie pseudokomentarze typu „ciekawy wpis” z typowym SEO linkiem, i komentarze pisane cyrylicą. Dominowały jednak anglojęzyczne reklamy środków viagropodobnych. Regularności we wpisach pod którymi zamieszczano komentarze praktycznie żadnej. Podobnie z IP wykorzystywanymi do wysyłki spamu. Na oko raczej stare wpisy, z różnych kategorii. 230 to nie jest duża próbka do analizy, ale może kiedyś zrobię statystyki.

W każdym razie w ostatnim czasie liczba spamów wzrosła. Dominował w zasadzie jeden IP: 92.204.174.134, we WHOIS mający powiązania z SEODEDIC. Zamieszczał nawet po trzy komentarze dziennie. Sprawdzenie logów serwera WWW pokazało, że po prostu wchodzi i wysyła komentarz. Żadnych wielokrotnych prób, ale niekoniecznie byłyby widoczne po stronie mojego serwera. Zatem ciężko stwierdzić czy któryś z serwisów do omijania CAPTCHA przy pomocy ludzi, czy sprytne metody typu machine learning do rozpoznawania obrazków[3] czy w końcu może sprytne wykorzystanie Google text to speech do obchodzenia CAPTCHA od Google.

Skoro spamerzy obchodzili reCAPTCHA, stwierdziłem, że to dobra okazja do wypróbowania alternatywy, o której ostatnio trochę było słychać. Chodzi o serwis hCaptcha. Rejestracja niezbyt gładka. A to mail na Onecie został uznany za nieprawdziwy adres email, a to były problemy z dostarczeniem maila z linkiem aktywacyjnym na inną skrzynkę. W końcu odnalazł się on w folderze spam.

Po aktywacji jest już z górki. Użyłem pluginu hCaptcha for WordPress, który pozwala na określenie, gdzie ma być serwowana CAPTCHA. Podajemy klucze API i… już. Przyznam, że kusiło mnie przez moment wypróbowanie używania obu pluginów jednocześnie. Szybko porzuciłem tę myśl. CAPTCHA jednak i jest nieco upierdliwym mechanizmem, i dokłada trochę objętości do wielkości strony.

No właśnie. W porównaniu z pierwotną wersją strona jest obecnie nieco cięższa. I główna, i strony poszczególnych wpisów. Dramatu nie ma, nad główną jeszcze popracuję, ale z kronikarskiego obowiązku odnotowuję.

Co dalej? Ano czekam na feedback od użytkowników jak się nowa CAPTCHA podoba. A jeśli spamer wróci z tego samego IP, to dostanie w łeb. Tarpitem. Jeśli i to nie pomoże, poszukam innych pluginów WordPress stworzonych, by zwalczać spam. No i przede wszystkim będę obserwował ilość spamu przychodzącego do moderacji.

[1] Dokładnie reCAPTCHA wraz z pluginem Advanced noCaptcha & invisible Captcha. Tutaj znajdziesz więcej o wykorzystywanych na tym blogu czy polecanych pluginach do WordPressa.
[2] Jeśli ktoś zada sobie trud policzenia, to wyjdzie nieścisłość matematyczna. I można policzyć ile dni wpis leżakował jako szkic.
[3] Tak, to akurat przeciwko hCaptcha, ale miałem pod ręką linka. Dla reCaptcha pewnie też coś analogicznego istnieje.