Audit secret dan session untuk tool CLI PowerShell internal perlu dilakukan sebelum script admin atau CLI dipakai luas oleh tim. Masalah yang paling sering muncul bukan pada fitur utama, melainkan pada detail operasional: token disimpan sembarangan, session tidak pernah kedaluwarsa, argumen tidak divalidasi, retry login terlalu longgar, dan log justru membocorkan data sensitif.

Dalam konteks tren tooling PowerShell internal—termasuk meningkatnya kebiasaan membuat helper script dan CLI privat di repository tim—hardening tidak boleh dianggap opsional. Artikel ini membahas pola yang bisa langsung diterapkan pada CLI atau script admin PowerShell: penyimpanan secret yang lebih aman, rotasi token, masking log, validasi input dan path, pembatasan retry, session timeout, least privilege, serta checklist audit yang realistis.

Ancaman nyata pada CLI PowerShell internal

Banyak tool internal diasumsikan aman hanya karena tidak diekspos ke publik. Asumsi ini keliru. Risiko justru sering datang dari developer workstation, jump host, runner CI, atau script yang dijalankan dengan hak akses tinggi.

Skenario serangan yang umum

  • Token bocor lewat log: parameter seperti -ApiKey atau header Authorization ikut tercetak saat debug atau saat exception di-dump mentah.
  • Secret tersimpan sebagai plain text: file konfigurasi di home directory, file CSV, atau transcript PowerShell menyimpan token tanpa proteksi.
  • Session terlalu panjang: token refresh tidak dibatasi, session lokal tidak punya idle timeout, atau cache auth tidak pernah dibersihkan.
  • Path injection atau unsafe file access: argumen path dari user dipakai langsung untuk membaca, menulis, atau menghapus file di luar direktori yang diizinkan.
  • Brute force dan abuse: perintah login atau retry terhadap API internal dapat dipanggil tanpa pembatasan sehingga mempermudah password spraying atau menghabiskan kuota layanan.
  • Privilege terlalu luas: CLI dijalankan dengan account yang punya akses admin global, padahal kebutuhan riil hanya read atau write pada resource tertentu.

Tujuan audit bukan sekadar menemukan kebocoran yang sudah terjadi, tetapi memperkecil blast radius saat workstation terkompromi, log terkumpul ke sistem pusat, atau script dipakai oleh operator non-security.

Audit secret: apa yang harus diperiksa

1. Pemetaan alur secret

Langkah pertama adalah memetakan di mana secret masuk, dipakai, disimpan, dan keluar. Untuk setiap token, API key, password, sertifikat, atau refresh token, jawab pertanyaan berikut:

  • Dari mana secret diperoleh: prompt interaktif, environment variable, vault, file config, atau argumen CLI?
  • Apakah secret disimpan secara persisten atau hanya di memori?
  • Siapa yang dapat membaca secret itu: user lokal, proses lain, service account, atau pipeline CI?
  • Ke mana secret bisa bocor: log, transcript, exception, history shell, temporary file, atau output object?
  • Bagaimana secret dirotasi dan dicabut saat user keluar tim atau token terkompromi?

Tanpa peta ini, perbaikan sering hanya menutup satu titik bocor sambil membiarkan jalur lain tetap terbuka.

2. Hindari anti-pattern penyimpanan secret

Beberapa pola berikut sebaiknya dianggap temuan audit:

  • Menyimpan API key dalam file JSON/YAML tanpa proteksi file permission yang ketat.
  • Meneruskan token melalui argumen command line, karena process listing atau history dapat mengekspos nilainya.
  • Mencetak object request atau header HTTP lengkap saat mode verbose/debug aktif.
  • Menyimpan secret dalam variabel global yang bertahan lama dan mudah ikut tersebar ke scope lain.
  • Mengandalkan SecureString sebagai jaminan keamanan penuh lintas platform. Di praktik modern, perlakukan ini sebagai bantuan API, bukan kontrol keamanan utama.

3. Pola yang lebih aman untuk menyimpan token dan API key

Pilih mekanisme berdasarkan kebutuhan operasional dan tingkat risiko:

  • Vault atau secret manager: pilihan terbaik jika organisasi sudah punya layanan seperti secret store internal, password manager enterprise, atau cloud secret manager. Kelebihannya audit trail dan rotasi lebih mudah.
  • Credential store OS: layak untuk tool desktop atau workstation jika secret perlu bertahan antar sesi, asalkan akses dibatasi ke user yang tepat.
  • Environment variable sementara: cocok untuk runtime singkat atau pipeline CI, tetapi tetap berisiko bocor ke child process, log build, atau dump environment.
  • Prompt interaktif tanpa persistensi: paling aman untuk operasi sensitif sesekali, tetapi kurang nyaman untuk otomasi.

