Skip Ribbon Commands
Skip to main content

Ondrej Sevecek's Blog

:

Engineering and troubleshooting by Directory Master!
Ondrej Sevecek's Blog > Posts > Ukládání tajných informací a hesel pro skripty a naplánované úlohy
srpen 06
Ukládání tajných informací a hesel pro skripty a naplánované úlohy

To je věčná otázka. Máte skript, nebo naplánovanou úlohu, nebo třeba dokonce službu, a potřebujete, aby měla k dispozici nějakou tajnou informaci, jako je třeba heslo k nějakému účtu, nebo šifrovací klíč k datům v databázi. Jak ho na počítači "bezpečně" uložíte?

Internet se hemží různými návody, jak uložit například PSCredentials do XML, nebo používat jakýsi cizí Get-PnPStoredCredential a ukládat hesla do takového toho zabudovaného trezoru na hesla.

To jsou všechno zbytečné obezličky. Já používám rovnou to nejjednodušší, přímo DPAPI, nad kterým jsou všechny ty ostatní metody stejně postaveny, a které je používáno libovolnými systémovými službami i aplikacemi. DPAPI je už minimálně od Windows 2000 zabudováno do systému a .NET Framework má pro něho už od verze 2.0 (a PowerShell 2.0 tedy také) statickou metodu, takže pohoda.

Jednoduché vysvětlení DPAPI

Detaily si můžete poslechnout na letošním hackerfest od Michaela Grafnettera. Mě zajímá princip. DPAPI (Data Protection API) vám jednoduše zašifruje cokoliv si řeknete svým náhodným klíčem. Každý počítač má svůj náhodný klíč per-machine. Tento klíč je uložen v systémových registrech počítače, nebo v systémovém profilu na disku (%windir%\system32\config\systemprofile) - Grafi bude vědět přesněji - je prostě na disku.

Každý uživatel má ve svém profilu (nejspíš opět v jeho registrech) také svůj náhodný profilový klíč, tedy per-user-profile. Schválně píšu per-user-profile, protože bez cestovního profilu máte na každém počítači jiný klíč, zatímco s profilem cestovním (roaming profile) máte všude klíč stejný. Také je důležité, že se jedná o profil, protože například IIS pracovní procesy (app pool) se ve výchozím stavu startují bez profilu a pokud u nich chcete DPAPI využívat, musíte jim profil zapnout ve vlastnostech apppool (processModel.loadUserProfile).

V uživatelském profilu je tento šifrovací náhodný klíč chráněn ještě navíc uživatelovým přihlašovacím heslem a paralelně AD záchraným klíčem. Je to tedy imunní proti offline krádeži profilu a díky AD klíči i proti resetu hesla správcem v AD. Lokální účty naopak nejsou imunní proti reset hesla správcem. Opět Grafi bude vědět detaily, předpokládám.

DPAPI umí použít buď tento per-machine, nebe per-user-profile náhodný klíč k tomu, aby vám zašifrovalo nějaká data. Pokud si to necháte zašifrovat per-machine, na daném počítači si to dešifruje úplně kdokoliv, nemusí být ani členem skupiny Administrators (příkladem je například SharePoint passphrase, jak už jsem tu o ní dávno psal). Pokud se k OS disku někdo dostane offline, také to dešifruje.

Pokud použijete klíč per-user-profile, tak to dešifruje pouze stejný uživatel na stejném počítači pokud nemá cestovní profil (roaming profile). Pokud cestovní profil má, tak na libovolném jiném počítači také.

Jak to použít z PowerShellu? Nejprve jeho obvyklé typy a práce s nimi

Člověk sbírá tajné údaje z hvězdiček jako SecureString:

$tajemstvi = Read-Host 'Zadej heslo nebo jine tajemstvi' -AsSecureString
$tajemstvi.GetType().FullName   # System.Security.SecureString

Nebo si to zkonvertuje z plaintextu:

$tajemstvi = ConvertTo-SecureString 'tajemstvi co je tajne' -AsPlainText -Force
$tajemstvi.GetType().FullName   # System.Security.SecureString

Nebo se zeptá rovnou na PSCredential:

$loginHeslo = Get-Credential -UserName 'gps\kamil' -Message 'Zadej heslo chlapku'
$loginHeslo.GetType().FullName   # System.Management.Automation.PSCredential

PSCredential lze rovnou založit přímo z plaintextu:

$loginHeslo = New-Object System.Management.Automation.PSCredential 'gps\kamil', (ConvertTo-SecureString 'Pa$$w0rd' -AsPlainText -Force)
$loginHeslo.GetType().FullName   # System.Management.Automation.PSCredential
$loginHeslo.UserName             # string
$loginHeslo.Password             # System.Security.SecureString

Poznámka. Už tady se používá DPAPI, sice jen paměťové, ale hodnota System.Security.SecureString je uložena v paměti opět v zašifrované formě (per-machine), aby to nešlo jenom tak prohlížet.

Ale bez problémů heslo dostanete ven i v čisté formě plaintextu:

$loginHeslo.GetNetworkCredential().Password   # Pa$$w0rd

Takže práce s tajnými údaji v PowerShellu by byla. Jak to uložit na disk, nebo do registrů, nebo do databáze?

Jak si nechat zašifrovat data pomocí DPAPI?

Jednoduše. Použijeme třídu System.Security.Cryptography.ProtectedData. Žádný klíč ani heslo nepotřebujete. Všechno je v počítači nebo ve vašem profilu samo od sebe. Jediné co ještě můžete přidat, když moooc chcete, je sůl (salt), neboli inicializační vektor (IV). Akorát tím zajistíte, že dvě stejné hodnoty by nemusely být zašifrovány stejným klíčem stejně (ale ony stejně nebudou, protože DPAPI samo vždycky zasolí nějakým náhodným číslem). Spíše tím zkomplikujete kreking, řekněme. Osobně necítím potřebu, vždycky je tam systémová sůl.

