Jak radzisz sobie z bezpieczeństwem bazy danych z aplikacji komputerowej?

12

Przez około 10 lat pracowałem nad różnymi wewnętrznymi aplikacjami klienckimi z magazynami danych SQL Server. Rzadko zaczynałem te projekty - większość z nich to prace związane z przejęciem.

Jedną rzeczą, która wydawała się stała wszędzie, było to, że było jedno globalne konto użytkownika SQL Server, z którego korzystała ta aplikacja, co dawało mu uprawnienia do wspólnej bazy danych, i tak, w niektórych naiwnych sytuacjach korzystało z sakonta użytkownika, które ogólnie próbowałem naprawić, gdy to możliwe .

Naprawdę nie można skutecznie ukryć tej nazwy użytkownika i hasła, których aplikacja używa do uzyskania dostępu do bazy danych. Są one zazwyczaj przechowywane w inilub configpliku, lub ewentualnie pieczone na wykonywalny sama. We wszystkich przypadkach są widoczne dla użytkownika, jeśli wykonają małe kopanie. W jednym przypadku faktycznie użyliśmy configpliku, ale go zaszyfrowaliśmy, ale oczywiście klucz szyfrowania musiał być przechowywany w pliku wykonywalnym (nie byliśmy naiwni wobec ograniczeń tego, ale skutecznie powstrzymywał ludzi od szukania informacji, którzy byli wystarczająco bystrzy przeglądać configpliki).

Wszystkie te systemy miały wbudowany w aplikację system uwierzytelniania użytkowników, ale oczywiście wszystkie były zarządzane przez samą aplikację, co oznacza, że ​​informacje o użytkowniku były przechowywane w bazie danych. Aplikacja ograniczyła to, co możesz zrobić na podstawie twojego poziomu dostępu, ale to wszelkiego rodzaju dyskusja, jeśli możesz po prostu połączyć się z bazą danych i uruchomić zapytania ad-hoc.

Chcę wiedzieć, co robią inne systemy, aby obejść ten problem. Oto opcje, które znam:

  1. Użyj mechanizmu bezpieczeństwa programu SQL Server, aby utrzymać listę użytkowników i ról, a następnie dodaj i usuń użytkowników za pomocą zapytań T-SQL.
  2. Zamiast łączyć się bezpośrednio z bazą danych, utwórz usługę sieci Web działającą na serwerze i umieść tam logikę uwierzytelniania. Każdemu żądaniu dokonaj weryfikacji bezpieczeństwa.

Pierwsze opcje są nieco brzydkie, ponieważ oddzielasz użytkowników od bazy danych, więc użytkownicy nie są już jednostkami pierwszej klasy i nie możesz odwoływać się do nich za pomocą relacji klucza obcego itp.

Drugi wydaje się być poważnym problemem związanym z wydajnością i dużo dodatkowej pracy, a ponadto nie można tak łatwo korzystać z maperów ORM, jak NHibernate (tak myślę).

Czy ktoś ma z tym doświadczenie? Najlepsze praktyki?

Edytować

Zastanawiając się trochę, czy uwierzytelnianie programu SQL Server faktycznie rozwiązuje ten problem? Na przykład, jeśli użytkownik musi mieć możliwość wstawiania i aktualizowania rekordów grafiku, aby móc edytować grafik, nie ma mowy, aby serwer SQL zabronił dostępu do innych wierszy w tabeli szczegółów grafiku, co oznacza, że ​​można również czytać i zapisywać grafiki innych osób.

Scott Whitlock
źródło
Na temat powiązań; niestosowanie ORM jak NHibernate nie jest (chyba) problemem. Jeśli użyjesz usług internetowych jako przykładu, znajdziesz wiele sposobów skutecznego powiązania danych z XML.
jasonk
W każdym razie nie powinieneś używać ORM jako bezpośredniego mapowania między obiektami biznesowymi a jednostkami DB, ponieważ jest to słabe podejście, które tworzy delikatne interfejsy. Wysyłaj żądania do warstwy biznesowej, która pobiera surowe jednostki DB i zwraca klientowi tylko wymagane dane.
gbjbaanb
@gbjbaanb - jasne, zmienię całą architekturę tego popołudnia. :)
Scott Whitlock,
Przypuszczam, że mógłbyś poczekać, aż ktoś cię zhakuje, zanim go zmienisz, ale z drugiej strony, przynajmniej wtedy nie będziesz miał problemów z
przekonaniem
Możesz uniemożliwić użytkownikowi aktualizowanie rekordów innej osoby - używając procedury składowanej jako jedynego sposobu aktualizacji rekordów i używając użytkownika, który uruchamia proces jako część zapytania. Zobacz CURRENT_USER
gbjbaanb