Prinsip utamanya: hindari penyimpanan plain text dan minimalkan persistensi. Jika tool hanya butuh token selama satu proses berjalan, jangan simpan ke disk.

4. Contoh pengambilan secret tanpa mengekspos ke output

param(
    [Parameter(Mandatory = $true)]
    [string]$ApiBaseUrl,

    [Parameter(Mandatory = $false)]
    [switch]$UseInteractiveLogin
)

function Get-AccessToken {
    param([switch]$Interactive)

    if ($Interactive) {
        $secure = Read-Host 'Masukkan access token' -AsSecureString
        $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
        try {
            return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
        }
        finally {
            [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
        }
    }

    if ($env:INTERNAL_API_TOKEN) {
        return $env:INTERNAL_API_TOKEN
    }

    throw 'Token tidak ditemukan. Gunakan login interaktif atau set environment variable sementara.'
}

$token = Get-AccessToken -Interactive:$UseInteractiveLogin

$headers = @{ Authorization = "Bearer $token" }
# Jangan Write-Host $headers atau dump request mentah ke log

Contoh di atas bukan solusi sempurna, tetapi menunjukkan dua prinsip penting: secret tidak diteruskan sebagai argumen command line, dan secret tidak dicetak ke log.

5. Rotasi secret dan pencabutan akses

Audit yang baik selalu memeriksa apakah rotasi bisa dilakukan tanpa mengubah kode. Idealnya:

  • Token memiliki masa berlaku pendek.
  • Refresh token, jika ada, dibatasi dan dapat dicabut.
  • CLI membaca secret dari sumber eksternal, bukan hardcoded.
  • Ketika token diganti, operator tidak perlu mengedit script.

Jika tool mengandalkan API key jangka panjang yang sama untuk seluruh tim, itu temuan serius. Selain sulit diaudit, kebocoran satu key berarti akses semua orang ikut terdampak.

Audit session dan autentikasi

1. Bedakan auth, session, dan cache

Pada CLI internal, ketiganya sering tercampur. Auth adalah proses membuktikan identitas. Session adalah konteks otorisasi yang dipakai setelah login. Cache adalah penyimpanan lokal untuk mengurangi login berulang. Ketika implementasi tidak membedakan tiga hal ini, session sering menjadi terlalu panjang dan sulit dicabut.

2. Terapkan session timeout yang jelas

Setidaknya pertimbangkan dua jenis timeout:

  • Absolute timeout: session selalu berakhir setelah durasi tertentu, walaupun tetap aktif.
  • Idle timeout: session berakhir jika tidak ada aktivitas dalam periode tertentu.

Untuk CLI admin, pendekatan ini masuk akal karena banyak operasi bersifat sensitif dan dijalankan dari workstation. Timeout mencegah token lokal tetap valid saat terminal ditinggalkan terbuka atau shell diwariskan ke sesi lain.

3. Contoh cache session sederhana dengan expiry

function Save-SessionCache {
    param(
        [string]$Path,
        [string]$AccessToken,
        [datetime]$ExpiresAt
    )

    $obj = [pscustomobject]@{
        AccessToken = $AccessToken
        ExpiresAt   = $ExpiresAt.ToString('o')
    }

    $json = $obj | ConvertTo-Json -Depth 3
    Set-Content -Path $Path -Value $json -Encoding UTF8
}

function Load-SessionCache {
    param([string]$Path)

    if (-not (Test-Path $Path)) { return $null }

    $raw = Get-Content -Path $Path -Raw
    $session = $raw | ConvertFrom-Json
    $expiresAt = [datetime]::Parse($session.ExpiresAt)

    if ($expiresAt -le (Get-Date)) {
        Remove-Item -Path $Path -Force -ErrorAction SilentlyContinue
        return $null
    }

    return $session
}

Jika cache session disimpan ke disk, audit permission file dan lokasi penyimpanannya. File session di direktori bersama atau lokasi yang ikut dibackup tanpa kontrol bisa menjadi titik bocor baru.

4. Batasi retry dan login attempt

CLI internal sering diberi retry otomatis untuk kenyamanan. Masalahnya, retry tanpa kontrol bisa berubah menjadi alat abuse, baik sengaja maupun tidak sengaja.

  • Batasi jumlah percobaan login interaktif dalam satu sesi proses.
  • Terapkan exponential backoff untuk request auth ke server.
  • Bedakan error yang layak di-retry, seperti timeout jaringan, dari error kredensial yang seharusnya gagal cepat.
  • Jangan looping login tanpa batas di background task atau scheduled job.
function Invoke-WithRetry {
    param(
        [scriptblock]$Action,
        [int]$MaxAttempts = 3
    )

    for ($i = 1; $i -le $MaxAttempts; $i++) {
        try {
            return & $Action
        }
        catch {
            if ($i -ge $MaxAttempts) { throw }
            Start-Sleep -Seconds ([Math]::Pow(2, $i))
        }
    }
}

Retry seperti ini hanya aman jika dipakai untuk operasi yang memang layak diulang. Jangan membungkus semua kegagalan autentikasi dengan retry otomatis.

5. Least privilege untuk session dan account

Session yang aman bukan hanya soal timeout, tetapi juga soal ruang lingkup akses. Audit token dan service account untuk memastikan:

  • Scope API sekecil mungkin.
  • CLI read-only tidak memakai token write/admin.
  • Perintah berbahaya memerlukan konfirmasi tambahan atau konteks auth yang berbeda.
  • Tool yang dijalankan di CI memakai identitas non-interaktif terpisah dari akun manusia.

Jika satu token bisa membaca, menulis, menghapus, dan mengelola IAM sekaligus, audit seharusnya menandainya sebagai desain yang terlalu luas.

Validasi input, path, dan pencegahan abuse

1. Validasi argumen secara ketat

PowerShell memudahkan pembuatan parameter, tetapi banyak CLI tetap menerima string mentah lalu memprosesnya tanpa pemeriksaan cukup. Gunakan validasi sedini mungkin:

  • Whitelist nilai yang diizinkan untuk environment, action, atau mode operasi.
  • Validasi format identifier, nama file, URL base, dan resource name.
  • Tolak input kosong, karakter kontrol, atau nilai yang terlalu panjang.
  • Hindari menjalankan command shell dari string hasil gabungan input user.
param(
    [Parameter(Mandatory = $true)]
    [ValidateSet('dev', 'staging', 'prod')]
    [string]$Environment,

    [Parameter(Mandatory = $true)]
    [ValidatePattern('^[a-zA-Z0-9._-]{1,64}$')]
    [string]$ResourceName
)

Validasi seperti ini sederhana, tetapi efektif untuk menghentikan banyak input tidak valid sebelum mencapai operasi sensitif.

2. Validasi path untuk mencegah traversal dan salah target

Path adalah sumber bug dan abuse yang sangat sering pada script admin. Masalah biasanya muncul saat script menerima path dari parameter lalu melakukan salin, hapus, ekstrak, atau overwrite tanpa memastikan lokasi target.

Pola audit yang baik:

  • Ubah path ke bentuk absolut sebelum dipakai.
  • Pastikan path masih berada di bawah root yang diizinkan.
  • Tolak path UNC, symlink tertentu, atau parent traversal jika tidak dibutuhkan.
  • Jangan menggabungkan path dengan konkatenasi string biasa.
function Test-SafePath {
    param(
        [string]$BasePath,
        [string]$CandidatePath
    )

    $base = [System.IO.Path]::GetFullPath($BasePath)
    $candidate = [System.IO.Path]::GetFullPath((Join-Path $BasePath $CandidatePath))

    return $candidate.StartsWith($base, [System.StringComparison]::OrdinalIgnoreCase)
}

if (-not (Test-SafePath -BasePath 'C:\InternalTool\workspace' -CandidatePath $InputPath)) {
    throw 'Path berada di luar workspace yang diizinkan.'
}

Pengecekan ini penting terutama pada CLI yang memproses arsip, template, export file, atau operasi cleanup.

3. Batasi operasi berbahaya

Untuk perintah yang melakukan delete, rotate credential, reset access, atau deploy ke environment sensitif, jangan hanya mengandalkan nama perintah. Tambahkan pengaman seperti:

  • Konfirmasi eksplisit untuk target produksi.
  • -Force hanya untuk operator yang sadar risikonya.
  • Dry-run sebagai mode default untuk operasi bulk.
  • Pemeriksaan role atau context sebelum mengeksekusi tindakan destruktif.

Logging, masking, dan debugging yang aman

1. Log harus berguna tanpa membocorkan secret

Tool internal sering gagal di titik ini: saat ada error, developer menambahkan logging sedetail mungkin, lalu tanpa sadar memasukkan token, cookie, body request, atau path sensitif. Audit log perlu memeriksa tiga hal:

  • Apa saja field yang dicatat?
  • Apakah ada mekanisme masking atau redaction?
  • Apakah mode debug berbeda dari mode normal dalam hal sanitasi data?

2. Masking nilai sensitif sebelum ditulis

function Mask-Secret {
    param([string]$Value)

    if ([string]::IsNullOrEmpty($Value)) { return $Value }
    if ($Value.Length -le 6) { return '***' }

    return $Value.Substring(0, 3) + '***' + $Value.Substring($Value.Length - 3)
}

$maskedToken = Mask-Secret $token
Write-Verbose "Menggunakan token: $maskedToken"

Masking bukan pengganti kontrol akses, tetapi jauh lebih baik daripada mencetak nilai utuh. Untuk header HTTP, lebih aman lagi jika field sensitif sama sekali tidak ditulis.

3. Hindari transcript dan exception dump mentah untuk operasi sensitif

Transcript PowerShell berguna untuk audit, tetapi dapat menjadi sumber kebocoran jika seluruh sesi terekam, termasuk input atau output sensitif. Demikian juga $_ | Format-List * -Force sering mencetak terlalu banyak detail. Jika butuh observabilitas, catat metadata yang relevan:

  • timestamp
  • user atau identity yang dipakai
  • nama command
  • target environment
  • hasil operasi dan correlation ID

Bukan body request, token, atau dump penuh object kredensial.

Anti-pattern umum pada tool CLI PowerShell internal

  • Hardcoded secret di dalam script, modul, atau repository privat.
  • Token lewat parameter CLI karena dianggap lebih mudah untuk otomasi.
  • Session cache tanpa expiry yang hanya dihapus manual jika ada masalah.
  • Retry tak terbatas saat login gagal atau API mengembalikan unauthorized.
  • Validasi input minimal dengan asumsi hanya tim internal yang memakai tool.
  • Jalankan semuanya sebagai admin untuk menghindari error permission.
  • Verbose logging mentah terhadap request, response, dan exception.
  • Satu token dipakai bersama tim sehingga audit trail hilang dan rotasi menjadi sulit.

Jika satu atau lebih pola ini ada, tool belum layak dipakai luas tanpa perbaikan.

Checklist audit sebelum tool dipakai tim

Checklist secret dan auth

  1. Semua secret diinventarisasi: sumber, lokasi simpan, scope, expiry, dan owner-nya jelas.
  2. Tidak ada secret hardcoded di source code, file contoh config, atau test artifact.
  3. Token tidak diteruskan melalui command line argument jika bisa dihindari.
  4. Secret dibaca dari vault, credential store, environment sementara, atau prompt interaktif sesuai kebutuhan.
  5. Ada prosedur rotasi dan pencabutan akses yang terdokumentasi.
  6. Scope token mengikuti least privilege dan dipisah per use case.

Checklist session dan abuse prevention

  1. Session cache memiliki absolute timeout dan dibersihkan saat kedaluwarsa.
  2. Retry login dibatasi dan hanya untuk error yang aman diulang.
  3. Operasi sensitif membutuhkan konfirmasi, role yang tepat, atau mode eksplisit.
  4. Identitas CI/service account dipisahkan dari akun manusia.
  5. Perintah berbahaya tidak berjalan otomatis tanpa guardrail.

Checklist input, path, dan logging

  1. Parameter memakai ValidateSet, ValidatePattern, atau validasi eksplisit lain.
  2. Path dinormalisasi, dibatasi ke direktori yang diizinkan, dan tidak dibangun dengan konkatenasi string.
  3. Tool tidak mengeksekusi command dari input mentah tanpa sanitasi yang benar.
  4. Log menerapkan masking atau redaction untuk field sensitif.
  5. Mode verbose/debug tetap aman dan tidak membocorkan token, cookie, atau body sensitif.
  6. Transcript, dump object, dan temporary file diaudit untuk mencegah kebocoran data.

Penutup

Audit secret dan session untuk tool CLI PowerShell internal pada dasarnya adalah pemeriksaan terhadap detail yang sering dianggap kecil, padahal paling mudah disalahgunakan: cara token masuk ke proses, berapa lama session bertahan, bagaimana input divalidasi, dan apa yang tercetak ke log. Hardening yang baik tidak harus rumit, tetapi harus konsisten.

Jika Anda hanya memulai dari beberapa perbaikan, prioritaskan empat hal ini: hapus penyimpanan secret plain text, terapkan session timeout, validasi argumen dan path secara ketat, serta batasi retry dan login attempt. Empat langkah ini sudah menurunkan risiko secara nyata untuk mayoritas CLI atau script admin PowerShell internal.