Jak bezpiecznie korzystać z loginów i haseł w Internecie i nie tylko

Przykład powyżej przedstawia sposób przechowywania znacznej części administratorów w polskich firmach prywatnych i państwowych. Oczywiście nie widać momentu jak wpisuję hasło do arkusza, ale darujmy sobie to. W sumie nie tylko mowa o administratorach, bo spotkałem się z przełożonymi korzystającymi z takich praktyk. Mój komentarz jest następujący: TO JEST DRAMAT. Postaram się wyjaśnić dlaczego łapię się za głowę jak to widzę i przy okazji postaram się przedstawić bezpieczny sposób na hasła w sieci i nie tylko. Jest to mechanizm, który sam sobie wypracowałem i posługuję się nim od ponad roku.

Jakie są najlepsze praktyki w zabezpieczaniu kont w sieci?

W skrócie:

  • hasła do różnych serwisów powinny być różne,
  • hasła powinny składać się z cyfr, znaków specjalnych, dużych i małych liter oraz powinny być dłuższe niż 12 znaków oraz hasło nie powinno składać się ze spójnych elementów (np. hasła z generatorów haseł), powinno zawierać pewien element losowości,
  • hasła powinny być przechowywane w pęku kluczy stworzonym przez menedżerze haseł,
  • hasła te powinny być zmieniane co jakiś czas (celowo nie określam przedziału czasowego),
  • pęk kluczy powinien być zabezpieczony hasłem lub kluczem publicznym lub kluczem sprzętowym (zaleca się wykorzystywanie co najmniej 2 form zabezpieczenia),
  • pęk kluczy powinien być zabezpieczony przed utratą w postaci kopii zapasowej (na przykład poprzez synchronizację na chmurze),
  • dwustopniowe uwierzytelnienie powinno być skonfigurowana w miejscach, gdzie to jest możliwe oraz powinna być realizowana przez mechanizm, który umożliwia tworzenie kopii zapasowej.

To może być trochę przerażające, ale to są naprawdę najlepsze opcje na bezpieczeństwo w sieci. Zakładam, że nie każdego stać i nie każdy ma ochotę kupować klucze sprzętowe by być bezpieczniejszym. Nie ma potrzeby do czegoś takiego się zmuszać, wszystko powinno się w tej kwestii robić z rozsądkiem.

Zanim wejdę w temat, krótkie wyjaśnienie:

Uwierzytelnienie (authentication) to proces, w którym udowadniamy, że my to my, możemy to zrobić poprzez „coś, co wiemy”, czyli na przykład znamy jakieś hasło, „coś, co mamy”, czyli na przykład token z aplikacji do logowania lub klucz sprzętowy lub poprzez „coś, czym jesteśmy”, czyli na przykład korzystając biometryki (używając odcisków palców na czytniku, korzystając z Intel RealSense 3D skanując nasz kształt twarzy przez specjalną kamerę, skanując tęczówki oka).

Autoryzacja (authorization) to proces, w którym system sprawdza, czy mamy prawo dostępu. Na przykład po zalogowaniu się do systemu ten wie, że my to my, lecz nie wie czy mamy dostęp do usług. Na przykład, jeśli nie opłacę subskrypcji za Netflixa i zaloguję się na swoje konto, ale nie będę mógł oglądać żadnych filmów. To przykład nieudanej autoryzacji.

Niektórzy używają pojęcia autentykacja – to jest kalka z angielskiego i dotyczy ona uwierzytelnienia.

„hasła do różnych serwisów powinny być różne”

Załóżmy, że używamy 1 hasła: Zaq12wsx!cokolwiek Jest to hasło, które spełnia prawie wszystkie wymagania, które przedstawiłem (z wyjątkiem losowości, bo jest ona trochę kiepska). Te hasło używamy na wielu różnych stronach. Facebook, Google, Instagram, cokolwiek innego. Nagle jeden z tych serwisów zostaje zaatakowany przez grupę hakerów, która wykrada bazę danych. Potem okazuje się, że strona nie przechowywała haseł w bezpieczny sposób (czyli na przykład przechowywała hasła w czystym tekście albo korzystała z kiepskiego mechanizmu haszującego). W ten sposób w większości wypadków ktoś ma w bazie nasz adres email i hasło – czyli najpopularniejszą formę logowania. Załóżmy, że z tej listy to był Instagram – w takiej sytuacji mając te same loginy i hasła atakujący może się zalogować na Facebooka, konto Google i cokolwiek innego tam, gdzie ustawiliśmy te same hasła. To jest małe piwo. Duże piwo zaczyna się wtedy jeśli używaliśmy tego samego hasła na kontach firmowych i ktoś zorientuje się jaki adres mailowy posiadamy w pracy lub nazwę użytkownika. Wtedy się mogą zacząć prawdziwe kłopoty.

Rozwiązanie? Inne hasła. Ale z drugiej strony spójrzmy prawdzie w oczy – ile jesteśmy w stanie haseł zapamiętać? 1? 5? 10? Będzie dobrze jak 5. Problem w tym, że większość z nas nie korzysta z 5 serwisów, tylko na przykład z 50. W moim przypadku zliczyłem ponad 150 kont w Internecie. Powodzenia w zapamiętaniu tych 150 haseł. To nie jest możliwe, dlatego trzeba sobie te hasła zabezpieczać. Dlatego hasła należy przechowywać w jednym, wspólnym miejscu.

mocne hasło i dwustopniowe uwierzytelnianie

Dzięki temu nasze hasło jest trudniej odgadnąć. Najprościej jest używać menedżera haseł i kopiować wygenerowane hasła – moje hasła są w większości 32-znakowe i nie ma szans, bym pamiętał chociaż jedno. Jest kilka kont, do których potrzebuję szybkiego dostępu typu na przykład Koleje Śląskie jak bym chciał szybko przez telefon kupić bilet na pociąg, ale poza tym wszędzie te hasła są po prostu długie i je wszystkie kopiuje z bazy. Po 10 sekundach od skopiowania hasła z KeePassa te jest usuwane ze schowka w systemie. Hasło same w sobie powinno mieć ogólnie więcej niż 12 znaków, duże, małe litery, cyfry i znaki specjalne. Do testowania haseł można skorzystać ze strony howsecureismypassword.net, która pozwala nam wpisać hasło, a strona powie nam w ile przeciętny komputer powinien takie hasło złamać:

Przykład dobrego hasła:

Kmn6X;c?F4Mx'=^"

Te hasło jest całkowicie losowe. Co prawda, nie może być wykorzystane wszędzie, bo zawiera znaki, których nie możemy dodać np. w pliku konfiguracyjnym. Takie znaki to często: { } [ ] ( ) / \ ' " ` ~ , ; : . < >, ale na szczęście generatory haseł pozwalają nam wykluczyć takie znaki z generacji, dzięki czemu można otrzymać to:

Cr*f6y_8EV4ey!&r

Przykład trudnego, ale nie całkowicie losowego hasła:

23N2iePatrz5ieNaP6likany%

Ciężko, by takie hasło było związane z jakąś pracą, bo jest całkowicie losowe + znaki w haśle są czasami w losowych miejscach. To sprawia, że hasło jest trudniejsze do złamania, ale jednocześnie trudniej je zapamiętać.

Przykład słabego hasła:

NazwaNaszejFirmy2021!123

Hasło składa się z prostych słów, które łatwo zapamiętać, przez co sprawia, że łatwiej je zapamiętać. To dobre dla nas, lecz też ułatwia to sprawę atakującym. Trzeba na to uważać z jakich słów składają się hasła, bo jeśli kontekst hasła pasuje do tego gdzie ono jest używane – sprawia to, że łatwiej jest je odgadnąć.

