Książki, Biblionetka, BookWyrm

Od zawsze korzystam z Biblionetki, dziś przeczytałem wpis o BookWyrm. Przypomniał mi o moich skomplikowanych relacjach z Biblionetką. Serwisu używam od zawsze, ale nie raz myślałem o migracji. Dawno temu serwis co prawda żył, ale wyrosła mu międzynarodowa konkurencja w postaci Goodreads. Wyglądał na nierozwijany programistycznie, a międzynarodowa konkurencja w social media raczej nie służy serwisom lokalnym. Patrz Nasza Klasa.

Potem Biblionetka została odświeżona, ponadto nie jest to serwis krytyczny z mojego punktu widzenia, dodatkowo okazało się, że na Goodreads brakuje wielu pozycji, które były na Biblionetce. Nie zmigrowałem więc. Stwierdziłem, że poczekam.

Poczekałem nieco dłużej, niż planowałem i… okazało się, że Goodreads likwiduje API. I w tym momencie gdybym korzystał z Goodreads, to rozważałbym ucieczkę. Albo raczej szukał miejsca, do którego się przenieść. Nie żeby Biblionetka miała API. Ale da się skorzystać z jej danych, co pokazuje choćby serwis do korelacji, o którym pisałem w jaką książkę warto przeczytać. Nie ma API, ale rozwiązanie 3rd party działa po niemal dekadzie. Szacun.

Tymczasem wpis o BookWyrm wspomina o możliwości importu danych z innych serwisów. Na przykład z Goodreads. O Biblionetce nic nie ma, ale nie dziwi mnie to. Stwierdziłem, że w sumie może warto robić backup ocen. Albo mieć skrypt, który zescrapuje dane ze strony i zwróci formę łatwą do parsowania.

Nie mam złudzeń, ze względu na różnice językowe to rozwiązanie nie wystarczy do eksportu do BookWyrm. Ale na pewno taką migrację ułatwi. Autor albo nie będzie wymagał interwencji, albo co najwyżej wystarczy zmapować raz. Przypuszczam, że autor plus automatyczne tłumaczenie tytułu pozwoli w wielu przypadkach na określenie tytułu.

Tak czy inaczej powstał taki oto scrapper Biblionetki. I jeśli ktoś przypuszcza, że można w ten sposób pobrać oceny wszystkich użytkowników, to prawdopodobnie ma rację. Publikuję, bo podstawowe, założone użycie to backup własnych ocen.

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.