MAC s vlastnými tajomstvami
Kruhy kľúčov kľúč spravujú: Sentinel ho vygeneruje, uloží, rotuje aj odvolá. Niektoré tajomstvá však patria vašej aplikácii — jedno na každý endpoint webhookov, zašifrované v jeho riadku a rotované s prechodným obdobím, alebo tajomstvo, ktoré vám odovzdal partner, často kratšie než 32 bajtov. Sentinel::macs() nimi podpisuje a overuje. Nepotrebuje kruh kľúčov, tabuľku ani nakonfigurovaný kľúč a nič neukladá:
use RoundlyConsulting\Sentinel\Enums\Algorithm;
use RoundlyConsulting\Sentinel\Facades\Sentinel;
$mac = Sentinel::macs()->sign($payload, $endpoint->secret); // raw bytes, HMAC-SHA256
$mac = Sentinel::macs()->sign($payload, $endpoint->secret, Algorithm::HmacSha512);
$header = 'v1,'.base64_encode($mac); // the wire format is yours
// Receiving: the current secret first, then the ones still in their grace period.
$index = Sentinel::macs()->verify($payload, base64_decode($signature), [$current, $previous]);
abort_if($index === null, 401); // 0 = current, 1 = previousKedy použiť ktoré
| Potrebujete | Použite |
|---|---|
| Aby kľúč spravoval Sentinel — vygeneroval, uložil zašifrovaný, rotoval, odvolal a naviazal na odosielateľa | Kruh pre MAC: keys()->ring($ring)->mac() / verifyMac() (pozri MAC pre vlastné služby) |
| Vlastné tajomstvo uložené tam, kde ho máte, ľubovoľnej dĺžky, počas rotácie viacero naraz | Sentinel::macs() |
Pravidlá
- Obyčajný HMAC: HMAC(tajomstvo, presné bajty správy) s tajomstvom tak, ako je — bez HKDF, bez označenia účelu —, takže ho protistrana v akomkoľvek jazyku zopakuje štandardnou knižnicou.
- Dnu aj von surové bajty: sign() vráti 32, 48 alebo 64 bajtov (hmac-sha256 — predvolený —, hmac-sha384, hmac-sha512) a verify() prijíma surový MAC. Base64, hex či base64url je váš formát prenosu.
- Len HMAC: Ed25519 a ECDSA vyhodia AlgorithmNotAllowedException::notHmacForMacs().
- Akékoľvek neprázdne tajomstvo: neplatí spodná hranica 32 bajtov (tá ostáva pri kľúčoch kruhov) ani horná (HMAC príliš dlhý kľúč zahašuje). Prázdne tajomstvo vyhodí InvalidKeyMaterialException::emptySecret() — pri každom tajomstve v zozname, skôr než sa vypočíta akýkoľvek MAC, aj keď stojí až za zhodou. Za silu tajomstva zodpovedáte vy: vlastné tajomstvá generujte aspoň z 32 náhodných bajtov (random_bytes(32)); krátke tajomstvo, ktoré ste dostali, je len také silné, aké je.
- 1 až 8 tajomstiev: prázdny zoznam vyhodí InvalidKeyMaterialException::noSecrets(), viac než MacsAccessor::MAX_SECRETS (8) ::tooManySecrets() a pole, ktoré nie je zoznamom alebo obsahuje niečo iné než reťazec, InvalidArgumentException.
- Výsledkom je najnižší index tajomstva, ktoré MAC vytvorilo (rovnaké tajomstvo dvakrát → prvé), alebo null. MAC nesprávnej dĺžky — aj prázdny — je null, nikdy výnimka.
- Bez predčasného ukončenia: verify() vypočíta a v konštantnom čase porovná MAC každého tajomstva, slučku nikdy neopustí skôr a index vyberie až z nahromadeného výsledku — z času sa nedá zistiť, ktoré tajomstvo sa zhodovalo, ani či vôbec nejaké. MAC nesprávnej dĺžky sa porovná s nulami správnej dĺžky, takže stojí toľko isto ako nesprávny.
- Rotácia je zoznam: [$current, $previous] počas prechodného obdobia, potom [$current]. Podpisujte len aktuálnym tajomstvom.
- Nič sa neukladá ani neloguje a odmietnutia nikdy neobsahujú tajomstvo, MAC ani správu. Tajomstvá aj MAC sú označené #[\SensitiveParameter] v každej vrstve, vrátane vlastného rámca fasády pri plochom volaní.
Odmietnutia
Každé pravidlo sa skontroluje skôr, než sa vypočíta akýkoľvek MAC. Štyri výnimky Sentinelu majú preložené správy (sentinel::messages.macs.*, anglicky a slovensky):
| Výnimka | Kedy |
|---|---|
AlgorithmNotAllowedException::notHmacForMacs() | sign() alebo verify() s Ed25519 či ECDSA. |
InvalidKeyMaterialException::emptySecret() | Prázdne tajomstvo — pri podpisovaní alebo kdekoľvek v zozname. |
InvalidKeyMaterialException::noSecrets() | verify() s prázdnym zoznamom. |
InvalidKeyMaterialException::tooManySecrets() | verify() s viac než 8 tajomstvami. |
InvalidArgumentException | Tajomstvá, ktoré nie sú zoznamom reťazcov — reťazcové kľúče, medzery v indexoch, prvok, ktorý nie je reťazec. |
Fasáda, DI a akcie
use RoundlyConsulting\Sentinel\Actions\Macs\SignWithSecretAction;
use RoundlyConsulting\Sentinel\Actions\Macs\VerifyWithSecretsAction;
$mac = Sentinel::signWithSecret($payload, $secret); // flat
$index = Sentinel::verifyWithSecrets($payload, $mac, [$current, $previous]);
$index = $this->sentinel->macs()->verify($payload, $mac, [$current, $previous]); // an injected SentinelManager
$mac = app(SignWithSecretAction::class)->execute($payload, $secret); // the raw actions
$index = app(VerifyWithSecretsAction::class)->execute($payload, $mac, [$current, $previous]);| Metóda | Vracia | Čo robí |
|---|---|---|
macs() | MacsAccessor | sign() a verify() s tajomstvami, ktoré drží vaša aplikácia. |
signWithSecret(string $message, string $secret, Algorithm $algorithm = Algorithm::HmacSha256) | string | Surový MAC (32 / 48 / 64 bajtov). |
verifyWithSecrets(string $message, string $mac, array $secrets, Algorithm $algorithm = Algorithm::HmacSha256) | ?int | Najnižší index z 1–8 tajomstiev, ktorému surový MAC zodpovedá, inak null — v konštantnom čase cez všetky tajomstvá. |
Každé volanie ide cez manažéra, takže naviazanie ktorejkoľvek z oboch akcií v kontajneri platí všade a Sentinel::fake() vidí fasádu, prístup aj injektovaný SentinelManager rovnako.
Prehratie a symetria
- Prehratie nepokrýva, rovnako ako MAC s kľúčmi kruhu: MAC dokazuje, kto bajty poslal, nie to, že prídu len raz. Spolu s obsahom zahrňte do MAC časovú značku alebo id správy a duplikáty odfiltrujte na svojej strane — urobia to idempotenčné kľúče Sentinelu (pozri Idempotenčné kľúče).
- Kto overuje, má tajomstvo a vie pod ním MAC aj sfalšovať. Každé tajomstvo uchovávajte zašifrované — cast encrypted — a každému odosielateľovi dajte vlastné.
Testovanie
Sentinel::macs() je bezstavový, preto ho Sentinel::fake() nepodvrhuje: sign() a verify() spustia skutočné akcie — každé pravidlo, rovnaké výnimky — a vrátia skutočný MAC a index, bajt po bajte. Každé volanie sa zaznamená bez tajomstiev (signWithSecret so správou a algoritmom, verifyWithSecrets so správou, algoritmom a počtom tajomstiev); MAC odovzdaný do verify() sa tiež nezaznamená a odmietnuté volanie nezaznamená nič. rejectMacsWithSecrets() zariadi, že každé verify() vráti null, takže cestu k 401 otestujete bez falšovania MAC:
use RoundlyConsulting\Sentinel\Enums\Algorithm;
$fake = Sentinel::fake();
$mac = Sentinel::macs()->sign('{"id":1}', $secret); // the real MAC, byte for byte
$fake->assertMacSigned('{"id":1}', Algorithm::HmacSha256);
$fake->rejectMacsWithSecrets();
$this->postJson('/webhooks/acme', $payload, ['Webhook-Signature' => 'v1,bogus'])->assertUnauthorized();
$fake->assertMacsVerified(matched: false);Volania overia assertMacSigned(?string $message = null, ?Algorithm $algorithm = null), assertMacsVerified(?string $message = null, ?bool $matched = null) — true, ak sa niektoré tajomstvo zhodovalo, false, ak žiadne, null pre oboje — a assertNoMacsWithSecrets(); pozri Testovanie.
Testovacie vektory pre iné jazyky
Balík obsahuje tests/Fixtures/macs-vectors.json s M-1…M-8: testovací prípad 2 z RFC 4231 (4-bajtový kľúč Jefe) pre všetky tri hashe, testovací prípad 6 (131-bajtový kľúč), 1-bajtový kľúč, príklad Standard Webhooks (24-bajtové tajomstvo) a dve 32-bajtové tajomstvá pre prípad rotácie — nezávisle vypočítané cez hash_hmac v PHP.
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 kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
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.