Social, czyli gra Elona

Kolejna „awaria” Twittera. Lub też skutek niefrasobliwego zarządzania, zależy jak spojrzeć. Co tym razem? Ludzie się skarżą, że nie mogą czytać postów. Dlaczego? Bo wprowadzono zmiany…

Główne zmiany to limit przeglądanych tweetów, w pierwszej wersji wynoszący 600 dla kont niezweryfikowanych. Czytaj: kont darmowych. Jeszcze mniej dla kont świeżych. Płacący dostają dziesięć razy więcej. Nie jest to jednak jedyna zmiana. Aktualnie nie można przeglądać wpisów na Twitterze, jeśli nie jest się zalogowanym. Oficjalny powód zmian? Scrapowanie treści. Znaczy czytanie. Ktoś umieścił coś w sieci, inni czytają, a właścicielowi platformy się to nie podoba.

Przypuszczam, że gdyby czytający płacili, problemu by nie było i właścicielowi by się podobało. Wydaje mi się bowiem, że Twitter dla Elona to taka zabawka, która służy do zabawy w grę „ile pieniędzy można wycisnąć z platformy”. I zupełnie nie mają znaczenia inne aspekty, typu wolność słowa, użyteczność platformy, czy liczba użytkowników.

Oczywiście zaczęło się wieszczenie „trzeciego końca Twittera”. Ludzie komentują kolejny wzrost rejestracji kont na Mastodonie. Przypuszczam, że sprawa za moment rozejdzie się po kościach. Elon pokręci gałeczką, z 600 zrobi się 1000, albo i trochę więcej. Jak poprzednio, część użytkowników odejdzie, część uzna, że warto zapłacić.

Uważam, że na Twitterze zmierzamy wprost, szybciej lub wolniej, do modelu z regularnym paywallem. Model znany i w Polsce choćby w wykonaniu Agory. Jakieś szczątkowe dostępy za darmo, a jak chcesz czytać, to płać. Jedyna różnica jest taka, że Agora płaci autorom, a na Twitterze ludzie będą płacili za dostęp do treści tworzonych przez znajomych. Niemożliwe? Nic podobnego, przecież to nie rewolucja, tylko klasyczne gotowanie żaby.

Powiedzmy sobie wprost, Mastodon jest w tym równaniu pomijalny. Liczba zarejestrowanych kont to 10 mln na koniec marca, obecnie ok. 13 mln. Zarejestrowanych, nie aktywnych. Zresztą, miałem o tym notkę. Dla porównania, Twitter to jakieś 400 mln, z czego połowa korzysta codziennie. Przypuszczam, że dodatkowy milion czy dwa założonych kont na Mastodonie jest zupełnie pomijalny. Zwłaszcza, jeśli po zmianach wzrośnie nieco ilość zweryfikowanych kont na Twitterze, a użytkownicy nie przeniosą się, usuwając konta na Twitterze, tylko założą obok.

Zresztą Mastodon ma swoje problemy. Jeden to UX, drugi to… brak wsparcia dla fediwersum u „dużych”. Pamiętacie jak na początku Tumblr zapowiadał, że zaraz wprowadzi wsparcie dla fediwersum? „Zaraz” trwa już osiem miesięcy, a temat ucichł. Spiskowa teoria mówi, że Automattic, czyli właściciel WordPress.com i Tumblr wcale nie przejął wtyczki activypub po to, by ją rozwijać, a tylko po to, by kontrolować, żeby się nie rozwinęła. Bo wolne, zintegrowane ze sobą social media ktoś mógł uznać za zagrożenie dla ww. platform blogowych. Zresztą pewnie słusznie.

Na koniec cebulowa porada dla użytkowników Twittera. Jeśli korzystacie z tego portalu w przeglądarce, to okazuje się, że wtyczka uBlock origin nie tylko blokuje reklamy na Twitterze, ale też pozwala obejść limity. Nie liczyłem oczywiście czytanych tweetów, ale wczoraj korzystałem dość intensywnie i żadnych blokad nie zauważyłem. Przez moment mignął mi komunikat o limicie, ale ledwo go zauważyłem, bo zniknął i posty się wczytały.

