Sockety - optymalność i bezpieczeństwo

0

Mam pewne pytania dotyczące socketów. Ogólna idea mojej aplikacji jest taka: Serwer aktualizuje kilka plików co jakiś czas, a aplikacja w telefonie(client) ma te pliki mieć zawsze aktualne, czyli zgodne z serwerem. I tutaj pojawiają się pytania.

  1. Pliki mają kilka MB. Pobierać tyle z każdym włączeniem aplikacji nie jest chyba dobrym pomysłem. Na pewno nie byłoby, gdyby pliki urosły do rozmiaru 1GB. Pomysł mam taki, żeby każdej aktualizacji plików przez serwer przyporządkować numer odpowiadający dacie aktualizacji (sekundy, które upłynęły od 1970 roku czy coś) i ten numer wysyłać clientowi za każdym razem, gdy pobiera pliki. Przy włączeniu aplikacji client by wysyłał ten numer i gdy są równe to pliki nie zostaną wysłane i zaoszczędzimy kilka MB transferu. Pytanie: Jak realizować taki problem? Czy moje rozwiązanie jest sensowne i tak powinno się to robić?

  2. Inna sprawa to bezpieczeństwo. Nie widzę potrzeby, aby owe pliki były w pełni publiczne. Czy powinienem zastosować jakieś szyfrowanie? Jak wygląda sprawa z możliwością przechwycenia tych plików przez "kogoś z zewnątrz"? Gdybym zamiast mało istotnych plików przesyłał login i hasło do serwisu działającego na serwerze to konieczne by było jakieś konkretne szyfrowanie, czy może prosty algorytm typu RSA byłby wystarczającym zabezpieczeniem?

2
  1. A nie prościej policzyć hash z tego pliku i wysłać tylko ten hash? ;)

prosty algorytm typu RSA

o_O ? Prosty to on jest chyba tylko w wersji textbook implementowany "na pałe" (i podatny na wszystkie możliwe side channele). Zresztą rozumiesz chyba ze RSA nie pozwala szyfrować danych wiekszych niż modulus? Do szyfrowania dużych danych stosuje się algorytmy symetryczne jak AES.

2

odp1.

Robisz hashsum na pliku i jeśli się nie zgadza u klienta, to znaczy, że ma stary plik.

Teraz żeby nie przesyłać przy aktualizacji całego pliku 1GB, to możesz dane mieć zapisane pewnymi blokami.

A dane mieć podzielone jak w drzewie binarnym.

Gdy hashsum wszystkiego się nie zgadza, to sprawdza, która połowa się nie zgadza i tak szukasz bloku, który cię interesuje.

Trochę się zminimalizuje transfer, ale nie wiem czy tak się robi komercyjnie.

0

Ok, czyli wydedukowałem:

  1. Wysyłać hash pliku w celu ustalenia, czy plik jest spójny. Do tego mógłbym użyć algorytmów wyznaczania md5, sha-1. Tylko zadbać, żeby taki algorytm eliminował kolizje.
  2. Pliki warto zaszyfrować przez np. AES
0
  1. Ale znów: nie implementować tego samemu i nie robić tego na pałe. AESa też można łamać na wiele sposobów jeśli ktoś go źle używa i nie rozumie co robi.
1
  1. Nie lepsze byłoby stworzenie szyfrowanego połączenia? Java w standardzie ma możliwość tworzenia socketów korzystających z SSLa.
0

@Shalom,

Ale znów: nie implementować tego samemu i nie robić tego na pałe. AESa też można łamać na wiele sposobów jeśli ktoś go źle używa i nie rozumie co robi.

  1. Czy możesz rozwinąć trochę?
  2. Co masz na myśli, mówiąc, że AESa można łamać na wiele sposobów? Możesz podać jakieś konkretne- oczywiście chodzi o słowa-klucze.
  3. Co to znaczy używać na pałę?
    Wiadomo, że własnej implementacji się nie robi. Masz na mysli tylko to? Pomijamy skrajności typu wektorem IV jest klucz albo klucz jest jawny.
  4. Np., czy widzisz jak można łatwo coś spieprzyć używając AESa z OpenSSLa?

I w sumie drugie pytanie. Powiedzmy, że potrzebujemy generować sporo kluczy symetrycznych. Skąd wziąć jakiś pewny generator pseudolosowy i co najważniejsze, być pewnym, że jest bezpiecznie?

0