Teraz pytanie: czy każdy powinien używać takiego trudnego hasła? To zależy gdzie takie hasło będziesz używać:

  • strony internetowe – całkowicie losowe,
  • bazy danych, aplikacje – całkowicie losowe,
  • menedżer haseł – trudne, ale nie musi być całkowicie losowe,
  • strony „niskiego ryzyka” – trudne, ale nie musi być całkowicie losowe.

Dlaczego tak? Pierwsze dwa zawsze mogą być kopiowane z menedżera haseł i nie trzeba tych haseł pamiętać. Tak samo jest z drugim. W przypadku menedżera haseł te hasło musimy pamiętać (jest wyjątek, opiszę go w dalszej części posta). Tutaj musimy sami zdecydować jak dobrą mamy pamięć lub papier, na którym zapisujemy tekst.

W przypadku stron „niskiego ryzyka” – mam na myśli strony, w których jeśli stracimy dostęp, nikt nam nie wyrządzi szkody. Dla mnie taką stroną jest strona Kolei Śląskich, bo w przypadku, gdy ktoś przejąłby moje konto to mógłby zobaczyć jak się nazywam i ewentualnie mógłby kupić mi bilet na pociąg 😂

Żarty żartami, ale definitywnie do niskiego ryzyka nie należą strony takie jak na przykład Facebook. Typowy argument, który ja słyszę to: ale przecież ja nie mam nic do ukrycia, tam i tak nic nie ma, przecież nie jestem żadnym przestępcą, to co mogłoby się stać. Cóż, mogłoby i dam przykład. Załóżmy, że ja jestem atakującym. Mógłbym zalogować się na Facebooka ofiary i napisać do 2 znajomych, z którymi ofiara ostatnio pisała, że wykopałem się kasy i potrzebuje 50 zł na tydzień. W dużej mierze raczej osoba odpisze, że nie ma problemu, bo większość ludzi ma uśpioną czujność i nie zadzwoni na komórkę, by sprawdzić czy to na pewno prawdziwa prośba. Czyli w ten sposób wyjąłem od ofiary 100 zł, bo koniec końców pewnie odda te pieniądze. A to tylko jedna osoba. Oczywiście to tylko początek rzeczy, jakie można robić podszywając się pod kogoś i chyba lepiej się o tym nie przekonywać jak to mogłoby się skończyć, gdy ktoś przegląda nasze wiadomości prywatne…

Hasło do menedżera haseł może być losowym i trudnym hasłem pod warunkiem, że będziemy w stanie je wprowadzić w wygodny sposób. W innym wypadku większość ludzi po prostu nie będzie korzystać z takiego rozwiązania. W takich sytuacjach szczególnie warto się wyposażyć w klucz sprzętowy, jakim jest np. YubiKey:

Dzięki temu poza podaniem hasła w KeePassie należy wybrać klucz sprzętowy przypisany do bazy:

Alternatywą dla kluczy sprzętowych jest klucz publiczny – generujemy taki za pomocą Pageanta, gdy na przykład używamy PuTTY na Windowsie – to zdecydowanie wystarczy. Używałem czegoś takiego w poprzedniej pracy. Dla cierpliwych zawsze można skorzystać ze wszystkich trzech opcji, ale moim zdaniem nie jest warto.

„hasła powinny być przechowywane w pęku kluczy stworzonym przez menedżerze haseł”

Niektórzy używają Excela ale to jest kiepskie rozwiązanie z tego względu, że nie każdy zabezpiecza go hasłem, poza tym nie każdy ma nowego Office, co sprawiałoby to, że Office szyfrowałby dokument aktualnymi bezpiecznie mechanizmami (bodajże Office 2007 i 2003 mają bardzo łatwe do złamania mechanizmy). Poza tym Excel nie jest przeznaczony do przechowywania haseł, do tego są pęki haseł w programach typu KeePassXC (to taki lepszy KeePass). KeePass jest przykładowym rozwiązaniem i nie jest wyłącznym menedżerem haseł, które rozwiązuje ten problem. Jest wiele innych, czasami opartych o chmurę rozwiązań, które też są bezpieczne.

KeePass poza samym przechowywaniem haseł pozwala na obsługę 2FA kont, łatwe generowanie haseł, pozwala zabezpieczyć bazę haseł dwu- i trzystopniowo (hasło, klucz publiczny, klucz sprzetowy), da się go zintegrować z przeglądarką, dzięki czemu wpisywanie haseł jest prostsze. Zresztą przedstawiłem już nawet przykład powyżej.

Oczywiście trzeba pamiętać, że KeePass i jego baza nie jest czymś nie do złamania. Fakt, zawartość jest szyfrowana przez AES 256-bitowy, ale to nie zmienia faktu, że takie hasło da się złamać. Ten sam program jest w stanie łamać hasła do arkuszy w Excelu i jest nim hashcat. To ogólnie narzędzie do łamania haseł w różnych algorytmach, i po prostu bazy KeePassa i arkusze Excela są jedynie drobnym elementem listy tego, co on obsługuje. Tutaj jest gotowy skrypt do łamania arkuszy Excela.

Hasła się łamie na dwa sposoby: słownikowo i siłowo. Słownik zakłada, że posiada się dużą bazę haseł (na przykład jedną wielką sklejkę haseł z wycieków różnych serwisów internetowych z przeciągu kilku lat 😉) lub serwerek z mocną kartą graficzną do obliczeń i jeśli hasło było krótkie lub łatwe – zostanie złamane dosyć szybko.

Pokażę jak to wygląda w praktyce zakładając, że definiuję metodę siłową, która natrafi na hasło, które jest w arkuszu:

supra@supra-pc:/mnt/r/supra/Pulpit$ python office2hashcat.py Zeszyt1.xlsx
$office$*2013*100000*256*16*cb973f1be246fbd96939c909bce5905e*18fe55fc3f134776687f79551c68a3f6*b519e82fa04c357910f2022f6ddd8f2f2ba0ac81436c2b83d96ca7eb0eda8883
R:\supra\Pulpit\hashcat-6.0.0>hashcat.exe -a 3 -w 3 -m 9600 hash.lst ^Z?l?l?d?dwsx!
hashcat (v6.0.0) starting...

* Device #1: CUDA SDK Toolkit installation NOT detected.
             CUDA SDK Toolkit installation required for proper device support and utilization
             Falling back to OpenCL Runtime

* Device #1: WARNING! Kernel exec timeout is not disabled.
             This may cause "CL_OUT_OF_RESOURCES" or related errors.
             To disable the timeout, see: https://hashcat.net/q/timeoutpatch
OpenCL API (OpenCL 1.2 CUDA 11.0.140) - Platform #1 [NVIDIA Corporation]
========================================================================
* Device #1: GeForce RTX 2070, 6912/8192 MB (2048 MB allocatable), 36MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates

Applicable optimizers:
* Zero-Byte
* Single-Hash
* Single-Salt
* Brute-Force
* Slow-Hash-SIMD-LOOP

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 696 MB

The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.

[s]tatus [p]ause [b]ypass [c]heckpoint [q]uit =>

Session..........: hashcat
Status...........: Running
Hash.Name........: MS Office 2013
Hash.Target......: $office$*2013*100000*256*16*cb973f1be246fbd96939c90...da8883
Time.Started.....: Fri Jul 17 12:12:55 2020 (2 secs)
Time.Estimated...: Fri Jul 17 12:13:03 2020 (6 secs)
Guess.Mask.......: Z?l?l?d?dwsx! [9]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:    10937 H/s (31.52ms) @ Accel:4 Loops:512 Thr:1024 Vec:1
Recovered........: 0/1 (0.00%) Digests
Progress.........: 0/67600 (0.00%)
Rejected.........: 0/0 (0.00%)
Restore.Point....: 0/67600 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:31744-32256
Candidates.#1....: Zar12wsx! -> Zqx84wsx!
Hardware.Mon.#1..: Temp: 71c Fan: 46% Util:100% Core:1935MHz Mem:7000MHz Bus:16

