---
title: "Podpisy HTTP správ — Sentinel for Laravel | Roundly"
description: "Overujte požiadavky partnerov podpísané podľa RFC 9421, podpisujte odchádzajúce volania a overujte odpovede — kľúče, profily, odmietnutia a voľby."
url: https://roundly-consulting.com/sk/open-source/navody/sentinel-for-laravel/podpisy-http
language: sk
---

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

[Sentinel for Laravel](https://roundly-consulting.com/sk/open-source/navody/sentinel-for-laravel.md)

# Podpisy HTTP správ

Partneri dokazujú, že požiadavka pochádza od nich a nebola zmenená ani prehraná: HTTP Message Signatures (RFC 9421) s hmac-sha256, ed25519, ecdsa-p256-sha256 alebo ecdsa-p384-sha384, Content-Digest podľa RFC 9530 a štruktúrované polia podľa RFC 9651 (vlastný parser a serializér). Ich kľúče žijú v kruhu http — tajomstvo partnera nikdy nevytvorí ani neoverí pečať.

## Kľúče partnerov

```php
use RoundlyConsulting\Sentinel\Enums\Algorithm;

// The partner sends you their public key (PEM or base64:…) — import it, no deploy:
Sentinel::keys()->ring('http')->import('acme-2026-10', Algorithm::Ed25519, $partnerPublicKeyPem, owner: $partner);

// …or you generate the pair and share the public half:
$key = Sentinel::keys()->ring('http')->generate(Algorithm::Ed25519, keyId: 'acme-2027-01', owner: $partner);
$key->publicKey;      // base64:… — share it with the partner
```

Importované kľúče partnerov sú len na overovanie a viazané na model partnera — bez nasadenia pre každého partnera. Alternatíva bez databázy ich uvedie ako kľúče len na overovanie v konfigurácii, pričom kruh číta oba drivery — jedno nasadenie na partnera a kľúč bez vlastníka:

```bash
SENTINEL_HTTP_KEY_DRIVER=chain
SENTINEL_HTTP_KEYS="partner-ed|ed25519|base64:<32-byte public key>,partner-hmac|hmac-sha256|base64:<shared secret>"
```

Zdieľané tajomstvo partnera je kľúč len na overovanie. Ak ho nastavíte ako aktuálny kľúč kruhu http (SENTINEL\_HTTP\_KEY), stane sa kľúčom, ktorým podpisuje vaša aplikácia, a prichádzajúce požiadavky podpísané ním sa odmietnu (unknown\_key). Uveďte ho medzi predošlými kľúčmi kruhu alebo ho importujte len na overovanie; accept\_signing\_keys nastavte iba vtedy, ak tým istým tajomstvom aj sami podpisujete odchádzajúce požiadavky.

## Overovanie prichádzajúcich požiadaviek

```php
Route::post('/partner/events', PartnerEvents::class)->middleware('sentinel.signed');           // default profile
Route::post('/partner/orders', PartnerOrders::class)->middleware('sentinel.signed:partners');  // a named profile

$signature = Sentinel::signatures()->current($request);   // ?VerifiedSignature — what sentinel.signed verified
$partner = Sentinel::signatures()->owner($request);       // the key's owner model (database keys), or null
$signature = Sentinel::signatures()->verify($request, 'partners');   // the same check, programmatic
```

Predvolený profil vyžaduje created, keyid a nonce, prijíma podpisy staré najviac 300 sekúnd (± 30 sekúnd rozdielu hodín) a vyžaduje content-digest vždy, keď má požiadavka telo. Profily pridáte v sentinel.signatures.profiles a odkážete na ne menom: sentinel.signed:partners. Ak podpisy autentifikujú klientov API, zaraďte sentinel.signed pred auth.

@scheme a @authority sa berú z požiadavky Laravelu, takže za load balancerom alebo proxy sa riadia nastavením dôveryhodných proxy (X-Forwarded-Proto, X-Forwarded-Host). Proxy nastavte ako dôveryhodnú, inak sa požiadavka, ktorú partner podpísal pre https://api.example.com, poskladá s internou schémou a hostiteľom a skončí ako invalid\_signature. @status existuje len pri odpovediach: ak ho uvediete v components profilu, odchádzajúcich podpisov alebo v SigningOptions::$components, odmietne sa ako neplatná konfigurácia — podpis odpovede ho aj tak pokrýva vždy.

VerifiedSignature nesie label, ring, keyId, algorithm, created a expires (Unix sekundy), nonce, tag, components a ownerType / ownerId kľúča (pri kľúčoch z konfigurácie null). verify() ho vráti, alebo vyhodí HttpSignatureException (401; reason() vráti SignatureRejection, keyId() deklarované id kľúča).

## Profil v poradí krokov

- Label: nakonfigurovaný, inak jediný člen, inak prvý, ktorého tag sa zhoduje s tagom profilu, inak ambiguous\_signature.
- Parametre: keyid, created, nonce pri require\_nonce, tag, ak je nakonfigurovaný; expires sa vynúti, ak je prítomné.
- Pokrytie: požiadavka musí vykonať metódu, s ktorou bola odoslaná (prepis metódy → malformed); pokryté musia byť komponenty profilu, @query pri query reťazci a content-digest pri tele.
- Čas (vrátane hraníc): created v budúcnosti nad clock\_skew → not\_yet\_valid; staršie než max\_age plus clock\_skew → too\_old; expires uplynulé o viac než clock\_skew → expired.
- Kľúč: hľadá sa v kruhu profilu — chýbajúci, čakajúci, poškodený či vyradený → unknown\_key; odvolaný → revoked\_key; alg iný než algoritmus kľúča → algorithm\_mismatch; mimo allowlistov → algorithm\_not\_allowed; kľúč, ktorým aplikácia sama podpisuje → unknown\_key, pokiaľ nie je zapnuté accept\_signing\_keys.
- Digest: každý podporovaný člen Content-Digest musí zodpovedať surovému telu (digest\_mismatch); žiadny podporovaný → unsupported\_digest.
- Podpis: základ sa zostaví znova a overí → invalid\_signature.
- Nonce: až po platnom podpise, zapamätá sa na čas okna platnosti → replayed.

## Odmietnutia

Odmietnutie odpovie 401 application/problem+json s kódom signature\_rejected — presný dôvod len pri app.debug, aby nevzniklo orákulum na overovanie — a pri signatures.advertise s nápovedou Accept-Signature. HttpSignatureRejected sa spustí s presným dôvodom:

| SignatureRejection | Význam |
| --- | --- |
| `missing_signature` | Chýba Signature / Signature-Input. |
| `malformed` | Nečitateľné polia, duplicitné komponenty, prepísaná metóda. |
| `ambiguous_signature` | Nie je jediný label na overenie. |
| `missing_parameter` | Chýba keyid, created alebo nonce. |
| `missing_component` | Povinný komponent nie je pokrytý alebo chýba. |
| `unsupported_component` | Parametrizovaný alebo nepodporovaný komponent. |
| `unsupported_algorithm` | Algoritmus, ktorý RFC 9421 neregistruje — alg ako rsa-pss-sha512 alebo kľúč ako hmac-sha512. |
| `unknown_key, revoked_key` | Vyhľadanie kľúča. |
| `algorithm_mismatch` | Parameter alg iný než algoritmus kľúča. |
| `algorithm_not_allowed` | Algoritmus kľúča je mimo allowlistu profilu alebo kruhu. |
| `tag_mismatch` | Nesprávny tag. |
| `not_yet_valid, too_old, expired` | Časové okno. |
| `digest_mismatch, unsupported_digest` | Content-Digest |
| `invalid_signature` | Podpis sa neoverí. |
| `replayed` | Nonce už bol videný. |

## Podpísané multipart požiadavky zlyhajú bezpečne

PHP rozparsuje telo multipart/form-data do $\_POST / $\_FILES ešte pred Laravelom a surové bajty si neponechá, takže Content-Digest sa nedá overiť: požiadavka sa odmietne s digest\_mismatch, aj keď ju partner podpísal správne. Dohodnite sa s partnerom, aby súbor posielal ako surové telo (Content-Type: application/octet-stream, metadáta v pokrytých hlavičkách alebo v query) alebo ako JSON. Ak sa multipart nedá obísť, obsluhujte daný endpoint z PHP poolu s enable\_post\_data\_reading = Off a php://input si rozparsujte sami.

## Podpisovanie odchádzajúcich požiadaviek

Podpisujte kľúčom odchádzajúceho kruhu — musí byť aktívny a mať súkromný materiál; importovaný kľúč partnera nikdy nepodpisuje:

```php
use RoundlyConsulting\Sentinel\DataTransferObjects\SigningOptions;
use RoundlyConsulting\Sentinel\Enums\DigestAlgorithm;

Http::withSignature('acme-2027-01')->post('https://partner.example/events', $event);
Http::withSignature('acme-2027-01', new SigningOptions(expiresIn: 60, tag: 'acme', includeAlg: true))
    ->post('https://partner.example/events', $event);

// Any PSR-7 request — here without a nonce, for a peer that does not de-duplicate them:
$signed = Sentinel::signatures()->sign($psrRequest, 'acme-2027-01', new SigningOptions(
    components: ['@method', '@authority', '@path', 'content-digest'],
    digest: DigestAlgorithm::Sha512,
    nonce: false,
));

Sentinel::signatures()->verifyResponse($response, 'partners');   // a signed response (PSR-7 or Laravel client)
Sentinel::signatures()->contentDigest('{"hello": "world"}');      // 'sha-256=:X48E…:'
```

Http::withSignature() podpíše finálnu požiadavku, až po každom middleware požiadavky a callbacku beforeSending() (opakovanie či presmerovanie sa podpíše nanovo). Pridá Content-Digest, ak telo nie je prázdne a content-digest je komponentom, vynechá @query bez query reťazca aj hlavičky, ktoré požiadavka nemá, a nastaví created, keyid a nový nonce (plus expires, tag a alg, ak sú nakonfigurované). Stream, ktorý sa nedá pretočiť, sa najprv uloží do kópie s možnosťou posunu, vytvorenej cez HttpFactory z PSR-7 balíka Guzzle, ktorý prichádza s illuminate/http — bez ďalšej závislosti. Pri odchádzajúcej požiadavke sa @authority (aj @target-uri) berie z jej hlavičky Host, ak ju má — to je cieľová autorita, ktorú myslí RFC 9421, a tú číta aj príjemca —, inak z URI, takže požiadavka odoslaná na https://10.0.0.5/… s Host: api.example.com sa podpíše pre api.example.com. Každé pole SigningOptions ponechané na null použije sentinel.signatures.outbound.\*; nastavené sa overí rovnako ako jeho náprotivok v konfigurácii:

| Voľba | Predvolené | Účinok |
| --- | --- | --- |
| `components` | `outbound.components` | Pokryté komponenty. |
| `label` | `outbound.label (sig1)` | Label podpisu. |
| `expiresIn` | `outbound.expires_in` | Sekundy do parametra expires (predvolene žiadny). |
| `tag` | `outbound.tag` | Parameter tag (predvolene žiadny). |
| `includeAlg` | `outbound.include_alg (false)` | Posielať parameter alg. |
| `digest` | `outbound.digest (sha-256)` | Algoritmus Content-Digest. |
| `ring` | `outbound.ring (http)` | Kruh, v ktorom sa hľadá id kľúča. |
| `nonce` | `true` | false vynechá parameter nonce. |

## Ploché tvary a akcie

```php
use RoundlyConsulting\Sentinel\Actions\Signatures\SignRequestAction;

$signed    = Sentinel::signRequest($psrRequest, 'acme-2026-10', new SigningOptions(expiresIn: 60));
$signature = Sentinel::verifyRequestSignature($request, 'partners');
$signature = Sentinel::verifyResponseSignature($clientResponse);
$partner   = Sentinel::signatureOwner($request);

$signed = app(SignRequestAction::class)->execute($psrRequest, 'acme-2026-10', new SigningOptions);
```

Podpísaná odpoveď sa overuje podľa tých istých pravidiel plus @status; nikdy nevyžaduje query ani nonce (prítomný nonce sa však stále deduplikuje). Jej telo sa načíta raz — kontrola pokrytia aj Content-Digest ho zdieľajú —, takže sa overí aj streamovaná odpoveď (stream => true, telo bez možnosti posunu). Také telo sa nedá pretočiť, takže po overení je spotrebované: ak ho ešte potrebujete, odpoveď najprv uložte do pamäte ($psr->withBody(GuzzleHttp\\Psr7\\Utils::streamFor((string) $psr->getBody()))) a overte aj čítajte túto kópiu.

Interoperabilita je overená voči prílohe B RFC 9421: B.2.5 (hmac-sha256) a B.2.6 (ed25519) sa podpíšu bajt po bajte a overia, B.2.4 (ecdsa-p256-sha256) sa overí a RSA vektory sa odmietnu. Nepodporované: parametre komponentov, @request-target, @query-param, politiky viacerých podpisov nad rámec jedného vybraného labelu a spracovanie Accept-Signature protistrany.

## 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/sentinel-for-laravel.md)
