Zabezpieczenie bazy przed zniszczeniami w wyniku włamania

3

Zainspirowany tym wątkiem - Czy trzymanie Id na froncie jest dobrą praktyką? zacząłem się zastanawiać, jakie znacie i stosujecie sposoby zabezpieczeń bazy przed zniszczeniami. I nie chodzi mi o blokowanie działań w stylu SQL injection, tylko raczej co w sytuacji, jeśli będzie jakaś dziura i przykładowo komuś uda się jakieś zapytanie podrzucić.

Takim konkretnym impulsem był ten post - https://4programmers.net/Forum/1696296. Bo o ile ukrycie ID danego usera problem z dostępem do tego konkretnego pacjenta rozwiązuje, ale nie zabezpiecza nas to przed wstawieniem WHERE 1=1. Doszedłem do wniosku, że chyba najlepiej jest zablokować loginom/rolom/użytkownikom SQL którzy się łączą z bazą wszystkie operacje typu DELETE, DROP itp i stworzyć jakieś procedury w bazie, które będą odpowiadać za wykonywanie operacji zapisu. Jak nie podasz wprost ID (albo nie spełnisz jakiegoś innego wymogu zabezpieczającego), to nic się nie wykona. Poszukałem w necie i wygląda na to, że ktoś wpadł na to już przede mną :D - chociażby https://stackoverflow.com/questions/4910449/disable-all-queries-in-sql-server-that-dont-use-named-parameters.

Tylko nie wiem, czy to ma sens, a może jest przysłowiową armatą na komara. Czy takie coś nie zabije bazy?

Pytanie - co Wy o tym sądzicie? Jakie stosujecie rozwiązania/patenty?

2

Kopia przyrostowa Veeamem całej wirtualki z bazą według mnie to jedna z najlepszych opcji.

4

@hzmzp: ale to trochę na zasadzie odmalowywania ściany za każdym razem, gdy ktoś narysuje sprayem kutasa, a mi chodzi raczej o to, żeby zamontować kamerę, bramkę i ochroniarza, żeby nikomu się nie udało niczego namalować ;)

2
cerrato napisał(a):

stworzyć jakieś procedury w bazie, które będą odpowiadać za wykonywanie operacji zapisu. (...) Czy takie coś nie zabije bazy?

Bazodanowcy cały czas powtarzają jakie to nie są szybkie procedury składowane więc myślę że nie będzie to duży narzut na wydajność. Gorzej że będzie to duży narzut na utrzymanie, bo ktoś te zapytania i procedury musi pisać

Oczywiście najlepszym rozwiązanie jest nie używanie bazy wprost tylko jakiś CQRS z ES :D . Tam masz bazę tylko od odczytu dla użytkownika

4

Najbardziej podstawowa rzecz - pod żadnym pozorem nie sklejać takich zapytań "na pałę" albo próbując jakoś escapować user input po swojemu, jeśli mają go zawierać:

dbConnection.execute("SELECT xyz FROM qwerty WHERE abc = '$userInput'")

Zamiast tego bezpieczniej jest używać prepared statement. Coś w stylu:

def statement = dbConnection.preparedStatement("SELECT xyz FROM qwerty WHERE abc = ?")
statement.execute([ userInput ])

Nie ochroni to przed podaniem nie-swojego ID, ale przynajmniej odbierasz możliwość zrobienia bazie kuku przez dopisanie własnego SQL ;)

0

@superdurszlak: ale jeśli ktoś w jakiś sposób przechwyci dane dostępu do takiego usera bazy, to może się wpiąć w jakiś inny sposób (nie przez moją aplikację) i wtedy już nic go nie powstrzyma przed wstawienie z łapy DROP. Natomiast jeśli dana rola w bazie nie będzie miała możliwości wykonywania pewnych operacji, to nawet mając dane logowania w stylu 10.11.12.13 user stefan hasło XXX to i tak nie zrobi niczego, co wykracza poza fukncjonalność oferowaną przez dostępne dla danego usera proceudury osadzone w bazie.

4

"Kopia przyrostowa Veeamem" - to nie jest takie proste. Jak masz bazę z której na bieżąco korzysta np. kilkaset osób to na przywróceniem stanu bazy sprzed godziny może oznaczać, że kilkaset wpisów z bazy zniknie, a to w większości przypadków jest niedopuszczalne.

2

No ale skąd będzie miał te dane usera do bazy? mapują się 1:1 dane usera w aplikacji z danymi do bazy? zwykle uprawnienia są rozwiązywane w aplikacji (użytkownik mietek2137 w aplikacji nie ma odpowiadającego usera na bazie), jest jakiś jeden user do użytku przez aplikację i koniec. Credentiale do bazy jak się da to najlepiej w jakimś secure store. Baza najlepiej w ogóle nie wystawiona w publicznej sieci, tylko schowana żeby nikt nieuprawniony nie mógł się do niej nawet próbować dobić.