$office$*2013*100000*256*16*cb973f1be246fbd96939c909bce5905e*18fe55fc3f134776687f79551c68a3f6*b519e82fa04c357910f2022f6ddd8f2f2ba0ac81436c2b83d96ca7eb0eda8883:Zaq12wsx!

Session..........: hashcat
Status...........: Cracked
Hash.Name........: MS Office 2013
Hash.Target......: $office$*2013*100000*256*16*cb973f1be246fbd96939c90...da8883
Time.Started.....: Fri Jul 17 12:12:55 2020 (6 secs)
Time.Estimated...: Fri Jul 17 12:13:01 2020 (0 secs)
Guess.Mask.......: Z?l?l?d?dwsx! [9]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:    11630 H/s (28.61ms) @ Accel:4 Loops:512 Thr:1024 Vec:1
Recovered........: 1/1 (100.00%) Digests
Progress.........: 67600/67600 (100.00%)
Rejected.........: 0/67600 (0.00%)
Restore.Point....: 0/67600 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidates.#1....: Zar12wsx! -> Zqx84wsx!
Hardware.Mon.#1..: Temp: 71c Fan: 54% Util:100% Core:1935MHz Mem:7000MHz Bus:16

Started: Fri Jul 17 12:12:52 2020
Stopped: Fri Jul 17 12:13:02 2020


/////////////////////////////////////////////////////////////////////////
Hashcat.potfile
$office$*2013*100000*256*16*cb973f1be246fbd96939c909bce5905e*18fe55fc3f134776687f79551c68a3f6*b519e82fa04c357910f2022f6ddd8f2f2ba0ac81436c2b83d96ca7eb0eda8883:Zaq12wsx!
/////////////////////////////////////////////////////////////////////////

W zaznaczonym miejscu widać hasło. Jak widać poniżej, taka metoda też jest dostępna dla KeePassa:

W przypadku posiadania dwustopniowego uwierzytelniania raczej ta opcja w hashcat to przycisk do papieru. Nie sądzę, by było możliwe łamanie dwóch ciągów znaków jednocześnie w wydajny sposób i w czasie, który pozwoli na łatwe złamanie siłą rzeczy dwóch haseł jednocześnie. Jeśli się mylę – dajcie znać.

„hasła te powinny być zmieniane co jakiś czas”

Tutaj nie ma za dużo do obgadywania. Hasła co jakiś czas należy zmieniać i to jest trochę jak wymienianie wypalonych żarówek w pokoju, lecz klepiemy na klawiaturze. W firmach sensownym okresem czasu jest 3-6 miesięcy. Prywatnie nie zmieniałbym częściej niż rok, nawet dwa. Wyjątkiem od tej reguły jest wyciek – wtedy zmieniamy hasło natychmiast. Oczywiście trzymając się poprzedniej reguły dotyczącej różnych haseł na różnych serwisach nie trzeba zmieniać tych haseł wszędzie – wystarczy w miejscu, gdzie to hasło wyciekło.

„pęk kluczy powinien być zabezpieczony hasłem lub kluczem publicznym lub kluczem sprzętowym (zaleca się wykorzystywanie co najmniej 2 form zabezpieczenia)”

Ten punkt jest opcjonalny. Nie każdego stać na to i nie każdy musi to mieć prywatnie, ale moim zdaniem służbowo jako administrator w firmie każdy powinien coś takiego posiadać. Dwa klucze sprzętowe, które są moim zdaniem godne uwagi to YubiKey 5 NFC i SoloKey Solo V2 (które powinno już być dostępne). Klucze powinno się kupować w parach, to znaczy zawsze dwie sztuki, bo jak uszkodzimy klucz to na zapasowym mamy kopię naszych kluczy prywatnych/HMAC itd. W przypadku YubiKey 5 NFC koszt to około 500 zł mając to na względzie. Solo V2 dostaniemy za połowę tej ceny. W cenie YubiKey dostaniemy definitywnie dużą odporność na uszkodzenia i wodoszczelność (której nie ma w Solo V2), w zamian w Solo V2 otrzymamy sprzęt z otwartym oprogramowaniem układowym (firmware YubiKey jest zamknięty) oraz nieco większe możliwości pod kątem autoryzacji w Windowsie od YubiKey).

Konfiguracja kluczy jest dosyć prosta, udało mi się wszystko ustawić tego samego wieczoru, którego odebrałem urządzenia 😊

„pęk kluczy powinien być zabezpieczony przed utratą w postaci kopii zapasowej”

Podałem jako przykład chmurę, bo to wydaje się najrozsądniejsze. W ten sposób możemy mieć dostęp do pęku kluczy z telefonu, bo na Androidzie jest aplikacja KeePass2Android, która jest w stanie pobierać pęk kluczy z chmury i go aktualizować. Ponadto obsługuje klucze sprzętowe! To jest moim zdaniem coś. Najlepiej mieć do tego własną chmurę, ale zdając sobie sprawę z tego, że nie każdego na to stać lub nie każdy chce musieć coś takiego ustawiać – zawsze można wybrać Google Drive czy Dropbox. To też są rozwiązania, które raczej będą okej. Ja osobiście korzystam z Nextcloud, bo uważam to za najlepsze rozwiązanie do prywatnej chmury ze względu na możliwości i fakt, że te oprogramowanie jest otwartoźródłowe. Ponadto jest wtyczka, która pozwala na otwieranie baz KeePassa bezpośrednio ze stronki Nextcloud, lecz niestety ona raczej nie obsługuje zabezpieczenia kluczem sprzętowym 😔

Jeśli hasła dotyczą tylko lokalnych systemów/baz – dobrym rozwiązaniem też może być przechowywanie bazy na udziale sieciowym. Jeśli ktoś chce – zawsze można korzystać z dysków zewnętrznych, ale to jest dosyć uporczywe, bo trzeba ciągle pamiętać o kopiowaniu najnowszej wersji pliku i posiadaniu jego kopii gdzieś indziej. Chmura sama w sobie to rozwiązuje. Jeśli komputer padnie – jest na chmurze. Jeśli chmura padnie – jest na dysku lokalnie. Poza tym mam kopię na dwóch komputerach, więc mogę spać spokojnie.

„dwustopniowe uwierzytelnienie powinno być skonfigurowana w miejscach, gdzie to jest możliwe oraz powinna być realizowana przez mechanizm, który umożliwia tworzenie kopii zapasowej”

W każdym szanującym się dzisiaj serwisie mamy możliwość korzystania z dwustopniowego uwierzytelniania (2FA, two-factor authentication) lecz większość ludzi z tego nie korzysta. BŁĄD. To jest mechanizm, który pozwala nam znacznie zwiększyć bezpieczeństwo w sieci. W skrócie działa to tak: wpisujemy login i hasło, następnie wpisujemy token (ciąg cyfr, najczęściej 6) z telefonu lub używamy klucza sprzętowego do potwierdzenia, że to naprawdę my chcemy się zalogować. Tokeny same w sobie są generowane na podstawie tajnego kodu przechowywanego przez aplikację oraz aktualnego czasu. Nie wymagają one połączenia z Internetem. W skrócie, musimy mieć ustawioną aktualną godzinę, w innym wypadku generowane tokeny nie będą działać. Odnosi się to do tokenów z aplikacji, nie z SMSów.

Korzystanie z tokenów na telefonie odbywa się przez wiadomości SMS lub aplikację i w drugim przypadku wymaga od nas jedynie posiadania telefonu z Androidem lub iOSem (sic!). Na telefonie musimy zainstalować odpowiedni program, następnie należy dodać metodę uwierzytelniania w postaci tokenu. Następnie należy aplikacją do tokenów zeskanować kod QR wyświetlany na ekranie lub podać tajny kod wygenerowany przez kreator do programu z tokenami. To doda możliwość generowania tokenów. Następnie taki token należy przepisać do okienka w kreatorze dodawania metody uwierzytelniania i voilà – gotowe.

