Przejdź do głównej zawartości

Skrypty i hasła

· 5 min aby przeczytać

Jako pierwszy temat na nową odsłonę technicznego bloga idą hasła i ich wykorzystanie w skryptach i programach. Jest to po części rozszerzenie tematu z cyberstartera, tam w jednym ze skryptów wykorzystywane było hasło, ale oryginalny skrypt uprościłem na potrzeby prezentacji. Teraz pokażę oryginalne rozwiązanie wraz z opisem. Chcę się skupić właśnie na tym temacie, bo widziałem naprawdę wiele haseł trzymanych bezpośrednio w kodach źródłowych, co nigdy nie jest dobrym pomysłem. W tym tutorialu skupiam się na rozwiązaniach windowsowych, ale bardzo łatwo je, po drobnych zmianach, zastosować również w linuksach. Jeżeli będzie taka wola, to mogę o tym napisać kolejny tutorial.

Zanim jednak przejdę do rzeczy to, chwilę zatrzymajmy się przy samym problemie. Dlaczego hasła (czy też inne składniki uwierzytelnienia) nie powinny być przechowywane w kodzie? Niby jest to oczywiste, ale spotkałem się z głosami, że to nic takiego, bo do skryptu i tak ma dostęp tylko administrator. Pytanie, czy do kopii bezpeiczeństwa tego, czy też do repo, w którym jest trzymany, tez ma dostęp tylko administrator? No i czy kiedyś przez przypadek, nie wyślemy komuś skryptu, zapominając, że są tam wrażliwe dane. Widziałem rozwiązania, które próbują to hasło w jakiś sposób ukryć. Takie security through obscurity to też nie jest najlepszy sposób, bo nawet jeżeli zastosujemy własną skomplikowaną kryptografię (co nigdy nie jest dobrym pomysłem) to rozwiązanie deszyfrujące również musimy ukryć w kodzie. Lepszym rozwiązaniem jest umieszczenie tego hasła gdzieś wewnątrz systemu, na którym uruchamiany jest skrypt. Czasem jest to dodatkowy plik, czasem zmienna środowiskowa a czasem, i to moim zdaniem najlepsze rozwiązanie, software'owy pęk kluczy, który zapewni dość ograniczony dostęp do potrzebnych danych. Takie rozwiązanie jest częścią systemu operacyjnego Windows, chyba we wszystkich wersjach i nazywa się Menadżer poświadczeń (Credential Manager). Skoro mamy takie rozwiązanie na pokładzie to dlaczego by go nie użyć?

przygotowania

Zanim zaczniemy odpytywać system o hasło, to dobrze jest się przygotować. Dla potrzeb tego tutorialu wyobraźmy sobie jakiś zasób o nazwie tajnyZasob. Może to być baza danych, jakieś RESTowe api, czy cokolwiek innego uwierzytelnianego hasłem. Na takim zasobie dobrze jest stworzyć specjalnego użytkownika, który będzie miał ograniczony dostęp, tak żeby mógł wykonać tylko i tylko to, czego wymagamy od niego w skrypcie. To jest dość istotne, bo zawsze może się coś zdarzyć. Na przykład, spartoliliśmy skrypt i da się niego wstrzyknąć jakiś dodatkowe komendy. Dobrze jest też założyć różnych użytkowników pod różne rozwiązania. Uniwersalny użytkownik serwisowy, nie jest dobrym pomysłem. Jeżeli nasze rozwiązanie jest czasowe, dobrze jest ustawić dla tego konta jakiś termin ważności lub nawet ograniczyć czasowo możliwości logowania tylko do momentu wykonania skryptu.
Jeżeli to jest gotowe to dodajemy dane uwierzytelniające do menadżera poświadczeń

Hasło można ustawić również, korzystając ze wbudowanej konsolowej komendy:

cmdkey /generic:tajnyZasob /user:specuser /pass:dupa.8

W obu przypadkach wynik powinien wyglądać mniej więcej tak:

Trzeba zwrócić uwagę, że w ten sposób hasło, będzie dostępne tylko dla konta, na którym zostało dodane. Natomiast skrypt nie zawsze musi być uruchamiany na tym samym użytkowniku. Dlatego należy docelowo dodać to hasło do menadżera poświadczeń, tego usera, który będzie wykonywał skrypt. Jest jeszcze jeden specjalny przypadek, ale o tym później, teraz czas na zdobycie hasła na poziomie skryptu.