Jak baza danych jest publicznie dostępna w sieci i jeszcze wyciekną jakieś credentiale to i tak jest dupa zbita za przeproszeniem :D

2
cw napisał(a):

"Kopia przyrostowa Veeamem" - to nie jest takie proste. Jak masz bazę z której na bieżąco korzysta np. kilkaset osób to na przywróceniem stanu bazy sprzed godziny może oznaczać, że kilkaset wpisów z bazy zniknie, a to w większości przypadków jest niedopuszczalne.

Ale można przywrócić dane z logów. Poza tym lepiej przywrócić kopie z przed ostatnich kilku godzin niż żeby w p***e poszło ileś miesięcy.

Innym sposobem jest jeszcze fs oparty na blockchain zawsze można się cofnąć :D tylko gorzej z przestrzenią dyskową.

1

W aktualnym projekcie apka ma dostęp tylko do widoków a operuje przez procedury składowane. Ale w tym przypadku nie sprawia to problemów bo tabele są bardzo luźno powiazane. Jedyny minus ze takie rozwiazania mają tendencje do rozrastania w wyniku czego potem ciezko postawic całość od zera np dla developerów lokalnie.

1
Schadoow napisał(a):

Jedyny minus ze takie rozwiazania mają tendencje do rozrastania w wyniku czego potem ciezko postawic całość od zera np dla developerów lokalnie.

Rozwiązania:

  • Zdalna baza (najgorsze)
  • Docker ze skonfigurowaną bazą gotowy do ściągnięcia
  • Baza stawiana ze skryptów za pomocą np. flywaya. W skryptach są też procedury składowane (IHMO najlepsze rozwiązanie)
1
  1. Prepared statements.
  2. Dynamicznie generowane credentiale (np. poprzez Vault).
  3. Backup, najlepiej point-of-time, ale każdy lepszy niż żaden.
3

zgadzam się co do logów (jeżeli są tworzone odpowiednio często) mogą one trochę poprawić sytuację, ale dalej uważam, ze przywracanie bazy z kopii zapasowej powinno być ostatnią linią obrony.

@cw: jak zamierzasz inaczej przywrócić drapniętą baze albo powiedzmy, że ktoś zrobił update na polu price w tabeli z produktami w twojej hurtowni?
O ile nie ma problemu z zamówieniami bo masz księgowość, maile, proformy i to da się odtworzyć to niestety część danych może zostać utracona i trzeba będzie ją ręcznie odtworzyć.


Kiedyś widziałem taką samoróbkę:
1.Dump bazy (!Point)
2.Każde query logowane do nosqlowej bazy.
Nawet jak się coś pokićkało wystarczyło w logach zobaczyć od kiedy się sypie i przywrócić bazę z !Point i wykonać wszystkie operacje do momentu kiedy działało. Trochę to trwało ale skuteczne.

1
  1. Opakować dostęp do bazy czymś (serwis/widoki/procedury/funkcje) i aplikacja korzysta z API - w serwisie używamy bind variables

  2. Uprawnienia na poziomie bazy - nie wszystko wszystkim, ale minimum potrzebne aplikacji do prawidłowego działania
    (insert tylko do X,Y, delete tylko na Z, select z A,B,C, execute na PROC1; PROC2; itd.)

  3. Wydzielenie ról aplikacyjnych (różne aplikacje, różne uprawnienia, różne grupy użytkowników)

  4. ID zapobiegające łatwemu odgadnięciu i w przypadku dziury wyciągnięciu danych po Idkach, trudność takiego ataku jest różna dla servlet.php?client_id=1, 2, 3, ... vs servlet.php?client_id=31337-1deal-dead-b33f)

  5. Mechanizmy baozodanowe do audytowania - jak zepsują, to i tak ślad zostanie (można rekordy audytu zapisywać poza bazą). W wersji dla ubogich triggery, które umożliwią zbudowanie protezy audytowania.

1

Z czego wynika że SQL Injection to nadal bardzo popularny wektor ataku?

Nie dość że jest chyba nawet w każdym hinduskim tutorialu poruszane, to dodatkowo biblioteki wręcz same z siebie wystawiają metody z parametryzowaną wersją (chyba? czy nie?) to jeszcze są sposoby na wykrycie SQL Injection

3