Ataków na AESa jest sporo. Jeśli implementujesz samodzielnie to oczywiście jest kupa ataków side-channel pozwalajacych odzyskać klucz prywatny (ataki na cache, memory deduplication, power analysis i wiele innych). Z drugiej strony nawet używając jakiegoś OpenSSL może mocno zawalić sprawę jeśli nie rozumiesz co robisz.
https://github.com/p4-team/ctf/tree/master/2015-10-18-hitcon/crypto_100_simple atak na AES-CFB
https://github.com/p4-team/ctf/tree/master/2016-03-12-0ctf/peoples_square atak na AES ze zmniejszoną liczbą rund
https://github.com/p4-team/ctf/tree/master/2016-09-16-csaw/neo atak Padding Oracle na AES-CBC (swego czasu dość popularna podatność jeśli sygnalizujesz użytkownikowi że deszyfrowanie się nie powiodło)
https://github.com/p4-team/ctf/tree/master/2017-03-18-0ctf-quals/integrity forgery przez bitflipping AES-CBC
https://github.com/p4-team/ctf/tree/master/2017-02-12-bsidessf/delphi forgery przez bitflipping AES-CBC
https://github.com/p4-team/ctf/tree/master/2016-01-16-insomnihack/crypto_200_fridginator deszyfrowanie części wiadomości szyfrowanej przez AES-ECB a następnie forgery w trybie AES-ECB

0

https://github.com/p4-team/ctf/tree/master/2016-09-16-csaw/neo atak Padding Oracle na AES-CBC (swego czasu dość popularna podatność jeśli sygnalizujesz użytkownikowi że deszyfrowanie się nie powiodło)

Czyli co to znaczy? Że AES-CBC jest niebezepieczny? Widziałem inne publikacje, które potwierdzają jego bezpieczeństwo. Więc w końcu nie wiem. Bo przecież nie można opierać się na informowaniu usera. Bo nawet jeżeli nie informujemy to co to daje? I tak się może domyślić. O co chodzi w tym nieinformowaniu?

0

Jeśli informujesz użytkownika że deszyfrowanie się nie powiodło, to AES-CBC nie jest bezpieczny bo można właśnie tak jak w tekście wyżej użyć tzw padding-oracle i deszyfrować sobie dane w dość prosty sposób.
Chodzi o to że użytkownik nie powinien móc odróżnić błędu deszyfrowania od błędnych danych. Jeśli np. masz podać gdzieś zaszyfrowany ciąg np. z loginem i hasłem i serwer je odszyfrowuje i próbuje użyć do logowania to zarówno błąd deszyfrowania jak i błędne dane powinny dać taki sam error.

0

Ja nie rozumiem jaki to ma sens. Żeby odpowiedź 'deszyfracja nie powiodła się' miała jakąkolwiek sens, aplikacja deszyfrująca musi dysponować poprawnym kluczem. Jeżeli użytkownik ma taki klucz (jego aplikacja) ma, to dlaczego to w ogóle jest uznawane za atak.

0

A kto powiedział że to użytkownik ma dostęp do klucza? :) Wyobraź sobie że wchodzisz na stronę która w cookie zapisuje jakieś zaszyfrowane dane. Strona może je spokojnie odszyfrować z cookie, ale ty nie.
Pamiętaj też o tym że są ataki MITM gdzie ktoś może przechwycić zaszyfrowany strumień danych a wcale nie ma klucza.

0

Ciągle Cię nie rozumiem.

Chodzi o to że użytkownik nie powinien móc odróżnić błędu deszyfrowania od błędnych danych.

Dobrze. Mamy serwer X. Aplikacja Y wysyła dane zaszyfrowane. W ogólności w takiej komunikacji masz dwa scenariusze:

  1. Serwer odszyfrował poprawnie, klucz był dobry, zwraca np. 200
  2. Serwer nie zdołał odszyfrować, bo Y nie ma klucza i "robi sobie jaja". Co może zrobić serwer? Może grać głupa, i mówić, że dane niepoprawne zamiast obwieścić miastu i światu, że nie udało się zdeszyfrować. Ale czy to cokolwiek daje? Czy trzeba dużej przenikliwości, że zgadłem jednak nieprawidłowo klucz, tj. były problemy z deszyfracją a nie żadne tam błędne dane?
0
Biały programista napisał(a):

Ciągle Cię nie rozumiem.

Nie rozumiesz, bo widzę, że pominąłeś ważną kwestię oraz podałeś banalny przykład, gdzie ominąłeś clue sprawy. I to nie chodzi o to, że "serwer zrobi głupa" i błędnie coś policzy.
@Shalom opisał już wcześniej przykład:

masz podać gdzieś zaszyfrowany ciąg np. z loginem i hasłem i serwer je odszyfrowuje i próbuje użyć do logowania to zarówno błąd deszyfrowania jak i błędne dane powinny dać taki sam error

I tutaj użytkownik nie może posiadać wiedzy co poszło nie tak, po prostu to za bardzo narusza bezpieczeństwo.
Czy serwer powinien radośnie komunikować, co poszło nie tak?

Spójrz: zarówno w przypadku podania błędnych danych logowania, jak i użycia złego klucza użytkownik otrzyma taki sam error. I tak być powinno. Nie wiesz co poszło nie tak.

