Nigdy nie wpisuj haseł na stałe do swojego kodu. Zostało to ostatnio poruszone w 25 najbardziej niebezpiecznych błędów programistycznych :
Zakodowanie na stałe tajnego konta i hasła w oprogramowaniu jest niezwykle wygodne — dla wykwalifikowanych inżynierów odwrotnych. Jeśli hasło jest takie samo w całym oprogramowaniu, każdy klient staje się podatny na ataki, gdy hasło nieuchronnie staje się znane. A ponieważ jest zakodowany, naprawienie tego jest bardzo trudne.
Informacje konfiguracyjne, w tym hasła, należy przechowywać w osobnym pliku, który aplikacja odczytuje podczas uruchamiania. To jedyny prawdziwy sposób, aby zapobiec wyciekowi hasła w wyniku dekompilacji (na początku nigdy nie kompiluj go do pliku binarnego).
Aby uzyskać więcej informacji na temat tego powszechnego błędu, przeczytaj artykuł CWE-259 . Artykuł zawiera dokładniejszą definicję, przykłady i wiele innych informacji na temat problemu.
W Javie jednym z najłatwiejszych sposobów na to 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
metoda po wyświetleniu okna dialogowego z pytaniem o nazwę użytkownika i hasło. Kiedy musisz połączyć się z bazą danych, możesz po prostu użyć getUsername
i getPassword
metody pobierania przechowywanych wartości. Dane logowania nie zostaną zapisane na stałe w 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 raw (uprawnienia UNIX, uprawnienia Windows itp.). Przynajmniej w Linuksie nie stanowi to problemu, ponieważ wywołanie Preferences.userNodeForPackage
utworzy plik XML w katalogu domowym bieżącego użytkownika, którego i tak nie mogą odczytać inni użytkownicy. W systemie Windows sytuacja może być inna.
Więcej ważnych uwag: W komentarzach do tej i innych odpowiedzi było wiele dyskusji na temat tego, jaka jest właściwa architektura w tej sytuacji. Pierwotne 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 zna już (i jest upoważniona do poznania) poświadczenia bazy danych. Drugi to przypadek, w którym Ty, programista, próbujesz zachować poświadczenia bazy danych w tajemnicy przed osobą korzystającą z programu.
Pierwszy przypadek:użytkownik jest upoważniony do poznania danych logowania do bazy danych
W takim przypadku zadziała rozwiązanie, o którym wspomniałem powyżej. Preference
Java class przechowuje nazwę użytkownika i hasło w postaci zwykłego tekstu, ale plik preferencji będzie odczytywany tylko przez autoryzowanego użytkownika. Użytkownik może po prostu otworzyć plik XML preferencji i odczytać poświadczenia logowania, ale nie stanowi to zagrożenia bezpieczeństwa, ponieważ użytkownik znał poświadczenia od samego początku.
Drugi przypadek:próba ukrycia danych logowania przed użytkownikiem
To jest 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. Poświadczenia logowania do bazy danych można przechowywać w pliku preferencji, ale użytkownik będzie mógł odczytać ten plik, ponieważ będzie on właścicielem. W rzeczywistości nie ma dobrego sposobu na bezpieczne korzystanie z tego przypadku.
Właściwy przypadek:korzystanie z architektury wielowarstwowej
Prawidłowym sposobem na to jest utworzenie warstwy środkowej pomię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 serwer bazy danych. Poświadczenia umożliwił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 mogłyby być przechowywane 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 robienia tego typu rzeczy.
Podstawowa kolejność operacji to:
- Klient uwierzytelnia się w warstwie logiki biznesowej przy użyciu osobistej nazwy użytkownika/hasła użytkownika. Nazwa użytkownika i hasło są znane użytkownikowi i nie są w żaden sposób powiązane z danymi logowania do bazy danych.
- Jeśli uwierzytelnianie się powiedzie, klient wysyła żądanie do warstwy logiki biznesowej, prosząc o pewne informacje z bazy danych. Na przykład spis produktów. Zauważ, że żądanie klienta nie jest zapytaniem SQL; jest to zdalne wywołanie procedury, takie jak
getInventoryList
. - Warstwa logiki biznesowej łączy się z bazą danych i pobiera żądane informacje. Warstwa logiki biznesowej odpowiada za tworzenie bezpiecznego zapytania SQL na podstawie żądania użytkownika. Wszelkie parametry zapytania SQL powinny zostać oczyszczone, aby zapobiec atakom typu SQL injection.
- Warstwa logiki biznesowej wysyła listę inwentarza z powrotem do aplikacji klienckiej.
- Klient wyświetla użytkownikowi listę inwentarzową.
Pamiętaj, że w całym procesie aplikacja kliencka nigdy nie łączy się bezpośrednio z bazą danych . Warstwa logiki biznesowej odbiera żądanie od uwierzytelnionego użytkownika, przetwarza żądanie klienta dotyczące listy inwentarzowej, a dopiero potem wykonuje zapytanie SQL.