A nie wystarczy ci docker z bazą do której ma dostęp tylko konkretny serwis przez odpowiedni network? Posiadanie nawet loginu i hasła do bazy nic nie da skoro porty nie są wyexposowane.
Z drugiej strony ważniejszym zabezpieczeniem będzie chyba poprawne zabezpieczenie samego serwera (odpowiednia konfiguracja portów, firewalle, szyfrowanie plików, kontrola userów itd itd). Jak już ktoś wbije ci się na serwer to wszystkie inne zabezpieczenia (czy to bazy, aplikacji whatever) są proste to przełamania (szczególnie że musisz gdzieś trzymać configi/pliki itd).

3

To jest klasyczny rachunek "wygoda vs bezpieczeństwo".

Zakładając maksymalną wygodę, aplikacja łączy się do bazy użytkownikiem z maksymalnymi uprawnieniami. Może bez problemu odpalać migracje trzymane blisko kodu aplikacji (np. liquibase), zmieniać strukturę bazy i wszystkie dane w niej. W większości projektów widziałem takie podejście, jeśli instancja bazy jest schowana w sieci prywatnej (nie jest widoczna z internetu) albo wystawiona jest na tej samej maszynie na localhoście, to nawet nie jest aż tak źle.

Zakładając maksymalne bezpieczeństwo, używamy tylko stored procedures, które trzymane są w osobnym repo (da się tak w ogóle? pewnie tak) i pisane są przez osobny team DBA-ów, a aplikacja łączy się ograniczonym użytkownikiem, który może tylko wywoływać określone procedury. Do tego raidy, replikacje, backupy, archiwizacja logów transakcji do write-only storage (typu bucket S3 z zablokowanym kasowaniem), audyty itp.

Zwykle wybiera się coś bliżej opcji 1 :)

3

Swoją drogą niezły timing z tematem, wygląda na to że obecnie panuje w internecie zmasowany atak na niektóre bazy nosql :
New ‘Meow’ attack has deleted almost 4,000 unsecured databases

2

Temat rzeka: podstawa to zabezpieczenie instancji bazy po instalacji - wile baz posiada jakiś domyślnych użytkowników (z domyślnymi hasłami) i tym podobne kwiatki, o których jak nie wiesz to się się wystawiasz na liścia. Dlatego ważne jest wiedze o zabezpieczaniu konkretnego silnika bazy danych. Użytkownik powinien mieć najmniej uprawnień jak to konieczne. To brzmi jak truizm, ale niestety często się zdarza, że użytkownik dostaje admina nawet jak go nie potrzeba. Spotkałem się też z problemem, że użycie jakiejś funkcji składowanej wymaga np. nadania prawa do dropa, bo dzieją się tam jakiejś cuda.
Ciekaw jestem statystyk takich "ataków" bo ja po kilkunastu latach pracy systemami typu ERP , CRP itp itd (niektóre miały mega podatność na SQL injection) nie spotykałem się z ani jednym atakiem. Częściej były problemy z pojechaniem bazy przez jakieś nieodpowiednie ruchy programistów/administratorów i tu bym się doszukiwał większego zagrożenia.

0

ja po kilkunastu latach pracy systemami typu ERP , CRP itp itd (niektóre miały mega podatność na SQL injection) nie spotykałem się z ani jednym atakiem.

Ja po kilkunastu latach bycia kierowcą nie miałem ani razu czołówki z TIR'em, ale mimo wszystko pasy zapinam i się ciesze, że mam airbagi w samochodzie ;)

użycie jakiejś funkcji składowanej wymaga np. nadania prawa do dropa

Pytanie - czy da się dopuścić użycie DROP'a ale jedynie w ramach procedury, ale zabronić jego wywołania wprost?

0

Pytanie - czy da się dopuścić użycie DROP'a ale jedynie w ramach procedury, ale zabronić jego wywołania wprost?

Nie spotkałem się z takim poziomem uprawnień (co nie oznacza ze nie istnieje) .

1

No to pytanie do pozostałych zawodników - czy ktoś z Was ma wiedze w tym temacie i może napisać, czy się da coś takiego zrobić (jeśli tak, to na jakiej bazie i w jaki sposób) albo niech napisze, że nie ma takiej opcji.

3

Jeśli nie ma takiego uprawnienia to można to obejść i stworzyć trigger, który wywali błąd w momencie kiedy ktoś będzie chciał wykonać drop. Trigger może być wyłaczany i włączany ponownie.

Ja bym jednak postawił na porządny backup. Jeśli zakładasz że ktoś wbije się na serwer to trzeba założyć że może zrobić tam wszystko

2

Triggery DDL a SQL Server https://www.sqlservertutorial.net/sql-server-triggers/sql-server-ddl-trigger/

Można literanie zabronić dropa tabeli (https://docs.microsoft.com/en-us/sql/t-sql/statements/deny-object-permissions-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15)

sam drop bazy może wykonać tylko członek grupe sysadmin i dbcreator, ale to raczej nie daje się pierwszemu lepszemu userowi

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