Twitter wyłącza 2FA

Przepraszam za clickbaitowy tytuł, ale tak podobno zostały odebrane ostatnie działania i wiadomości wysyłane przez ten serwis. Zatem, gwoli ścisłości nie 2FA, tylko jedną z metod 2FA (SMS) i nie wyłącza, a udostępnia jedynie w płatnej wersji usługi. Nadal można mieć na Twitterze 2FA, za darmo, w dodatku w teoretycznie bezpieczniejszej wersji.

Wiadomość od Twittera w sprawie SMS Źródło: https://twitter.com/jsrailton/status/1626791204238008320

Motywacja jest oczywista. Chodzi o pieniądze. Wszyscy się przyzwyczailiśmy, że SMSy są darmowe, w pakiecie lub abonamencie. Bo taka jest rzeczywistość, przynajmniej w Polsce i dla SMSów wysyłanych w kraju. Tyle, że sytuacja z puntku widzenia serwisu internetowego wygląda nieco inaczej – korzystają z zewnętrznych dostawców, wysyłają do różnych krajów. To jest usługa, która kosztuje i to zapewne niemało. Czyli SMSy to taki rodzaj 2FA, gdzie całość kosztu ponosi serwis. No chyba, że przerzuci ten koszt na użytkownika, co Twitter właśnie zrobił.

Przy okazji, SMSy to jeden z najsłabszych rodzajów 2FA. Dla przeciętnego użytkownika Twittera być może nie ma to praktycznego znaczenia[1], ale istnieją ataki pozwalające na obejście tej metody komunikacji. Jak widać były w praktyce stosowane, także do przejmowania kont na Twitterze. Co gorsza, ofiara, czyli właściciel konta z takim 2FA praktycznie nie ma wpływu na skuteczność tych ataków. Atak odbywa się bowiem na dostawcę sieci komórkowej, a w zasadzie na jego pracowników obsługi klienta.

Dygresja: jeśli korzystamy z serwisu na telefonie i odbieramy SMS służące do 2FA na tym samym telefonie, to czy to jest jeszcze drugi składnik zaczyna być mocno dyskusyjne. Zależy od zagrożenia, przed którym chcemy się bronić. Dla kradzieży hasła czy sesji np. przez XSS wystarczy, dla malware na telefonie czy kradzieży telefonu – niekoniecznie. Dotyczy to także innych metod 2FA, jak email czy TOTP w Google Authenicator[2] czy aplikacji.

Niezależnie od motywacji, zapewne przypadkiem, Twitter został pionierem rewolucji w security na miarę walki Orange ze spamem przez zablokowanie wysyłki maili na porcie 25. Jest to pierwszy duży serwis, który odsyła przestarzałe 2FA via SMS tam, gdzie jego miejsce. Tj. do fanaberii typu papierowa faktura w usługach masowych. Tu od dawna mamy mniej lub bardziej zawoalowane opłaty za wystawianie papierowych faktur. Zwykle w formie „będzie 5 zł taniej, warunkiem skorzystania z promocji jest wyrażenie zgody na faktury elektroniczne”. I jakoś nikt z tym nie ma problemu. Chociaż faktury elektroniczne są także tańsze dla firm. Ale lepsze, bo nie marnujemy papieru i środków na jego transport.

Jeśli ktoś chce nadal korzystać za darmo z 2FA dla konta na Twitterze, to nadal ma dostępne takie opcje:

Opcje 2FA na Twitterze. Źródło: https://twitter.com/mkusiciel/status/1626851994097922048

Według mnie marginalizacja 2FA via SMS to dobra zmiana i będę kibicował innym serwisom, które się zdecydują na jej wprowadzenie. O wadach i zaletach poszczególnych metod 2FA może napiszę innym razem.

[1] Przykro mi, nikomu nie zależy na przejęciu naszych kont. A przynajmniej nie jest to warte ryzyka i/lub kilkuset złotych.
[2] Upraszczam, nie tylko Google Authenticator to oferuje. Tak naprawdę to trywialny algorytm, ale ta implementacja jest najbardziej znana i chyba najpopularniejsza.

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)