Najpopularniejszą aplikacją do tokenów jest Google Authenticator, lecz ja osobiście polecam Authy. Google Authenticator nie posiada mechanizmu kopii zapasowej tajnych kodów, przez co jak zgubilibyśmy telefon, tracimy wszystkie tokeny i mamy problem. Duży problem. Z Authy takiego problemu nie ma, ponieważ tajne kody są przechowywane w chmurze. Aplikacja i tokeny w niej wyglądają mniej więcej tak:

Zrzut ekranu

Alternatywą jest klucz sprzętowy. Wpisujemy login i hasło, następnie musimy dotknąć klucz sprzętowy w odpowiednim miejscu lub podać kod PIN klucza. Przykład logowania do konta Google możecie zobaczyć poniżej:

Na innych serwisach możemy dostać nieco inny prompt, przykład z cloudflare.com:

Kopia takiego klucza jest realizowana w prosty sposób: po prostu na dwóch kluczach sprzętowych zapisuje się dokładnie ten sam klucz prywatny, klucz HMAC itd. Wtedy jeśli jeden przestaje działać, drugi dalej może być używany. Przy konfiguracji YubiKey zapisałem sobie plik tekstowy z kluczami, które znajdują się wewnątrz urządzenia. Dzięki temu będę mógł przepisać dane na kolejne urządzenie jeśli uszkodzę jakoś jeden klucz, a potem będę musiał kupić nowy, by zachować status 2 kluczy w jednym czasie. Klucz sprzętowy ma jedną zasadniczą wadę – można go łatwo zgubić. Warto więc przechowywać kopię klucza w bezpiecznym miejscu, a główny klucz w miejscu, które na pewno nie zniknie – ja trzymam go przy kluczach, bo tak na dobrą sprawę to jest taki trochę klucz, lecz elektroniczny. Ponadto używałbym takiego klucza tylko w sprzęcie, któremu ufam – nie wiem jakie oprogramowanie może pracować w czyimś komputerze i niekoniecznie chciałbym to testować.

najważniejsze pytanie – czy naprawdę nam jest to potrzebne?

To niegrzeczne, ale odpowiem pytaniem – a czy Tobie jest potrzebne bezpieczeństwo w sieci?

Definitywnie nie każdy musi mieć klucze sprzętowe. Nie powiem, że to jest bajer, to jest przydatna rzecz, ale nie każdy musi tak bardzo dbać o swoje bezpieczeństwo i to rozumiem. Jednak jak wspomniałem na początku, kradzież tożsamości z punktu widzenia ofiary to okropna rzecz i nikomu tego nie życzę. Dlatego moim zdaniem, dla własnego spokoju warto spędzić jeden lub dwa popołudnia przeglądając historię przeglądania Internetu w przeglądarce, spisać portale, na których się logujemy, utworzyć pęk kluczy, pozmieniać hasła do wszystkiego (na każdej stronie inne), zrobić kopię bazy kluczy i już jesteśmy bezpieczniejsi niż większość świata. To wymaga zaangażowania, ale nie wymaga wielkiego wysiłku, by o to zadbać.

Koniec końców trzeba sobie zadać pytanie – co bardziej się opłaca? Zmarnować trochę czasu na bawienie się w menedżera haseł i zmienianie haseł na trudniejsze, a potem ich kopiowanie przy logowaniu się do serwisów czy zmienianie ich po tym, jak ktoś w naszym imieniu wykorzysta naszych znajomych lub zrobi coś jeszcze gorszego i ten cały wstyd, który zostaje po całej sytuacji?

Odpowiedzcie sobie sami.

Zmiana typu interfejsu w VMware Workstation

W większości niestandardowych systemów takich jak np. FreeNAS VMware Workstation przy tworzeniu maszyny tworzy interfejs(y) sieciowe wykorzystując domyślnie sterownik e1000, który pozwala na wyciągnięcie 1 Gbit/s po stworzeniu sieci pomiędzy maszynami wirtualnymi/hostem. To jest problem, bo czasami jeśli chce się zrobić testy na iSCSI, sprzęt mamy wydajny i coś wolno działa. Generalnie ten sterownik jest problemem i jest proste rozwiązanie na zmianę tego sterownika na wydajny vmxnet3, który pozwala na wyciągnięcie na luzie 10 Gbit/s. Problem w tym, że nie każdy system to wspiera. W moim przypadku wiem, że wspiera.