Odpowiedzi:

9

Obawiam się, że dodanie warstwy usługi sieci Web jest prawdopodobnie poprawnym rozwiązaniem twojego problemu.

Oddzielenie klienta od bazowej implementacji bazy danych prawdopodobnie również pomoże na dłuższą metę.

Dodanie warstwy usług internetowych niekoniecznie musi obniżyć wydajność ...

Rzeczywiście, przy odpowiednim interfejsie API, usługa sieciowa może faktycznie poprawić wydajność, grupując wiele zapytań do bazy danych w sieci LAN centrum danych, zamiast wymagać wielu podróży w obie strony przez sieć WAN.

Oczywiście warstwę usług internetowych można często skalować w poziomie i dodawać odpowiednie buforowanie do zapytań do bazy danych, być może nawet mechanizm powiadamiania o zmianie.

Warstwa serwera zapewnia bezpieczeństwo, którego nie można zapewnić w aplikacjach działających na zdalnym kliencie. Wszystko, co działa na kliencie, można „zhakować” i nie powinno być traktowane w żaden sposób jako zaufane. Powinieneś naprawdę umieścić logikę prezentacji w kliencie i hostować wszystko, co ważne na sprzęcie, nad którym masz pełną kontrolę.

Nie wiem o twoich aplikacjach, ale moje aplikacje internetowe są naturalnie podzielone na kilka warstw, z kodem prezentacji oddzielonym od warstwy trwałej przez co najmniej jeden poziom logiki biznesowej, który je rozdziela. Uważam, że znacznie ułatwia to rozumowanie mojej aplikacji i znacznie szybsze dodawanie lub modyfikowanie funkcjonalności. Jeśli warstwy i tak są rozdzielone, względnie łatwo jest utrzymać warstwę prezentacji w kliencie, a resztę na serwerze pod moją kontrolą.

Tak więc, chociaż możesz rozwiązać swoje problemy bez wprowadzania warstwy „usługi internetowej”, do czasu napisania wszystkich procedur przechowywanych (lub równoważnych) niezbędnych do wypełnienia dziur w standardowej implementacji bezpieczeństwa bazy danych, prawdopodobnie lepiej byłoby pisać aplikacja po stronie serwera, dla której można napisać odpowiednie testy jednostkowe.

Bill Michell
źródło
Przyznaję, że nie musi to być wąskie gardło wydajności, ale z pewnością dodaje dodatkową warstwę do architektury, co oznacza znacznie więcej konserwacji.
Scott Whitlock
3
Dodaje warstwę, ale niekoniecznie konserwację. Należy wziąć pod uwagę, że przy całej logice umieszczonej w usłudze, a nie w kliencie, zmiany można „wdrożyć” bez wymagania od użytkowników aktualizacji aplikacji klienckich.
GrandmasterB
5

Podobnie jak w odpowiedzi jmoreno, możesz odmówić użytkownikowi dostępu do wszystkiego poza uprawnieniami EXECUTE do procedur przechowywanych, a następnie skorzystać z łańcucha własności, aby procedura przechowywana wykonała wymagane operacje na tabelach.

Zobacz tutaj szczegóły https://msdn.microsoft.com/en-us/library/bb669058(v=vs.110).aspx

Kiedy użytkownik wprowadza swoją nazwę użytkownika / hasło po stronie klienta, zapisuję je i przesyłam jako parametry do każdego wywołania procedury składowanej. Następnie można je zweryfikować pod kątem wartości zapisanych w tabeli przed wykonaniem żądanej operacji.

Zdecydowanie nie jest to ostatnie słowo w zabezpieczeniach, ale może być konieczne, jeśli komputery PC mają ogólne loginy, ograniczając możliwość korzystania z grup AD w celu uzyskania uprawnień lub masz ograniczony dostęp do samej AD.

James K.
źródło
2

Jednym z podejść jest użycie grup AD i procedur przechowywanych w celu ograniczenia tego, co użytkownik może zrobić - na przykład DB arkusza czasu pracy, może pozwolić na wstawianie, aktualizowanie i usuwanie godzin użytkowników, ale nie pozwala na aktualizowanie godzin innych osób. Identyfikator użytkownika byłby dostarczany przez silnik DB, użytkownik nie miałby bezpośredniego dostępu do tabel DB, tylko do sp, które uruchamiały zapytania na podstawie ich identyfikatora logowania.

Oczywiście nie zawsze jest to możliwe, ale może być. Najlepsze podejście będzie zależeć od twoich wymagań i zasobów.

jmoreno
źródło
Nie zastanawiałem się nad tym. Nie jestem pewien, czy to świetnie pasuje, ale zadziałałoby.
Scott Whitlock
1

To, co nazywacie „usługą internetową”, nazywa się architekturą wielowarstwową . Zazwyczaj jest to droga, w której prawdopodobne są problemy z bezpieczeństwem lub konfiguracją (na przykład dystrybucja aplikacji w wielu biurach). Jednak nie musi być „oparty na sieci”. Wiele działa z innymi protokołami.

Tworzysz serwer aplikacji, który działa jako pośrednik między klientem a bazą danych (i innymi zasobami). Serwer aplikacji obsługuje uwierzytelnianie oparte na aplikacji i wykonuje działania w imieniu klienta. W rzeczywistości idealnie byłoby, gdybyś nie robił SQL w swoim kliencie - raczej wywoływałeś metody na serwerze aplikacji. Serwer aplikacji obsługiwałby wszystkie manipulacje danymi.

Podejście to ma wiele zalet. Nie trzeba konfigurować połączeń z bazą danych i sterowników na klientach. Nie przechowujesz użytkowników bazy danych, haseł i serwerów. Konfiguracja klientów nie jest nawet konieczna - po prostu skieruj ich w kodzie do odpowiedniego adresu URL lub adresu. Ponadto dzięki „logice” na serwerze aplikacji nie trzeba powtarzać się podczas opracowywania innych aplikacji - ten sam serwer aplikacji może być ponownie wykorzystywany przez różne typy klientów.

Grandmaster B.
źródło
Co więcej, jeśli (lub kiedy) ktoś włamie się na twój pulpit (lub serwer sieciowy w ekwiwalencie internetowym), osoba atakująca może mieć pełny dostęp do systemu operacyjnego, ale nadal nie ma dostępu do bazy danych. Nie mogą więc uruchomić potoku „wybierz * od użytkowników” do pliku, który zabierają, złamać w wolnym czasie i pozwolić swojemu CEO wyjaśnić mediom, dlaczego naruszono bezpieczny system. Jeśli użyjesz również sproków w bazie danych, które zezwalają tylko na dostęp, atakujący może również zhakować serwer aplikacji i nadal nie może uzyskać całej bazy danych użytkowników.
gbjbaanb
1

Technologia nieco się zmieniła. Jeśli uwierzytelniasz każdego użytkownika w samej bazie danych i używasz ról bazy danych, możesz teraz użyć tak zwanego widoku aktualizowalnego, aby rozwiązać ten problem, przynajmniej w SQL Server.

Oto, jak mógłby wyglądać widok z możliwością aktualizacji dla tabeli o nazwie, w SomeTablektórej każdy wiersz w tej tabeli jest połączony z pracownikiem. Pracownik powinien widzieć powiązane z nimi wiersze, a członkowie roli HR powinni widzieć wszystkie wiersze, na przykład:

CREATE VIEW [dbo].[vwSomeTable]
AS
    SELECT SomeTable.*
    FROM SomeTable
        INNER JOIN Employee ON SomeTable.Employee_ID = Employee.Employee_ID
    WHERE Employee.Username = USER_NAME() OR IS_MEMBER('HR_Role')=1

GO

Następnie nadaj wszystkim użytkownikom uprawnienia do odczytu (i ewentualnie zapisu) do widoku ( vwSomeTable) i nie udzielaj żadnych uprawnień do table ( SomeTable).

Możesz to przetestować w następujący sposób:

EXECUTE AS USER = 'Some_Regular_Username'
SELECT * FROM vwSomeTable

... które powinny zwracać tylko ich wiersze. Lub:

EXECUTE AS USER = 'Some_HR_Username'
SELECT * FROM vwSomeTable

... które zwrócą wszystkie wiersze. Pamiętaj, że do wykonania tego testu potrzebne będą uprawnienia do wykonywania jako (personifikacja).

Widoki można aktualizować, więc nawet zwykły użytkownik może to zrobić, o ile wiersz jest połączony z jego Employeewierszem:

UPDATE vwSomeTable
SET SomeColumn = 5
WHERE SomeTable_ID = 'TheID'
Scott Whitlock
źródło
0

Korzystanie z uwierzytelniania opartego na certyfikatach jest „poprawnym” sposobem implementacji wspólnego konta SQL. Celem jest wyeliminowanie użycia hasła do tego rodzaju rzeczy.

Aktualizacja:

Chyba źle zrozumiałem pytanie. Myślałem, że ma to związek z próbą znalezienia alternatywy dla umieszczenia nazwy użytkownika i hasła db albo w konfiguracji aplikacji, albo w kopii zapasowej samej aplikacji.

Możesz wyeliminować problem zarządzania hasłami w aplikacjach, używając zamiast tego certyfikatów po stronie klienta. Sam certyfikat nie wystarczy, musisz mieć system dystrybucji i zarządzania zdolny do takich operacji, jak cofanie certyfikatu.

Odniesienie: http://en.wikipedia.org/wiki/Public-key_infrastructure

dietbuddha
źródło
Czy możesz podać więcej informacji na ten temat? Wygląda na to, że może być ortogonalny w stosunku do mojego pierwotnego pytania, ale jest interesujący.
Scott Whitlock
0

Tworząc nowe rozwiązanie bezpieczeństwa pulpitu, zdecydowaliśmy się na usługę sieciową, którą postaram się opisać poniżej.

Kompilujemy pliki wykonywalne aplikacji komputerowej w oddzielnym środowisku od programistów. I oblicz HASH na podstawie tego pliku wykonywalnego, który jest zapisany w bazie danych.

Jedna usługa internetowa, która zapewnia wszystkie informacje potrzebne do uruchomienia aplikacji, hasło DB, parametry połączenia, uprawnienia użytkownika itp.

korzystamy z jednego logowania DB dla każdej aplikacji i zapisujemy dane użytkownika w bazie danych w zmiennych sesji, aby móc kontrolować rekordy.

Biblioteka DLL obsługuje całą komunikację między aplikacją komputerową a usługą internetową, która jest dostępna tylko dzięki wbudowanemu tokenowi w bibliotece DLL.

Aby móc pobrać hasło DB aplikacji z usługi sieci Web, biblioteka DLL oblicza wartość HASH wywołujących wywołań DLL w środowisku wykonawczym i przekazuje jako parametr do usługi sieci Web, która weryfikuje token DLL, a wykonywalny środowisko wykonawcze oblicza wartość skrótu do wartości zarejestrowanej podczas wdrażania. (aplikacja jest dostępna tylko w ramach jednej udostępnionej instalacji sieciowej).

W ten sposób upadliśmy, jest to dobre rozwiązanie problemu bezpieczeństwa, który najbardziej nas interesował i jesteśmy świadomi kilku wad konstrukcyjnych. Już prawie kończymy tę implementację i jak dotąd jesteśmy zadowoleni z rezultatów.

Edycja: możesz zastąpić pomysł skrótu za pomocą podpisów cyfrowych i certyfikatów X.509.

Vitor Arbex
źródło
1
Wydaje się dość oczywiste, gdzie jest rażąca dziura bezpieczeństwa. Biblioteka DLL, o której mówisz, znajduje się w systemie klienta i kod serwera nie ma możliwości zweryfikowania, czy mówi ona o legalnej kopii biblioteki DLL lub zhakowanej / złośliwej / fałszywej. Właśnie stworzyłeś dla siebie dużo pracy bez dodawania, jeśli w ogóle, dodatkowego bezpieczeństwa. Złośliwa osoba potrzebuje tylko tokena i algorytmu, które znajdują się w bibliotece DLL dla każdego, kto chce wyglądać.
Scott Whitlock
@ScottWhitlock, Tak, zgadzam się. staramy się zaciemnić bibliotekę DLL i ruch przechodzący przez HTTPS. Staramy się to poprawić, naprawdę chciałbym mieć jakikolwiek wkład w to, jak to poprawić. Ale to rozwiązanie rozwiązuje już wiele problemów obecnego systemu, w tym hasła zwykłego tekstu przechowywane w plikach sieciowych. Również usługa internetowa pozwala na ponowne użycie dużej ilości kodu dostępnego w dowolnym języku klienta, którego tu używamy, w tym w klientach Delphi i Clipper (Harbour)!
Vitor Arbex
W twoim systemie użytkownik loguje się i prawdopodobnie jest uwierzytelniany przez usługę internetową. Zakładając użycie HTTPS, czy to nie wystarczy? Nie musisz ufać oprogramowaniu klienckiemu, ponieważ wiesz, że użytkownik jest tym, za kogo się podaje, i kontrolujesz usługę internetową, więc upewnij się, że usługa internetowa przekazuje tylko te informacje, które dany użytkownik jest uprawniony zobaczyć. Nawet jeśli zrekonstruują klienta i napisali własne, jakie szkody mogą wyrządzić? Tylko Twoja usługa internetowa zna hasło DB i powinno to być bezpieczne.
Scott Whitlock