---
title: "Tajné hodnoty v konfigurácii a fasádach — Package Toolkit | Roundly"
description: "API kľúče a sady kľúčov čítajte s chybami, ktoré hodnotu nikdy neukážu, a jedným traitom udržte tajné argumenty fasády mimo stack trace."
url: https://roundly-consulting.com/sk/open-source/navody/package-toolkit-for-laravel/tajne-hodnoty
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Package Toolkit for Laravel](https://roundly-consulting.com/sk/open-source/navody/package-toolkit-for-laravel.md)

# Tajné hodnoty v konfigurácii a fasádach

API kľúče, podpisové tajomstvá, heslá a sady kľúčov potrebujú rovnako prísnu kontrolu ako každé iné nastavenie — nesprávne nastavená tajná hodnota sa však nikdy nesmie dostať do chybovej správy, stack trace ani do nástroja na sledovanie chýb. Toolkit pokrýva obe miesta, cez ktoré tajná hodnota balíkom prechádza: čítače konfigurácie aj fasádu.

## Čítače tajných hodnôt

secret(), requireSecret() a secretList() čítajú ako string(), requireString() a list(), s dvoma rozdielmi. Chyby nikdy neukážu hodnotu: nesprávne nastavenú tajnú hodnotu opíšu len jej typom, napríklad Configuration value \[comments.akismet\_key\] must be a non-empty string, \[int\] given., a položka, ktorú $each odmietne, znie must contain only valid items, \[string\] given. Hodnota nie je v správe ani v trace, nech ju nesie ktorákoľvek trieda výnimky (Config::for() / Config::using()). A predvolená hodnota z balíka neexistuje, pretože pre tajnú hodnotu žiadna rozumná nie je: secret() pri nenastavenom kľúči vráti null a secretList() vráti \[\], takže o tom, čo znamená „nenakonfigurované“, rozhodne volajúci; requireSecret() vyhodí is required but missing. Zadaný reťazec sa vráti tak, ako je, nikdy neorezaný:

```php
use RoundlyConsulting\PackageToolkit\Support\Config;

// Required: not set throws "is required but missing"
$apiKey = Config::requireSecret('comments.akismet_key');

// Optional: not set → null, and the caller decides what "not configured" means
$apiKey = Config::secret('comments.akismet_key');

// config('comments.akismet_key') === 12345 — misconfigured, and the message names the type only:
// Configuration value [comments.akismet_key] must be a non-empty string, [int] given.
```

| Hodnota v konfigurácii | secret() | requireSecret() | secretList() |
| --- | --- | --- | --- |
| Reťazec, napr. ' key ' | ' key ' — tak, ako je, nikdy neorezaný | ' key ' | \['key'\] — položky orezané |
| `'a, b'` | 'a, b' | 'a, b' | \['a', 'b'\] |
| `['a', 'b']` | Vyhodí \[array\] given. | Vyhodí \[array\] given. | \['a', 'b'\] |
| Nenastavená: chýbajúca, null, '' alebo len medzery | null | Vyhodí is required but missing. | \[\] |
| `','` | ',' | ',' | \[\] |
| \[\] alebo \[' ', ''\] | Vyhodí \[array\] given. | Vyhodí \[array\] given. | \[\] |
| Int, float, bool, objekt | Vyhodí \[int\] / \[float\] / \[bool\] / \[ClassName\] given. | Rovnako | Vyhodí must be a list of strings …, \[int\] given. |
| Pole s položkou, ktorá nie je reťazcom | Vyhodí \[array\] given. | Rovnako | Vyhodí must contain only string items, \[int\] given. |

## Sady kľúčov

secretList() spracúva hodnotu ako list(): publikované pole alebo zoznam z env oddelený čiarkami, položky orezané, prázdne vypustené, kľúče zahodené. Tajná hodnota s čiarkou preto nemôže byť v zozname z env — publikujte namiesto toho konfiguračné pole. $each dostane každú položku, preto jej parameter označte #\[SensitiveParameter\]: rámec vašej vlastnej closure toolkit skryť nedokáže a closure, ktorá vyhodí výnimku, by inak položku dostala do trace:

```php
use RoundlyConsulting\PackageToolkit\Support\Config;
use SensitiveParameter;

$keys = Config::secretList('comments.signing_keys', static fn (#[SensitiveParameter] string $key): bool => strlen($key) >= 32);
$current = $keys[0] ?? throw new RuntimeException('Set COMMENTS_SIGNING_KEYS.');
```

## Vlastné správy okolo tajnej hodnoty

Každá továrenská metóda InvalidConfigurationException opíše hodnotu zabalenú do SensitiveParameterValue z PHP typom toho, čo obaľuje, nikdy jej obsahom. Využite to, keď váš balík skladá vlastnú správu okolo tajnej hodnoty:

```php
use RoundlyConsulting\PackageToolkit\Exceptions\InvalidConfigurationException;
use SensitiveParameterValue;

throw InvalidConfigurationException::notAString($key, new SensitiveParameterValue($value)); // "[int] given."
```

## Hodnoty v rámcoch stack trace

Každá výnimka si zaznamená argumenty všetkých funkcií na zásobníku, pokiaľ ich v produkcii neodstráni zend.exception\_ignore\_args — a nástroje na sledovanie chýb, ktoré zbierajú argumenty rámcov, print\_r($e) či vlastné loggery trace ich serializujú. Každý parameter, ktorý nesie konfiguračnú hodnotu, má preto #\[SensitiveParameter\]: $default každého čítača, pole odovzdané do Config::for() / ConfigValidator::forArray() aj hodnota v každej továrenskej metóde InvalidConfigurationException. Nesprávne nastavená hodnota sa ukáže v správe bežných čítačov, ktoré ju uvádzajú zámerne, a nikde inde. Čítače tajných hodnôt ju nepustia ani do správy.

## Tajné hodnoty odovzdané cez fasádu

PHP nahradí argument s #\[SensitiveParameter\] objektom SensitiveParameterValue len v rámci funkcie, ktorá atribút deklaruje. Volanie cez bežnú fasádu Laravelu prejde najprv cez Facade::\_\_callStatic($method, $args) a tento rámec nesie všetky argumenty v surovej podobe:

```php
Crypto::constantTimeEquals($known, $userInput);
// #0 CryptoManager->constantTimeEquals(Object(SensitiveParameterValue), Object(SensitiveParameterValue))
// #1 Facade::__callStatic('constantTimeEquals', Array)   ← Array holds $known and $userInput raw
```

getTraceAsString() vypíše pole tohto rámca ako Array, takže tam sa tajná hodnota neukáže. Ukáže sa však všade, kde sa argumenty rámcov serializujú: v nástrojoch na sledovanie chýb, ktoré ich zbierajú, v print\_r($e) / var\_dump($e) či vo vlastných loggeroch trace. Formátovač logov a chybová stránka Laravelu ukazujú len typy argumentov a produkčné zend.exception\_ignore\_args = On argumenty rámcov úplne odstráni, takže k úniku dôjde len pri vypnutom nastavení. To isté volanie cez dependency injection bolo skryté už predtým.

Túto medzeru uzavrie trait RedactsSensitiveArguments: v rámci samotnej fasády skryje presne tie argumenty, ktoré metóda koreňového objektu fasády označuje #\[SensitiveParameter\], a neškodné argumenty nechá viditeľné. Pridajte ho do fasády — nič iné sa nemení:

```php
use Illuminate\Support\Facades\Facade;
use RoundlyConsulting\PackageToolkit\Concerns\RedactsSensitiveArguments;

/**
 * @method static bool constantTimeEquals(string $known, string $userInput)
 *
 * @see CryptoManager
 */
final class Crypto extends Facade
{
    use RedactsSensitiveArguments;

    protected static function getFacadeAccessor(): string
    {
        return CryptoManager::class;
    }
}
```

Neudržiavate žiadny zoznam metód. Jediným zdrojom sú atribúty #\[SensitiveParameter\] koreňového objektu, takže nový tajný parameter sa skryje hneď, ako ho označíte.

## Čo trait pokrýva

| Volanie | Rámec fasády ukáže |
| --- | --- |
| `Crypto::check($secret, $label)` | \['check', \[Object(SensitiveParameterValue), $label\]\] |
| `Crypto::check(label: $label, secret: $secret)` | \['label' => $label, 'secret' => Object(SensitiveParameterValue)\] |
| `Crypto::seal($label, ...$parts)` | Citlivý variadický parameter: každá zozbieraná časť je skrytá, pozičná aj pomenovaná. |
| `Crypto::tag($secret, ...$labels)` | Neškodný variadický parameter vedľa tajnej hodnoty: tajná hodnota je skrytá, popisy zostanú viditeľné. |
| `Crypto::check(secrett: $secret)` | Preklep v pomenovanom argumente: skrytý (volanie aj tak zlyhá s Unknown named parameter). |
| Metóda bez #\[SensitiveParameter\] | Všetky argumenty, ako doteraz. |
| Koreňový objekt fasády sa nepodarí získať (kontajner vyhodí výnimku) | Skryté — argumenty sa skryjú ešte predtým, než sa koreňový objekt získa. |

Trait nahrádza Facade::\_\_callStatic(). Citlivé položky vo vlastnom $args zabalí — backtrace ukazuje aktuálnu hodnotu parametra — a ďalej odovzdá nedotknutú kópiu, takže koreňový objekt dostane skutočné hodnoty.

## Odkiaľ sa berú pozície

Trait reflexiou preskúma typ accessora: triedu alebo rozhranie, ktoré vracia getFacadeAccessor(). Vyhľadanie prebehne raz pre každú triedu fasády a metódu a výsledok sa uchová v cache na celý proces.

- Rozhrania ako accessor — parametre označte na rozhraní. Fasáda vidí rozhranie, nie triedu, ktorá je naň naviazaná.
- Fake a mocky — fake vymenený cez swap() / fake() alebo Mockery mock zo shouldReceive() dostane skutočné argumenty a zaznamená si ich pre svoje asercie. Rámec fasády zostane skrytý aj vtedy, keď prepísaná metóda vo fake atribút vynechá; o vlastný rámec sa stará fake.
- Čo pokryté nie je — accessor, ktorý nie je class-string (alias v kontajneri, napríklad 'crypto'), a metóda, ktorú typ accessora nedeklaruje (makro, ktoré koreňový objekt preposiela cez \_\_call), sa odovzdajú bez skrytia, presne ako pri bežnej fasáde. Tajné hodnoty vnútri objektov odovzdaných ako argumenty — DTO či objekt s prihlasovacími údajmi — sa ukážu ako samotný objekt; skryť ich obsah je úlohou objektu.

## Čo zostáva nezmenené

- Návratové hodnoty a výnimky pochádzajú z koreňového objektu, bez zmeny.
- Prevod skalárnych typov sa zhoduje s Laravelom. Facade::\_\_callStatic() je v súbore bez strict\_types, takže Crypto::base64Encode(123) zo striktného súboru prevedie 123 na '123', kým to isté volanie cez dependency injection vyhodí TypeError. Striktnosť určuje súbor, v ktorom je volanie napísané, preto súbor traitu zámerne strict\_types nemá a tie isté volania zostanú prijaté.
- „A facade root has not been set.“ sa vyhodí ako doteraz, keď nie je nastavená aplikácia.
- Réžia: približne o 170 ns viac na volanie metódy s tajnou hodnotou a približne o 30 ns pri metóde bez nej, oproti približne 250 ns pri volaní bežnej fasády (PHP 8.4 CLI, zapnutý opcache).

## Testovanie skrytia

Rámec fasády čítajte so zapnutými argumentmi rámcov. zend.exception\_ignore\_args je v produkčnom ini zapnuté — aj na CI runneroch, ktoré ho používajú — a rámec bez argumentov nič nedokazuje:

```php
it('hides the secret in the facade frame', function (): void {
    $previous = ini_set('zend.exception_ignore_args', '0');

    try {
        Crypto::base64UrlDecode('*not-base64*');
    } catch (InvalidEncodingException $e) {
        $frame = collect($e->getTrace())->firstWhere('function', '__callStatic');
    } finally {
        ini_set('zend.exception_ignore_args', (string) $previous);
    }

    expect($frame['args'][0])->toBe('base64UrlDecode')
        ->and($frame['args'][1][0])->toBeInstanceOf(SensitiveParameterValue::class);
});
```

## Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

[Ďalšie spôsoby podpory vrátane kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/package-toolkit-for-laravel.md)