Rozwiązanie jest proste: wystarczy znaleźć plik o formacie *.vmx dla naszej maszyny wirtualnej (w moim przypadku jest to maszyna freenas.vmx, edytować plik i zmienić zmienną ethernetX.virtualDev = "e1000" (X – numer interfejsu sieciowego) na ethernetX.virtualDev = "vmxnet3". To załatwia sprawę, ale trzeba pamiętać, że system wykryje te interfejsy jako nowe przechowując nadal starą konfigurację, więc trzeba się upewnić, że nie ma się w swoim systemie przypisanych żadnych ustawień do tych „starych” interfejsów.

Przykład pliku VMX przed:

.encoding = "windows-1250"
config.version = "8"
virtualHW.version = "18"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
vmci0.present = "TRUE"
hpet0.present = "TRUE"
nvram = "freenas.nvram"
virtualHW.productCompatibility = "hosted"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.suspend = "soft"
powerType.reset = "soft"
displayName = "freenas"
guestOS = "freebsd-64"
tools.syncTime = "FALSE"
sound.autoDetect = "TRUE"
sound.fileName = "-1"
sound.present = "TRUE"
numvcpus = "8"
cpuid.coresPerSocket = "8"
memsize = "8192"
nvme0.present = "TRUE"
nvme0:0.fileName = "freenas.vmdk"
ide1:0.deviceType = "cdrom-image"
ide1:0.fileName = "V:\Roboczy!\ISO i appliance\TrueNAS-12.0-RELEASE.iso"
ide1:0.present = "TRUE"
usb.present = "TRUE"
ehci.present = "TRUE"
ethernet0.connectionType = "custom"
ethernet0.addressType = "generated"
ethernet0.vnet = "VMnet2"
ethernet0.displayName = "VMnet2"
ethernet0.virtualDev = "e1000"
ethernet4.connectionType = "custom"
ethernet4.addressType = "generated"
ethernet4.vnet = "VMnet6"
ethernet4.displayName = "VMnet6"
ethernet4.virtualDev = "e1000"
ethernet3.connectionType = "custom"
ethernet3.addressType = "generated"
ethernet3.vnet = "VMnet5"
ethernet3.displayName = "VMnet5"
ethernet3.virtualDev = "e1000"
ethernet2.connectionType = "custom"
ethernet2.addressType = "generated"
ethernet2.vnet = "VMnet4"
ethernet2.displayName = "VMnet4"
ethernet2.virtualDev = "e1000"
ethernet1.connectionType = "custom"
ethernet1.addressType = "generated"
ethernet1.vnet = "VMnet3"
ethernet1.displayName = "VMnet3"
ethernet1.virtualDev = "e1000"
ethernet0.present = "TRUE"
ethernet4.present = "TRUE"
ethernet3.present = "TRUE"
ethernet2.present = "TRUE"
ethernet1.present = "TRUE"
extendedConfigFile = "freenas.vmxf"
floppy0.present = "FALSE"
numa.autosize.cookie = "80082"
numa.autosize.vcpu.maxPerVirtualNode = "8"
uuid.bios = "56 4d 91 60 c6 03 50 e2-04 21 ed b1 0e 01 c7 7b"
uuid.location = "56 4d 91 60 c6 03 50 e2-04 21 ed b1 0e 01 c7 7b"
nvme0:0.redo = ""
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
usb.pciSlotNumber = "32"
ethernet0.pciSlotNumber = "192"
ethernet1.pciSlotNumber = "224"
ethernet2.pciSlotNumber = "256"
ethernet3.pciSlotNumber = "1184"
ethernet4.pciSlotNumber = "1216"
sound.pciSlotNumber = "38"
ehci.pciSlotNumber = "39"
vmci0.pciSlotNumber = "40"
nvme0.pciSlotNumber = "160"
svga.vramSize = "268435456"
vmotion.checkpointFBSize = "134217728"
vmotion.checkpointSVGAPrimarySize = "268435456"
vmotion.svga.mobMaxSize = "268435456"
vmotion.svga.graphicsMemoryKB = "262144"
ethernet0.generatedAddress = "00:0c:29:01:c7:7b"
ethernet0.generatedAddressOffset = "0"
ethernet1.generatedAddress = "00:0c:29:01:c7:85"
ethernet1.generatedAddressOffset = "10"
ethernet2.generatedAddress = "00:0C:29:01:C7:8F"
ethernet2.generatedAddressOffset = "20"
ethernet3.generatedAddress = "00:0C:29:01:C7:99"
ethernet3.generatedAddressOffset = "30"
ethernet4.generatedAddress = "00:0C:29:01:C7:A3"
ethernet4.generatedAddressOffset = "40"
vmci0.id = "234997627"
monitor.phys_bits_used = "45"
cleanShutdown = "TRUE"
softPowerOff = "TRUE"
usb:1.speed = "2"
usb:1.present = "TRUE"
usb:1.deviceType = "hub"
usb:1.port = "1"
usb:1.parent = "-1"
nvme0:4.fileName = "freenas-3.vmdk"
nvme0:1.fileName = "freenas-2.vmdk"
nvme0:3.fileName = "freenas-2.vmdk"
nvme0:2.fileName = "freenas-1.vmdk"
nvme0:2.redo = ""
nvme0:3.redo = ""
nvme0:1.redo = ""
nvme0:4.redo = ""
guestOS.detailed.data = "bitness='64' distroName='FreeBSD' familyName='FreeBSD' kernelVersion='12.2-RC3'"
checkpoint.vmState = ""
scsi0.virtualDev = "lsilogic"
scsi0.present = "TRUE"
scsi0:2.fileName = "freenas-1.vmdk"
scsi0:3.fileName = "freenas-2.vmdk"
scsi0:0.fileName = "freenas.vmdk"
scsi0:1.fileName = "freenas-0.vmdk"
scsi0:2.redo = ""
scsi0:1.redo = ""
scsi0:3.redo = ""
scsi0:0.redo = ""
scsi0.pciSlotNumber = "16"
scsi0:0.present = "TRUE"
ide1:0.startConnected = "FALSE"
scsi0:3.present = "TRUE"
scsi0:4.fileName = "freenas-3.vmdk"
scsi0:4.present = "TRUE"
scsi0:1.present = "TRUE"
scsi0:2.present = "TRUE"
scsi0:4.redo = ""
usb:0.present = "TRUE"
usb:0.deviceType = "hid"
usb:0.port = "0"
usb:0.parent = "-1"

Po zmianach:

.encoding = "windows-1250"
config.version = "8"
virtualHW.version = "18"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
vmci0.present = "TRUE"
hpet0.present = "TRUE"
nvram = "freenas.nvram"
virtualHW.productCompatibility = "hosted"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.suspend = "soft"
powerType.reset = "soft"
displayName = "freenas"
guestOS = "freebsd-64"
tools.syncTime = "FALSE"
sound.autoDetect = "TRUE"
sound.fileName = "-1"
sound.present = "TRUE"
numvcpus = "8"
cpuid.coresPerSocket = "8"
memsize = "8192"
nvme0.present = "TRUE"
nvme0:0.fileName = "freenas.vmdk"
ide1:0.deviceType = "cdrom-image"
ide1:0.fileName = "V:\Roboczy!\ISO i appliance\TrueNAS-12.0-RELEASE.iso"
ide1:0.present = "TRUE"
usb.present = "TRUE"
ehci.present = "TRUE"
ethernet0.connectionType = "custom"
ethernet0.addressType = "generated"
ethernet0.vnet = "VMnet2"
ethernet0.displayName = "VMnet2"
ethernet0.virtualDev = "vmxnet3"
ethernet4.connectionType = "custom"
ethernet4.addressType = "generated"
ethernet4.vnet = "VMnet6"
ethernet4.displayName = "VMnet6"
ethernet4.virtualDev = "vmxnet3"
ethernet3.connectionType = "custom"
ethernet3.addressType = "generated"
ethernet3.vnet = "VMnet5"
ethernet3.displayName = "VMnet5"
ethernet3.virtualDev = "vmxnet3"
ethernet2.connectionType = "custom"
ethernet2.addressType = "generated"
ethernet2.vnet = "VMnet4"
ethernet2.displayName = "VMnet4"
ethernet2.virtualDev = "vmxnet3"
ethernet1.connectionType = "custom"
ethernet1.addressType = "generated"
ethernet1.vnet = "VMnet3"
ethernet1.displayName = "VMnet3"
ethernet1.virtualDev = "vmxnet3"
ethernet0.present = "TRUE"
ethernet4.present = "TRUE"
ethernet3.present = "TRUE"
ethernet2.present = "TRUE"
ethernet1.present = "TRUE"
extendedConfigFile = "freenas.vmxf"
floppy0.present = "FALSE"
numa.autosize.cookie = "80082"
numa.autosize.vcpu.maxPerVirtualNode = "8"
uuid.bios = "56 4d 91 60 c6 03 50 e2-04 21 ed b1 0e 01 c7 7b"
uuid.location = "56 4d 91 60 c6 03 50 e2-04 21 ed b1 0e 01 c7 7b"
nvme0:0.redo = ""
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
usb.pciSlotNumber = "32"
ethernet0.pciSlotNumber = "33"
ethernet1.pciSlotNumber = "34"
ethernet2.pciSlotNumber = "35"
ethernet3.pciSlotNumber = "36"
ethernet4.pciSlotNumber = "37"
sound.pciSlotNumber = "38"
ehci.pciSlotNumber = "39"
vmci0.pciSlotNumber = "40"
nvme0.pciSlotNumber = "160"
svga.vramSize = "268435456"
vmotion.checkpointFBSize = "134217728"
vmotion.checkpointSVGAPrimarySize = "268435456"
vmotion.svga.mobMaxSize = "268435456"
vmotion.svga.graphicsMemoryKB = "262144"
ethernet0.generatedAddress = "00:0c:29:01:c7:7b"
ethernet0.generatedAddressOffset = "0"
ethernet1.generatedAddress = "00:0c:29:01:c7:85"
ethernet1.generatedAddressOffset = "10"
ethernet2.generatedAddress = "00:0C:29:01:C7:8F"
ethernet2.generatedAddressOffset = "20"
ethernet3.generatedAddress = "00:0C:29:01:C7:99"
ethernet3.generatedAddressOffset = "30"
ethernet4.generatedAddress = "00:0C:29:01:C7:A3"
ethernet4.generatedAddressOffset = "40"
vmci0.id = "234997627"
monitor.phys_bits_used = "45"
cleanShutdown = "TRUE"
softPowerOff = "TRUE"
usb:1.speed = "2"
usb:1.present = "TRUE"
usb:1.deviceType = "hub"
usb:1.port = "1"
usb:1.parent = "-1"
nvme0:4.fileName = "freenas-3.vmdk"
nvme0:1.fileName = "freenas-2.vmdk"
nvme0:3.fileName = "freenas-2.vmdk"
nvme0:2.fileName = "freenas-1.vmdk"
nvme0:2.redo = ""
nvme0:3.redo = ""
nvme0:1.redo = ""
nvme0:4.redo = ""
guestOS.detailed.data = "bitness='64' distroName='FreeBSD' familyName='FreeBSD' kernelVersion='12.2-RC3'"
checkpoint.vmState = ""
scsi0.virtualDev = "lsilogic"
scsi0.present = "TRUE"
scsi0:2.fileName = "freenas-1.vmdk"
scsi0:3.fileName = "freenas-2.vmdk"
scsi0:0.fileName = "freenas.vmdk"
scsi0:1.fileName = "freenas-0.vmdk"
scsi0:2.redo = ""
scsi0:1.redo = ""
scsi0:3.redo = ""
scsi0:0.redo = ""
scsi0.pciSlotNumber = "16"
scsi0:0.present = "TRUE"
ide1:0.startConnected = "FALSE"
scsi0:3.present = "TRUE"
scsi0:4.fileName = "freenas-3.vmdk"
scsi0:4.present = "TRUE"
scsi0:1.present = "TRUE"
scsi0:2.present = "TRUE"
scsi0:4.redo = ""
usb:0.present = "TRUE"
usb:0.deviceType = "hid"
usb:0.port = "0"
usb:0.parent = "-1"

Tak wyglądają prędkości po zmianie w iperf:

Dopisanie adresu konsoli ShadowControl do instalatora agenta

Dzięki programowi Orca można osiągnąć taki efekt, że w trakcie kreatora instalacji nie trzeba podawać adresu serwera ShadowControl przy instalacji agenta, ponieważ może on być zapisany wewnątrz instalatora i wygląda to tak:

Dzięki temu można zainstalować agenta i po instalacji on sam się podepnie do konsoli. Program Orca możemy znaleźć w Windows 10 SDK – wystarczy przy instalacji zaznaczyć opcję MSI Tools i nic innego. Dzięki temu w folderze Windows 10 SDK będziemy mieć instalator do Orca:

Wystarczy przejść instalator i będziemy mieli program Orca na swoim komputerze. Po tym należy go otworzyć, przeciągnąć na niego instalator ShadowControl, a następnie wybrać po lewej pole Property, a następnie kliknąć na środku na wolne pole PPM i wybrać opcję Add row…

Następnie w polu Property należy zdefiniować APPL_ADDR i w polu Value wpisać adres IP lub FQDN naszej konsoli ShadowControl. Po tym zapisujemy i w File > Save nasz plik instalacyjny.

Po tym taki instalator możemy zainstalować przez GPO lub przez np. polecenie msiexec:

msiexec /qn /package ShadowControl_Installer.msi

Dodawanie zdjęć użytkowników w Active Directory i Exchange 2019

Nie wiem jak Wam, ale mi się taki klimat podoba. Niestandardowe tło z logiem firmy, zdjęcie pracownika na ekranie logowania przy koncie użytkownika…to sprawia, że jak ktoś patrzy na takie środowisko z boku to myśli, że wdrażał to jakiś profesjonalista, który dba o wszystkie szczegóły i w sumie w mojej pracy to jest to, do czego dążę, czyli być najlepszym w swoim rodzaju. Okej, ale dosyć lizania własnych jajek, przejdźmy do konkretów, czyli do tego jak to wdrożyć – zdjęcia użytkowników w AD i w Exchange 2019.
Exchange to taki dodatek do zdjęć użytkowników – tutaj ponownie poczta wygląda profesjonalnie w organizacji, gdy jest wszystko tak poukładane. Efekt w OWA (Outlook Web Access) wygląda tak:

Do tego będą nam potrzebne zdjęcia naszych użytkowników i te zdjęcia muszą być kwadratowe, czyli np. być rozmiaru 500x500px. Nie ma znaczenia czy te zdjęcie jest duże czy małe (byle nie za małe, bo wtedy będzie pikseloza, a nie twarze użytkowników) dlatego, bo i tak te zdjęcia zostaną w pewnym stopniu skompresowane. Zdjęcia, które ja wykorzystałem są z Unsplash i przedstawiają modelów w moim testowym środowisku. Do wycięcia zdjęć można użyć program wg własnego uznania, np. IrfanView czy Adobe Photoshop. Następnie takie pliki użytkowników należy jakoś nazwać. Najlepszym rozwiązaniem jest nazwanie plików imieniem i nazwiskiem, ponieważ wtedy możemy je wykorzystać lepiej w programie, którym będziemy przypisywać zdjęcia do użytkowników w AD, przykład: Radosław.Serba.jpg. Ja skorzystałem z nazwy sAMAccountName, czyli w moim przypadku rserba.jpg (niestety ta opcja nie pozwala na automatyczne dopasowanie obrazków do kont użytkowników w CodeTwo Active Directory Photos.

Zdjęcia w AD

Ogólnie zdjęcie użytkownika jest przechowywane w atrybucie użytkownika thumbnailPhoto i w plaintext można zobaczyć tylko w krótkim fragmencie:

Get-ADUser -Identity Administrator -Properties thumbnailPhoto


DistinguishedName : CN=Administrator,CN=Users,DC=serba,DC=local
Enabled           : True
GivenName         :
Name              : Administrator
ObjectClass       : user
ObjectGUID        : 9dfaf3bb-fcc9-4f09-be3f-b35b95f7f278
SamAccountName    : Administrator
SID               : S-1-5-21-723521058-2419329218-2805022534-500
Surname           :
thumbnailPhoto    : {137, 80, 78, 71...}
UserPrincipalName : [email protected]

W takim razie jak ustawić taki atrybut? Są dwa rozwiązania: przez PowerShell lub za pomocą fajnej aplikacji zrobionej przez CodeTwo.

PowerShell:

Set-ADUser rserba -Replace @{thumbnailPhoto=([byte[]](Get-Content "C:\zdjecia\rserba.jpg" -Encoding byte))}

CodeTwo Active Directory Photos:

Sytuacja jest prosta: instalujemy program z linka, odpalamy, wybieramy OU, w którym chcemy ustawić zdjęcia i zaznaczamy konta, w których chcemy ustawić avatarki, a następnie klikamy Import.

Tutaj wszystko zależy od tego jaki mamy schemat nazw plików obrazków. Jeśli trzymaliśmy się schematu, który proponowałem na początku to wystarczy, że pomiędzy {First name} i {Last name} wstawimy kropkę, więc pole będzie wypełnione tak:
{First name}.{Last name}
Po tym możemy kliknąć przycisk Automatch >.

Co prawda, ja tu już zdjęcia mam, ale to, co mamy tutaj na ekranie to scenariusz, w którym zdjęcia mogły nie być dopasowane i musimy je dopasować ręcznie, więc wystarczy przeciągnąć obrazki z prawej strony do odpowiedniego użytkownika, a następnie kliknąć Next >. Po tym wystarczy sprawdzić, czy zmiany, które chcemy zastosować nam pasują i zaznaczyć checkbox Apply the above settings to all selected images. Wartość jest ustawiona na 100 KB nieprzypadkowo, ponieważ to jest limit wielkości obrazka przechowywany w atrybcie thumbnailPhoto użytkownika.

Gdy to zatwierdzimy to zdjęcia się zaimportują i jedną część mamy gotową. Drugą częścią jest podpięcie gotowego skryptu, który będzie pobierał zdjęcia użytkowników przy wylogowywaniu się. Bez niego obrazki będą się importowały nam na maszynach tylko i wyłącznie, gdy posiadamy na nich lokalne uprawnienia administracyjne, czyli w sumie będzie to działać tylko dla adminów. Mowa tutaj o skrypcie, który także jest na stronie CodeTwo:

[CmdletBinding(SupportsShouldProcess = $true)]Param()
function Test-Null($InputObject) { return !([bool]$InputObject) }
Function ResizeImage() {
    param([String]$ImagePath, [Int]$Quality = 90, [Int]$targetSize, [String]$OutputLocation)
    Add-Type -AssemblyName "System.Drawing"
    $img = [System.Drawing.Image]::FromFile($ImagePath)
    $CanvasWidth = $targetSize
    $CanvasHeight = $targetSize
    #Encoder parameter for image quality
    $ImageEncoder = [System.Drawing.Imaging.Encoder]::Quality
    $encoderParams = New-Object System.Drawing.Imaging.EncoderParameters(1)
    $encoderParams.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter($ImageEncoder, $Quality)
    # get codec
    $Codec = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where { $_.MimeType -eq 'image/jpeg' }
    #compute the final ratio to use
    $ratioX = $CanvasWidth / $img.Width;
    $ratioY = $CanvasHeight / $img.Height;
    $ratio = $ratioY
    if ($ratioX -le $ratioY) {
        $ratio = $ratioX
    }
    $newWidth = [int] ($img.Width * $ratio)
    $newHeight = [int] ($img.Height * $ratio)
    $bmpResized = New-Object System.Drawing.Bitmap($newWidth, $newHeight)
    $graph = [System.Drawing.Graphics]::FromImage($bmpResized)
    $graph.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
    $graph.Clear([System.Drawing.Color]::White)
    $graph.DrawImage($img, 0, 0, $newWidth, $newHeight)
    #save to file
    $bmpResized.Save($OutputLocation, $Codec, $($encoderParams))
    $bmpResized.Dispose()
    $img.Dispose()
}
#get sid and photo for current user
$user = ([ADSISearcher]"(&(objectCategory=User)(SAMAccountName=$env:username))").FindOne().Properties
$user_photo = $user.thumbnailphoto
$user_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
Write-Verbose "Updating account picture for $($user.displayname)..."
#continue if an image was returned
If ((Test-Null $user_photo) -eq $false) {
    Write-Verbose "Success. Photo exists in Active Directory."
    #set up image sizes and base path
    $image_sizes = @(32, 40, 48, 96, 192, 200, 240, 448)
    $image_mask = "Image{0}.jpg"
    $image_base = "C:\ProgramData\AccountPictures"
    #set up registry
    $reg_base = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\{0}"
    $reg_key = [string]::format($reg_base, $user_sid)
    $reg_value_mask = "Image{0}"
    If ((Test-Path -Path $reg_key) -eq $false) { New-Item -Path $reg_key }
    #save images, set reg keys
    ForEach ($size in $image_sizes) {
        #create hidden directory, if it doesn't exist
        $dir = $image_base + "\" + $user_sid
        If ((Test-Path -Path $dir) -eq $false) { $(mkdir $dir).Attributes = "Hidden" }
        #save photo to disk, overwrite existing files
        $file_name = ([string]::format($image_mask, $size))
        $pathtmp = $dir + "\_" + $file_name
        $path = $dir + "\" + $file_name
        Write-Verbose " saving: $file_name"
        $user_photo | Set-Content -Path $pathtmp -Encoding Byte -Force
        ResizeImage $pathtmp $size $size $path
        Remove-Item $pathtmp
        #save the path in registry, overwrite existing entries
        $name = [string]::format($reg_value_mask, $size)
        $value = New-ItemProperty -Path $reg_key -Name $name -Value $path -Force
    }
    Write-Verbose "Done."
}
else { Write-Error "No photo found in Active Directory for $env:username" }

Taki skrypt zapisujemy w pliku, ja nazwałem go UserProfilePicture.ps1 i umieściłem go w folderze NETLOGON. Ten folder jest synchronizowany pomiędzy wszystkimi kontrolerami domeny. Następnie należy stworzyć politykę GPO, w której definiujemy ten skrypt jako skrypt uruchamiany przy wylogowaniu użytkownika, więc należy przejść do Konfiguracja użytkownika > Ustawienia systemu Windows > Skrypty (logowanie/wylogowywanie) > Wylogowywanie.

Następnie, w zakładce Skrypty PowerShell należy wskazać ścieżkę do pliku. Jeśli plik jest umieszczony bezpośrednio w NETLOGON, ścieżka powinna być //<nazwa-domeny>/NETLOGON/<nazwa-skryptu> i tak też jest w moim przypadku.

Po dodaniu można zapisać i zamknąć wszystkie okna, a następnie poczekać, aż polityki się zaaktualizują na komputerach. Po tym kwestia zdjęć na komputerach jest załatwiona.

Zdjęcia w Exchange 2019

Obstawiam, że sposób działania zdjęć jest mniej więcej taki sam w starszych wersjach Exchange 2019 (mam na myśli Exchange 2016 i 2013, bo w 2010 zmiany są ponoć spore). Do wrzucenia zdjęć możemy wykorzystać poniższy skrypt, lecz będziemy w nim musieli zmienić główną gałąź naszego OU, w którym przechowujemy użytkowników (linia 3) oraz ścieżkę do folderu, w którym przechowujemy pliki (linia 7). Poniżej opcja skryptu dla wykorzystywania nazw sAMAccountName:

Import-Module activedirectory

$users = Get-ADUser -Filter "ObjectClass -eq 'user'" -SearchBase 'OU=it.supra.tf,DC=serba,DC=local'
foreach($user in $users)
{
    $samaccountname = $user.SamAccountName
    Set-UserPhoto -Identity $user.SamAccountName -PictureData ([System.IO.File]::ReadAllBytes("C:\Users\rserba.SERBA\Desktop\it.supra.tf\$samaccountname.jpg")) -Confirm:$false
}

Ponadto druga opcja z imionami i nazwiskami oddzielonymi kropką:

Import-Module activedirectory

$users = Get-ADUser -Filter "ObjectClass -eq 'user'" -SearchBase 'OU=it.supra.tf,DC=serba,DC=local'
foreach($user in $users)
{
    $firstname = $user.firstname
    $lastname = $user.lastname
    Set-UserPhoto -Identity $user.SamAccountName -PictureData ([System.IO.File]::ReadAllBytes("C:\Users\rserba.SERBA\Desktop\it.supra.tf\$firstname.$lastname.jpg")) -Confirm:$false
}

Taki skrypt odpalamy w Exchange Management Shell. Jeśli popełniliśmy błędy w nazwie plików lub one nie istnieją, zostaniemy o tym poinformowani przez skrypt:

[PS] C:\Users\rserba.SERBA\desktop>.\UpdateExchangePictureProfile.ps1
Exception calling "ReadAllBytes" with "1" argument(s): "Nie można odnaleźć pliku 'C:\Users\rserba.SERBA\Desktop\it.supra.tf\ferexio.jpg'."
At C:\Users\rserba.SERBA\desktop\UpdateExchangePictureProfile.ps1:7 char:5
+     Set-UserPhoto -Identity $user.SamAccountName -PictureData ([Syste ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FileNotFoundException

Exception calling "ReadAllBytes" with "1" argument(s): "Nie można odnaleźć pliku 'C:\Users\rserba.SERBA\Desktop\it.supra.tf\kszymocha.jpg'."
At C:\Users\rserba.SERBA\desktop\UpdateExchangePictureProfile.ps1:7 char:5
+     Set-UserPhoto -Identity $user.SamAccountName -PictureData ([Syste ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FileNotFoundException

Efekty widać na początku posta 😊

Wdrażanie usługi rejestracji urządzeń sieciowych (NDES)

SCEP (Simple Certificate Enrollment Protocol) nie jest tak popularny w dzisiejszych czasach z tego względu, że większość rozwiązań, na których wdraża się certyfikaty SSL ma już wbudowany interfejs pozwalający na proste wymienienie certyfikatu. SCEP na pewno byłby dobrym rozwiązaniem jeśli w organizacji urządzeń z certyfikatami są setki i chcielibyśmy zaaktualizować na nich wszystkich certyfikaty, lecz nadal trzeba pamiętać, że takowe urządzenie musi to obsługiwać, z czego na ten moment spotkałem się z tym, że tylko FortiGate był w stanie obsłużyć SCEPa.

Funkcja, która pozwala na podpisywanie certyfikatów to NDES (Network Device Enrollment Service) lub po polsku Usługa rejestracji urządzeń sieciowych. Poniżej opiszę po krótce jak to wdrożyć szybko.

Przygotowanie

Na początku należy zacząć od założenia konta dla NDESa, w moim przypadku nazywa się scepuser.

Następnie musimy takie konto dodać do grupy IIS_IUSRS. Ogólnie te konto powinno być w takiej lokalnej grupie, lecz z faktu, że zainstalowałem tą rolę na kontrolerze domeny (czego się nie powinno robić) jest w grupie domenowej.

Z faktu, że to jest jednak kontroler domeny, musiałem w polityce kontrolerów domeny dodać możliwość logowania się w trybie usługi tego konta. Bez tego nie uda nam się skonfigurować roli. Na zrzucie ekranu jest widoczne gdzie można znaleźć te ustawienie.

Poniżej widok jak to wygląda po skonfigurowaniu w polityce kontrolerów domeny. W normalnym wypadku to się definiuje dla serwera, na którym jest instalowana rola NDES.

Podobnie należy zezwolić na logowanie lokalne do serwera.

Jeśli tego nie zrobimy, w trakcie konfigurowania usługi po wybraniu naszego konta serwisowego dostaniemy to:

Konfiguracja

Po przygotowaniu konta należy zainstalować rolę Usługa rejestracji urządzeń sieciowych.

Po zainstalowaniu należy kliknąć Konfiguruj usługi certyfikatów Active Directory na serwerze docelowym.

Tutaj trzeba wskazać konto administracyjne, które pozwala na skonfigurowanie roli. Najlepiej skorzystać z konta administratora.

Z listy zaznaczamy nowo zainstalowaną rolę.

Dopiero tutaj wskazujemy użytkownika scepuser.

Następnie musimy podać dane do nowego urzędu rejestrowania. Dosyć prosta sprawa.

Teraz wybieramy dostawcę klucza podpisu i długość klucza. Można zostawić domyślne.

I to byłoby na tyle, klikamy Konfiguruj i mamy gotowe…prawie.

Tutaj potwierdzenie, że to, co zrobiliśmy się udało.

Zanim NCEP będzie działał, musimy dodać szablon do podpisywania, z którego będziemy korzystać. W sumie to przy instalacji NCEPa on sam się wdraża, więc jedynie musimy się upewnić, że konfiguracja jest OK. Otwieramy urząd certyfikacji poprzez przystawkę certsrv, wybieramy Szablony certyfikatów, klikamy prawym na wolne miejsce i Zarządzaj.

W konfiguracji szablonów wybieramy szablon IPSec (żądanie offline), klikamy prawym i Właściwości.

W ustawieniach zabezpieczeń konto scepuser musi mieć uprawnienia do rejestracji.

Jeśli tak wygląda szablon – w sumie to nie musimy nic więcej robić, bo to oznacza, że wszystko jest już gotowe.

Testowanie SCEP

Ogólnie do przetestowania są płatne narzędzia, z takich darmowych to znalazłem post, który opisuje użycie tutaj. Z tego, co widziałem NCEPa używa się przy instalacji Microsoft Intune, więc myślę, że pod kątem tego narzędzia będzie najlepiej testować.

Stormshield jako podrzędny urząd certyfikacji dla AD CS w filtrze SSL i dodawanie certyfikatu SSL dla strony

Okej, ten sam temat przerabialiśmy wcześniej z FortiGate i teraz robimy to na Stormshieldzie. Nie chce mi się instalować przez GPO wbudowanego certyfikatu ze Stormshielda na każdym komputerze. Łatwiej jest podpisać certyfikat dla podrzędnego CA, którym byłby Stormshield i temat załatwiony, bo główne CA i tak mają wszyscy w komputerach po instalacji AD CS. Druga rzecz to jest to, by stronka wyświetlała stronkę Stormshielda jako bezpieczną. Zaczniemy najpierw od tego. Przykłady są bazowane na SNS 4.0.3, ale równie dobrze to będzie działało zarówno na starszych jak i nowszych wersjach.

Instalowanie certyfikatu SSL dla strony

To, jak uzyskać taki certyfikat z AD CSa opisałem tutaj, więc zapraszam do lekturki. Po tym, gdy certyfikat już mamy wyeksportowany należy przejść do strony Stormshielda, zalogować się i przejść do zakładki Konfiguracja, następnie z lewej strony wybrać Obiekty > Certyfikaty – PKI, a następnie kliknąć Dodaj > Importuj plik.

Następnie należy wskazać ścieżkę do plików certyfikatu (certyfikat musi mieć zintegrowany klucz prywatny, więc musimy skorzystać z P12 (PKCS #12). Jeśli to jest potrzebne – podajemy hasło i klikamy Importuj.

Poniżej coś, na co się naciąłem przy generowaniu certyfikatów z Windowsa: jeśli nie zaimportujemy podpisanego certyfikatu do naszego lokalnego magazynu certyfikatów z komputera, na którym generowaliśmy CSR to nie będzie widać drzewka z urzędu certyfikacji (przykład po lewej). Po prawej jest przykład tego, jak to powinno wyglądać.

Potem należy przejść do Użytkownicy > Portal uwierzytelnienia, wybrać zakładkę Portal uwierzytelnienia i w sekcji Serwer SSL wybrać nasz nowy certyfikat, a po tym zatwierdzić ustawienia.

Po otwarciu na nowo przeglądarki strona powinna być jako bezpieczna…

Nawet po adresie IP, jeśli wykorzystaliście mój przykład z generowaniem CSR z CLI z tego posta.

Na wypadek fuck-upu

Jeśli zrobicie tak jak ja i odwołacie certyfikat, który aktualnie jest używany na Stormshieldzie to jesteście w czarnej D. W takiej sytuacji jedyne, co pozostaje do podpięcie się przez PuTTY po SSH do Stormshielda i usunięcie certyfikatu z CLI. Należy otworzyć plik konfiguracyjny za pomocą polecenia:

joe ConfigFiles/auth

Następnie należy w pliku wyczyścić w sekcji [Config] wartość pola SslCertificate=:

Po tym wystarczy zapisać skrótem Ctrl+K+X i wykonać polecenie enauth. To zrestartuje portal i znowu będziemy mieć samopodpisany certyfikat SSL.

Certyfikat SSL dla podrzędnego CA w celu filtrowania SSL

Generowanie certyfikatu i import wygląda podobnie. Szablon w pliku tekstowym wygląda tak:

[Version]
Signature="$Windows NT$"
 
[NewRequest]
;w tym polu określamy dane certyfikatu
Subject = "C=PL, O=it.supra.tf, CN=stormshield.stormshield.local, L=Tychy, S=śląskie, [email protected], OU=IT"
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
KeySpec = AT_KEYEXCHANGE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = CERT_CRL_SIGN_KEY_USAGE
HashAlgorithm = SHA256

[RequestAttributes]
CertificateTemplate=SubCA

Równie dobrze można to zrobić z GUI. W praktyce działa tak samo. Sposób wystawiania takiego certyfikatu i eksportowania go opisałem tutaj. Tutaj część o robieniu tego przez CLI. Certyfikat dla podrzędnego CA importuje się tak samo, jak ten do stronki:

Następnie w Kontrola aplikacji > Analiza protokołów > SSL należy wybrać Pokaż ustawienia wspólne dla wszystkich profili i wybrać w Główny urząd certyfikacyjny (CA) nasz urząd certyfikacji:

Na koniec wystarczy dodać hasło do certyfikatu i zapisać.

Tak wygląda efekt końcowy: