.class
Pliki Java można dość łatwo dekompilować. Jak mogę chronić swoją bazę danych, jeśli muszę używać danych logowania w kodzie?
.class
Pliki Java można dość łatwo dekompilować. Jak mogę chronić swoją bazę danych, jeśli muszę używać danych logowania w kodzie?
Odpowiedzi:
Nigdy nie wpisuj haseł na stałe do swojego kodu. Niedawno pojawiło się to na liście 25 najbardziej niebezpiecznych błędów w programowaniu :
Zakodowanie na stałe tajnego konta i hasła w oprogramowaniu jest niezwykle wygodne - dla wykwalifikowanych inżynierów wstecz. Jeśli hasło jest takie samo we wszystkich programach, każdy klient staje się podatny na ataki, gdy hasło nieuchronnie staje się znane. A ponieważ jest zakodowany na stałe, naprawienie go jest ogromnym problemem.
Informacje konfiguracyjne, w tym hasła, należy przechowywać w oddzielnym pliku, który aplikacja odczytuje podczas uruchamiania. To jedyny prawdziwy sposób, aby zapobiec wyciekowi hasła w wyniku dekompilacji (nigdy nie kompiluj go do pliku binarnego na początek).
Więcej informacji na temat tego częstego błędu można znaleźć w artykule CWE-259 . Artykuł zawiera dokładniejszą definicję, przykłady i wiele innych informacji na temat problemu.
W Javie jednym z najłatwiejszych sposobów jest użycie klasy Preferences. Jest przeznaczony do przechowywania wszelkiego rodzaju ustawień programu, z których niektóre mogą zawierać nazwę użytkownika i hasło.
import java.util.prefs.Preferences;
public class DemoApplication {
Preferences preferences =
Preferences.userNodeForPackage(DemoApplication.class);
public void setCredentials(String username, String password) {
preferences.put("db_username", username);
preferences.put("db_password", password);
}
public String getUsername() {
return preferences.get("db_username", null);
}
public String getPassword() {
return preferences.get("db_password", null);
}
// your code here
}
W powyższym kodzie możesz wywołać setCredentials
metodę po wyświetleniu okna dialogowego z pytaniem o nazwę użytkownika i hasło. Gdy potrzebujesz połączyć się z bazą danych, możesz po prostu użyć metod getUsername
i getPassword
, aby pobrać zapisane wartości. Dane logowania nie zostaną zakodowane na stałe w Twoich plikach binarnych, więc dekompilacja nie będzie stanowić zagrożenia dla bezpieczeństwa.
Ważna uwaga: Pliki preferencji to zwykłe pliki tekstowe XML. Upewnij się, że podejmujesz odpowiednie kroki, aby uniemożliwić nieautoryzowanym użytkownikom przeglądanie plików surowych (uprawnienia UNIX, uprawnienia systemu Windows itp.). Przynajmniej w Linuksie nie stanowi to problemu, ponieważ wywołanie Preferences.userNodeForPackage
spowoduje utworzenie pliku XML w katalogu domowym bieżącego użytkownika, który i tak jest nieczytelny dla innych użytkowników. W systemie Windows sytuacja może wyglądać inaczej.
Więcej ważnych uwag: W komentarzach do tej odpowiedzi i innych było wiele dyskusji na temat prawidłowej architektury w tej sytuacji. Oryginalne pytanie tak naprawdę nie wspomina o kontekście, w którym aplikacja jest używana, więc opowiem o dwóch sytuacjach, które przychodzą mi do głowy. Pierwszy to przypadek, w którym osoba korzystająca z programu już zna (i ma prawo znać) dane uwierzytelniające do bazy danych. Drugi to przypadek, w którym programista próbuje ukryć dane uwierzytelniające bazy danych przed osobą korzystającą z programu.
Pierwszy przypadek: użytkownik jest upoważniony do poznania danych logowania do bazy danych
W takim przypadku sprawdzi się rozwiązanie, o którym wspomniałem powyżej. Preference
Klasa Java będzie przechowywać nazwę użytkownika i hasło w postaci zwykłego tekstu, ale plik preferencji będzie mógł odczytać tylko upoważniony użytkownik. Użytkownik może po prostu otworzyć plik XML preferencji i odczytać dane logowania, ale nie stanowi to zagrożenia dla bezpieczeństwa, ponieważ na początku znał dane uwierzytelniające.
Drugi przypadek: próba ukrycia danych logowania przed użytkownikiem
Jest to bardziej skomplikowany przypadek: użytkownik nie powinien znać danych logowania, ale nadal potrzebuje dostępu do bazy danych. W takim przypadku użytkownik uruchamiający aplikację ma bezpośredni dostęp do bazy danych, co oznacza, że program musi wcześniej znać dane logowania. Rozwiązanie, o którym wspomniałem powyżej, nie jest odpowiednie w tym przypadku. Możesz przechowywać dane logowania do bazy danych w pliku preferencji, ale użytkownik będzie mógł odczytać ten plik, ponieważ będzie właścicielem. W rzeczywistości nie ma dobrego sposobu na bezpieczne korzystanie z tego przypadku.
Prawidłowy przypadek: użycie architektury wielowarstwowej
Prawidłowym sposobem na to jest utworzenie warstwy pośredniej między serwerem bazy danych a aplikacją kliencką, która uwierzytelnia poszczególnych użytkowników i umożliwia wykonanie ograniczonego zestawu operacji. Każdy użytkownik miałby własne poświadczenia logowania, ale nie do serwera bazy danych. Poświadczenia umożliwiałyby dostęp do warstwy środkowej (warstwy logiki biznesowej) i byłyby różne dla każdego użytkownika.
Każdy użytkownik miałby własną nazwę użytkownika i hasło, które można przechowywać lokalnie w pliku preferencji bez żadnego zagrożenia bezpieczeństwa. Nazywa się to architekturą trójwarstwową (warstwy to serwer bazy danych, serwer logiki biznesowej i aplikacja kliencka). Jest to bardziej złożone, ale naprawdę jest to najbezpieczniejszy sposób zrobienia tego rodzaju rzeczy.
Podstawowa kolejność operacji to:
getInventoryList
.Zauważ, że w całym procesie aplikacja kliencka nigdy nie łączy się bezpośrednio z bazą danych . Warstwa logiki biznesowej otrzymuje żądanie od uwierzytelnionego użytkownika, przetwarza żądanie klienta dotyczące listy zapasów, a dopiero potem wykonuje zapytanie SQL.
Umieść hasło w pliku, który odczyta aplikacja. NIGDY nie osadzaj haseł w pliku źródłowym. Kropka.
Ruby ma mało znany moduł o nazwie DBI :: DBRC do takiego użycia. Nie wątpię, że Java ma odpowiednik. Zresztą napisanie takiego nie jest trudne.
Piszesz aplikację internetową? Jeśli tak, użyj JNDI, aby skonfigurować go zewnętrznie w aplikacji. Przegląd jest dostępny tutaj :
JNDI zapewnia aplikacji jednolity sposób znajdowania i uzyskiwania dostępu do usług zdalnych w sieci. Usługą zdalną może być dowolna usługa przedsiębiorstwa, w tym usługa przesyłania wiadomości lub usługa specyficzna dla aplikacji, ale oczywiście aplikacja JDBC jest zainteresowana głównie usługą bazy danych. Po utworzeniu i zarejestrowaniu obiektu DataSource w usłudze nazewnictwa JNDI aplikacja może korzystać z interfejsu API JNDI w celu uzyskania dostępu do tego obiektu DataSource, którego można następnie użyć do połączenia się ze źródłem danych, które reprezentuje.
Bez względu na to, co zrobisz, poufne informacje będą przechowywane gdzieś w jakimś pliku. Twoim celem jest uczynienie go tak trudnym, jak to tylko możliwe. To, ile z tego możesz osiągnąć, zależy od Twojego projektu, potrzeb i grubości portfela Twojej firmy.
Najlepszym sposobem jest nigdzie nie przechowywanie haseł. Osiąga się to za pomocą funkcji skrótu do generowania i przechowywania skrótów haseł:
hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366
Algorytmy skrótu to funkcje jednokierunkowe. Zamieniają dowolną ilość danych w „odcisk palca” o stałej długości, którego nie można cofnąć. Mają również tę właściwość, że jeśli dane wejściowe zmieni się choćby odrobinę, wynikowy hash jest zupełnie inny (patrz przykład powyżej). Jest to świetne do ochrony haseł, ponieważ chcemy przechowywać hasła w postaci, która je chroni, nawet jeśli sam plik haseł jest zagrożony, ale jednocześnie musimy być w stanie zweryfikować, czy hasło użytkownika jest poprawne.
Niepowiązana uwaga: W dawnych czasach internetu po kliknięciu łącza „Nie pamiętam hasła” strony internetowe wysyłały pocztą e-mail hasło w postaci zwykłego tekstu. Prawdopodobnie przechowywali je gdzieś w bazie danych. Gdy hakerzy uzyskaliby dostęp do ich bazy danych, uzyskaliby dostęp do wszystkich haseł. Ponieważ wielu użytkowników używałoby tego samego hasła w wielu witrynach internetowych, był to ogromny problem z bezpieczeństwem. Na szczęście w dzisiejszych czasach nie jest to powszechna praktyka.
Teraz pojawia się pytanie: jaki jest najlepszy sposób przechowywania haseł? Uznałbym tego (uwierzytelnianie i zarządzanie użytkownikami usług stormpath za) rozwiązanie dość jedna cholernie idealny:
Oczywiście nie jesteś Google ani bankiem, więc jest to dla Ciebie przesada. Ale potem pojawia się pytanie: ile bezpieczeństwa wymaga Twój projekt, ile masz czasu i pieniędzy?
W przypadku wielu aplikacji, chociaż nie jest to zalecane, przechowywanie zakodowanego na stałe hasła w kodzie może być wystarczająco dobrym rozwiązaniem. Jednak dzięki łatwemu dodaniu kilku dodatkowych kroków zabezpieczeń z powyższej listy możesz uczynić swoją aplikację znacznie bezpieczniejszą.
Na przykład załóżmy, że krok 1 nie jest akceptowalnym rozwiązaniem dla twojego projektu. Nie chcesz, aby użytkownicy wpisywali hasło za każdym razem lub nawet nie chcesz, aby użytkownicy znali hasło. Nadal masz gdzieś poufne informacje i chcesz je chronić. Masz prostą aplikację, nie ma serwera do przechowywania plików lub jest to zbyt kłopotliwe dla Twojego projektu. Twoja aplikacja działa w środowiskach, w których nie jest możliwe bezpieczne przechowywanie plików. To jeden z najgorszych przypadków, ale mimo to dzięki dodatkowym środkom bezpieczeństwa możesz mieć znacznie bezpieczniejsze rozwiązanie. Na przykład możesz przechowywać poufne informacje w pliku i możesz zaszyfrować plik. Możesz mieć prywatny klucz szyfrowania zakodowany na stałe w kodzie. Możesz zaciemnić kod, więc utrudniasz komuś złamanie go.ten link . (Chcę jeszcze raz Cię ostrzec, że nie jest to w 100% bezpieczne. Sprytny haker z odpowiednią wiedzą i narzędziami może to zhakować. Jednak w oparciu o Twoje wymagania i potrzeby może to być wystarczająco dobre rozwiązanie dla Ciebie).
To pytanie pokazuje, jak przechowywać hasła i inne dane w zaszyfrowanym pliku: 256-bitowe szyfrowanie Java AES oparte na hasłach
MD5 to algorytm mieszający, a nie algorytm szyfrowania, w skrócie nie można odzyskać haszowania, można tylko porównać. Powinien być idealnie używany do przechowywania informacji uwierzytelniających użytkownika, a nie nazwy użytkownika i hasła db. nazwa użytkownika db i pwd powinny być zaszyfrowane i przechowywane w pliku konfiguracyjnym, co najmniej.