$tajnyText = 'Tohle je megasuper tajne'
$bajty = [System.Text.Encoding]::Unicode.GetBytes($tajnyText)

# per-user-profile
$zasifrovaneBajty = [System.Security.Cryptography.ProtectedData]::Protect($bajty, $null, 'CurrentUser')
[Convert]::ToBase64String($zasifrovaneBajty)

# per-machine
$zasifrovaneBajty = [System.Security.Cryptography.ProtectedData]::Protect($bajty, $null, 'LocalMachine')
[Convert]::ToBase64String($zasifrovaneBajty)

Dobrá, slanější je vždycky chutnější:

$tajnyText = 'Tohle je megasuper tajne'
$sul = 'Hlavne to Jiriku nepresol'
$bajty = [System.Text.Encoding]::Unicode.GetBytes($tajnyText)
$iv = [System.Text.Encoding]::Unicode.GetBytes($sul)

# per-user-profile
$zasifrovaneBajty = [System.Security.Cryptography.ProtectedData]::Protect($bajty, $iv, 'CurrentUser')
$tohleSeDaUlozit = [Convert]::ToBase64String($zasifrovaneBajty)
$tohleSeDaUlozit    # proste pekny ulozitelny text ve formatu Base64

# per-machine
$zasifrovaneBajty = [System.Security.Cryptography.ProtectedData]::Protect($bajty, $iv, 'LocalMachine')
$tohleSeDaUlozit = [Convert]::ToBase64String($zasifrovaneBajty)
$tohleSeDaUlozit    # proste pekny ulozitelny text ve formatu Base64

Předpokládám, že uložit to Base64 zašifrované monstrum už zvládnete sami, ne?

Dešifrování DPAPI chráněných Base64 řetězců

Co jste si před chvilkou uložili ve formě Base64 zašifrovaného textu, si načtete a podle chuti přidáte původní inicializační vektor (IV):

$sul = 'Hlavne to Jiriku nepresol'
$iv = [System.Text.Encoding]::Unicode.GetBytes($sul)

# per-user-profile
$desifrovaneBajty = [System.Security.Cryptography.ProtectedData]::Unprotect(([Convert]::FromBase64String($tohleSeDaUlozit)), $iv, 'CurrentUser')
[System.Text.Encoding]::Unicode.GetString($desifrovaneBajty)

# per-machine
$desifrovaneBajty = [System.Security.Cryptography.ProtectedData]::Unprotect(([Convert]::FromBase64String($tohleSeDaUlozit)), $iv, 'LocalMachine')
[System.Text.Encoding]::Unicode.GetString($desifrovaneBajty)

A jak by řekl Babica, když nemáte inicializační vektor, dáte tam něco jinýho. Akorát dejte bacha, aby vám z toho nevyšla nějaká ta jeho dobrota.

Comments

Re: Ukládání tajných informací a hesel pro skripty a naplánované úlohy

Přežijí data uložená per-user-profile i Sysprep?
Pupik on 7.8.2018 12:49

Hello

V PowerShellu existují dvě statické metody pro práci s DPAPI - ProtectToMemory() a UnprotectFromMemory(). Tyto metody umožňují zašifrovat nebo dešifrovat data pomocí DPAPI. https://tunnel-rush.co/
Emma Orabelle on 6.5.2023 10:06

Re: Ukládání tajných informací a hesel pro skripty a naplánované úlohy

V PowerShellu je možné pracovat s tajnými údaji pomocí typu SecureString nebo PSCredential. SecureString umožňuje uložení tajného textu, zatímco PSCredential uchovává jméno a heslo. Oba typy mohou být zašifrovány pomocí DPAPI https://eggy-car.com
Timothy Ferriss on 20.5.2023 10:01

Re: Ukládání tajných informací a hesel pro skripty a naplánované úlohy

Jednou z možností je využít Data Protection API (DPAPI), které je součástí systému Windows od verze 2000. DPAPI umožňuje zašifrování dat pomocí náhodného klíče, který je uložen na počítači https://ovo-game.com
drewbinsky on 30.5.2023 4:37

Re: Ukládání tajných informací a hesel pro skripty a naplánované úlohy

Windows 2000 introduced the Data Protection API (DPAPI). https://dino-game.co
Herbert Giles on 16.6.2023 10:24

Good

I'm glad you shared a lot of interesting information about DPAPI. Thanks to that, I have understood more things about DPAPI and its usage. https://slitherio.online
Mari on 26.6.2023 4:33

a

I have a different opinion on this topic https://unogame.io/ , but I still enjoyed reading this article and learning about the author's perspective.
tommchris on 24.10.2023 10:43

Success

Success and money don't change people; they just make what's already there stronger.
https://ducklife.online/
junn88 on 17.11.2023 3:36

Add Comment

Title


Pole Title nemusíte vyplňovat, doplní se to samo na stejnou hodnotu jako je nadpis článku.

Author *


Pole Author nesmí být stejné jako pole Title! Mám to tu jako ochranu proti spamu. Roboti to nevyplní dobře :-)

Body *


Type number two as digit *


Semhle vyplňte číslici dvě. Předchozí antispemové pole nefunguje úplně dokonale, zdá se, že jsou i spamery, které pochopily, že je občas potřeba vyplnit autora :-)

Email


Emailová adresa, pokud na ni chcete ode mě dostat odpověď. Nikdo jiný než já vaši emailovou adresu neuvidí.

Attachments