skrypty

powershel

Potrzebny nam będzie moduł CredentialManager.

Install-Module -Name TUN.CredentialManager

Z zainstalowanym modułem, zdobycie hasła nie będzie już żadnym problemem.

getcreds.ps1:

$po = Get-StoredCredential -target 'tajnyZasob' -AsCredentialObject
echo $po.Password

A wynikiem skryptu będzie:

python

Podobnie jak w przypadku powershella, tu również musimy doinstalować dodatkowy moduł, tym razem jest to keyring.

pip install keyring

Keyring będzie działał również na linuksach i macosach, tam jednak potrzebne będzie dodatkowe oprogramowanie, które będzie pełniło funkcję pęku kluczy. Może to być na przykład KWallet dla KDE, ale to jak wspomniałem na początku, zupełnie inna historia.

getcreds.py:

import keyring
password = keyring.get_password("tajnyZasob", "specuser")
print(password)

Wynik tego skryptu będzie taki sam jak w przypadku powershella.

Node.js

Node to może nie do końca temat skryptów administracyjnych, ale zdarzały mi się również sytuacje, w których aplikacje Node.js czy Electron.js potrzebowały hasła do jakichś zasobów. Przykładowo, aplikacja desktopowa, zbudowana w elektronie musi odpytać jakąś usługę korzystając z RESTa, ale w usłudze trzeba najpierw się uwierzytelnić. Użycie menadżera poświadczeń tworzy sytuację, w której tylko użytkownik z dodanymi wcześniej poświadczeniami może poprawnie użyć aplikacji. Modułem, którego potrzebujemy w tym przypadku jest Keytar

keytar = require 'keytar'
const secret = keytar.getPassword('tajnyZasob', 'specuser');
secret.then((result) => {
console.log("result: "+ result);
});

Konto systemowe

No i na koniec ten szczególny przypadek. Skrypty, które potrzebują specjalnego dostępu do zasobów systemu, nie są uruchamiane jako normalny użytkownik, któremu w łatwy sposób możemy dodać poświadczenia. Dla przykładu spójrzmy na windowsowy harmonogram zadań.

Zaznaczona tutaj opcja "Uruchom z najwyższymi uprawnieniami" spodowuje, że skrypt będzie uruchamiany na koncie systemowym, a to konto nie ma dodanych specjalnych poświadczeń. Tutaj przyjdzie nam z pomocą pakiet PSTools a dokładniej komenda psexec. Komenda ta zazwyczaj służy do wykonywania poleceń na zdalnych systemach, w tym akurat przypadku wykonamy komendę lokalnie, ale na koncie systemowym.

psexec -S cmdkey /generic:tajnyZasob /user:specuser /pass:dupa.8

psexec -S uruchamiamy jako administrator

To nam gwarantuje, że konto systemowe ma dostęp do odpowiednich poświadczeń, żeby wykonać skrypt.

Na zakończenie

Pamiętajmy, że w tym rozwiązaniu, jak w zasadzie w każdym znajdzie się również wady. Jest wiele metod ataku, a zdobycie dostępu do systemu z uprawnieniami danego użytkownika, daje nam dostęp do jego poświadczeń. To przed czym chroni to rozwiązanie, to przede wszystkim przypadkowe ujawnienie poświadczeń w kodzie źródłowym. To rozwiązanie powoduje, że wejście w posiadanie skryptu czy aplikacji nie gwarantuje nam pełnego dostępu do zasobów. Nie neguję w zupełnie innych rozwiązań. W prezentacji na cyberstarterze hasło do bazy było przekazywane przez parametr nagiosowej komendy. Czasem to wystarczy. W linuksach, klucze do różnych API często trzyma się w zmiennych środowiskowych użytkownika, który uruchamia skrypt. To również może zdawać egzamin.
Rozwiązań jest wiele i różny jest ich poziom bezpieczeństwa, ale trzymanie haseł, nawet obfuskowanych, w kodzie źródłowym, nie jest rozwiązaniem. Z tą myślą zostawiam Was na nowy nadchodzący rok.
Wszystkiego bezpiecznego.

Do następnego razu!