Jeśli masz serwer, który traktuje ww. przypadki osobno i udostępnisz informację Błąd deszyfrowania, to potencjalny kradziej już wie, że musi szukać innego klucza. Jak w końcu go znajdzie to otrzyma informację Błędne dane. I co? Ma kolejną cegiełkę wiedzy, że trafił na dobry klucz. A ten fragment wiedzy otrzymał bezpośrednio od serwera (czy tam od programisty, który ten soft napisał).
Teraz zostanie kradziejowi tylko szukanie nazwy użytkownika i hasła.

Oczywiście szukanie klucza, czy tam loginów i haseł ma większą/mniejszą trudność, niemniej każda niepotrzebna informacje tylko wpływa na podatność na potencjalny atak.

0
Grzyboo napisał(a):
  1. Pliki mają kilka MB. Pobierać tyle z każdym włączeniem aplikacji nie jest chyba dobrym pomysłem. Na pewno nie byłoby, gdyby pliki urosły do rozmiaru 1GB. Pomysł mam taki, żeby każdej aktualizacji plików przez serwer przyporządkować numer odpowiadający dacie aktualizacji (sekundy, które upłynęły od 1970 roku czy coś) i ten numer wysyłać clientowi za każdym razem, gdy pobiera pliki. Przy włączeniu aplikacji client by wysyłał ten numer i gdy są równe to pliki nie zostaną wysłane i zaoszczędzimy kilka MB transferu. Pytanie: Jak realizować taki problem? Czy moje rozwiązanie jest sensowne i tak powinno się to robić?

Czemu nie rozdzielić tego na 2 części:

  • Serwis metadanych, który trzyma informacjie o plikach - struktura, timestampy. Być może daloby się wykorzystać ZooKeeper albo etcd.
  • Storage plików, z dostępem przez sftp albo scp. Na początek zwykły serwer powinien wystarczyć.

Aplikacja kliencka odpytywałaby serwer metadanych o zmiany a następnie sama decydowała co pobrać.

0

@Biały programista zakładasz blędnie że ktoś tu próbuje "zgadywać klucz", co nie ma wcale miejsca. Ze względu na sposoby działania niektórych algorytmów szyfrowania można robić różne ciekawe rzeczy nie znając klucza. Szyfry blokowe w trybie CBC działają tak, że blok tekstu jawnego jest xorowany z poprzednim blokiem tekstu zaszyfrowanego i dopiero te dane są szyfrowane. Analogicznie deszyfracja wygląda tak, że najpierw deszyfrujesz dane (np. jakimś AESem) a potem xorujesz je z poprzednim zaszyfrowanym blokiem, zeby odzyskać tekst jawny.
Dla pierwszego bloku xor jest wykonywany z IV (bo nie ma "poprzedniego" bloku). Jak nie trudno zauważyć, można w takim razie zmienić IV albo któryś kolejny blok tekstu zaszyfrowanego i spowoduje to zmianę wartości do której deszyfruje się kolejny blok, bo xor wykona się z czymś innym.
Jeśli ktoś rozumie jak działa XOR to łatwo zauważyć że możemy w praktyce wymusić deszyfrowanie się bloku do zupełnie dowolnej wartości, jeśli znamy dla tego bloku tekst jawny. Wystarczy ze zrobimy IV[i] = IV[i] xor plaintext[i] xor expected_plaintext[i] i voila.
Tą technike można wykorzystać do modyfikowania deszyfrowanej wiadomosci, bez znajomości klucza. Można ją też wykorzystać do padding oracle attack.

Zgadywanie klucza nie ma tu nic do rzeczy ;)

0
Grzyboo napisał(a):
  1. Pliki mają kilka MB. Pobierać tyle z każdym włączeniem aplikacji nie jest chyba dobrym pomysłem. Na pewno nie byłoby, gdyby pliki urosły do rozmiaru 1GB. Pomysł mam taki, żeby każdej aktualizacji plików przez serwer przyporządkować numer odpowiadający dacie aktualizacji (sekundy, które upłynęły od 1970 roku czy coś) i ten numer wysyłać clientowi za każdym razem, gdy pobiera pliki. Przy włączeniu aplikacji client by wysyłał ten numer i gdy są równe to pliki nie zostaną wysłane i zaoszczędzimy kilka MB transferu. Pytanie: Jak realizować taki problem? Czy moje rozwiązanie jest sensowne i tak powinno się to robić?

Tutaj można zastosować jeszcze inny mechanizm, zapamiętujesz najnowszą wersje pliku i zapamiętujesz metadane zmian w formie wersja X dodanie Y bajtów w pozycji Z
Teraz klient wysyła do serwera info że posiada plik w wersji V(tylko numer wersji). Serwer na tej podstawie zwraca informacje jakie operacje należy wykonać na pliku żeby uzyskać taki sam plik jak na serwerze. Klient po wykonaniu tej operacji aktualizuje numer wersji pliku do najnowszej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1