Nigdy nie byłem wielkim fanem tematów związanych z bezpieczeństwem w SQL Server, ale historia o której chciałem Wam dzisiaj opowiedzieć jest naprawdę ciekawa i powinna „poruszyć” nawet największych ignorantów w tym obszarze.
SQL CLR + UNSAFE permission set = Trustworthy
Sprawa dotyczy ustawienia Trustworthy na poziomie bazy danych. Ustawienie często nadużywane przez programistów budujących aplikacje z wykorzystaniem SQL CLR. Włączenie Trustworthy pozwala na uruchamianie procedur CLR z grupą uprawnień UNSAFE, czyli praktycznie bez kontroli i z pełnym dostępem do zasobów poza SQL Server.
Dlaczego programiści często z niego korzystają? Bo jest łatwo dostępne i upraszcza/przyspiesza proces pisania aplikacji a wiedza o ryzykach wynikających z tego ustawienia nie jest wystarczająco rozpowszechniona.
No właśnie, co oprócz możliwości uruchamiania „niebezpiecznych” CLR daje Trustworthy? Użytkownikom w roli db_owner daje możliwość impersonacji na konto będące właścicielem bazy danych… Mało? Czasami bardzo dużo!
Dlaczego nadużywamy roli db_owner?
Zacznijmy od tego, że nieuzasadnione przydzielanie roli db_owner w SQL Server jest bardzo powszechne. Wszędzie tam, gdzie pojawia się potrzeba uruchamiania procedur składowanych, zamiast nadawania uprawnień EXECUTE, na ogół wykorzystujemy rolę db_owner, dlaczego? Bo nie trzeba przydzielać uprawnień dla każdej z procedur osobno… a przecież w naszej bazie danych może ich być mnóstwo…
Z moich obserwacji wynika również, że samo wdrożenie aplikacji/bazy danych na serwerze produkcyjnym bardzo często odbywa się na koncie użytkownika administrującego danym serwerem, czyli sysadmin’a. Wówczas właścicielem bazy danych (DBO) będzie konto, w kontekście którego wykonujemy wdrożenie. Czy to źle? Na pierwszy rzut oka niekoniecznie, ale kombinacja opisanych powyżej, dość powszechnych praktyk może doprowadzić do opłakanych w skutkach rezultatów.
Co z tego wynika?
Podsumujmy naszą hipotetyczną sytuację (hipotetyczną, choć tak naprawdę spotkałem się z nią nie raz…):
- W naszej bazie danych wykorzystujemy ustawienie Trustworthy, na potrzeby uruchamiania UNSAFE CLR
- Użytkownik aplikacyjny jest w naszej bazie w roli db_owner (dlatego, że nie chciało nam się nadawać mu uprawnień do wszystkich procedur składowanych z osobna)
- Właścicielem bazy danych (dbo) jest login w roli sysadmin (administrator serwera odpowiedzialny za wdrożenie aplikacji)
No to teraz się pobawmy…
Do przeprowadzenia demonstracji będziemy potrzebować dwa loginy. Dla przejrzystości wykorzystamy loginy SQL’owe (choć dokładnie ten sam efekt osiągnąć można z wykorzystaniem loginów opartych o konta domenowe). W tym celu zmieniam ustawienia uwierzytelniania na „mixed authentication” (pamiętajcie, że to nie jest „best practice”J). Loginy, które wykorzystamy do testów to:
- LoginWithSysadmin – reprezentujący administratora serwera, przypisany do roli sysadmin na poziomie instancji SQL Server
- ApplicationLogin – reprezentujący użytkownika aplikacyjnego, za pomocą którego aplikacja dostaje się do bazy danych. Jest on przypisany do roli db_owner w naszej bazie danych i nie posiada żadnych uprawnień na poziomie instancji SQL Server
Teraz możemy zacząć działać… W pierwszym kroku nawiązujemy połączenie z naszą instancją z wykorzystaniem LoginWithSysadmin, dla niedowiarków:
Login posiada już uprawnienia sysadmin na poziomie instancji, oto dowód:
LoginWithSysadmin jest naszym administratorem serwera, to właśnie on jest odpowiedzialny za wdrożenie aplikacji. Nie komplikujmy zbytnio scenariusza, po prostu stwórzmy pustą bazę danych:
Domyślnie tworzona baza danych nie posiada włączonej opcji Trustworthy, to ustawienie z definicji jest wyłączone:
Włączmy zatem Trustworthy:
I sprawdźmy, czy rzeczywiście się udało:
Wygląda na to, że nasza baza danych jest już przygotowana (wdrożenie się powiodło), mamy włączone Trustworthy, a właścicielem jest nasz administrator (LoginWithSysadmin):
Teraz logujemy się do naszego SQL Server z wykorzystaniem loginu aplikacyjnego:
Na poziomie instancji nie mamy żadnych uprawnień:
Ale w bazie TrustworthyDB jesteśmy db_owner’em:
Jak na razie wszystko się zgadza, na dowód braku uprawnień na poziomie instancji spróbujmy pokazać zaawansowane opcje z wykorzystaniem sp_configure:
Wygląda na to, że nie można, czyli tak jak powinno być… a teraz?
Uppss… no to kim ja właściwie teraz jestem?
…Jestem administratorem SQL’a, no to zróbmy coś fajnego:):
Teraz już mam dostęp do wiersza poleceń. Dostęp realizowany jest w kontekście konta serwisowego SQL Server. Sprawdźmy, zatem jak konto się nazywa:
I trochę dodatkowych informacji:
A skoro już wiemy, że jest członkiem grupy lokalnych administratorów, to nie pozwólmy, żeby ktoś o złych zamiarach tę lukę wykorzystał:):
Pa pa!
Podsumowanie
Właściciel bazy danych z uprawnieniami sysadmin’a oraz włączona opcja Trustworthy to bardzo niebezpieczna kombinacja, z czego mało kto zdaje sobie sprawę. Jak dorzucimy do tego wysoko uprawnione konto serwisowe to mamy do czynienia z wyjątkowo niebezpieczną konfiguracją dającą praktycznie nieograniczone możliwości potencjalnemu „włamywaczowi”.
Sprawdźcie, czy Wasze bazy danych nie mają przypadkiem włączonej opcji Trustworthy… Trustworthy wcale nie warto ufać:)