Download the PHP package silversoft/api-signer without Composer

On this page you can find all versions of the php package silversoft/api-signer. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package api-signer

api-signer (PHP)

Podpisywanie i weryfikacja żądań HTTP zgodnie z RFC 9421 — HTTP Message Signatures, bez zależności.

RFC 9421

Odpowiednik dla Node: @silversoft/api-signer. Obie paczki realizują jeden format drutowy i są trzymane na tych samych wektorach testowych — patrz Zgodność ze standardem.

Ta paczka podpisuje i weryfikuje, ale niczego nie wysyła. Do wywoływania wewnętrznego API służy silversoft/api-client, który używa tej paczki pod spodem.


Po co to jest

Wspólny sekret w nagłówku — Authorization: Bearer …, X-Api-Key: … albo cokolwiek w tym kształcie — leci w całości przy każdym wywołaniu. Osiada w logach dostępu, w logach proxy, w systemach błędów, w historii powłoki, na zrzucie ekranu w zgłoszeniu. Kto zobaczy go raz, może podszywać się pod klienta bez ograniczeń, a sam sekret nie jest w żaden sposób związany z żądaniem, z którym przyszedł.

Podpis rozwiązuje oba problemy. Sekret nie opuszcza żadnej ze stron: klient dowodzi jego posiadania podpisując, serwer dowodzi tego samego przeliczając. Podpis obejmuje metodę, ścieżkę, query i skrót ciała, więc przechwyconego żądania nie da się zmienić, przekierować na inny endpoint ani powtórzyć po wygaśnięciu.

Do czego: integracje serwer–serwer, gdzie obie strony są Twoje albo partnera — API wewnętrzne, webhooki, komunikacja między usługami bez mTLS, wszystko tam, gdzie dziś krąży klucz API.

Do czego nie: uwierzytelnianie użytkowników w przeglądarce. Klient potrzebuje materiału klucza, a przeglądarka nie ma go gdzie bezpiecznie trzymać.

Szybki start

Klient

$signed->body to ładunek w postaci podpisanej. Nie serializuj go drugi raz — oddanie tablicy z powrotem klientowi HTTP, żeby zakodował ją ponownie, to najczęstsza przyczyna błędu signature does not match.

Serwer

Model bezpieczeństwa

Co podpis chroni

Autentyczność żądanie przyszło od kogoś, kto ma klucz dla keyid
Integralność metoda, ścieżka, query i ciało są dokładnie tym, co podpisano
Świeżość podpis jest ważny tylko między created a expires

Czego nie chroni

Założenia, na których ta paczka stoi

Nie są opcjonalne. Ich złamanie po cichu odbiera większość korzyści:

  1. TLS przy każdym wywołaniu. Podpis nie zastępuje szyfrowania.
  2. Jeden klucz na parę usług. Klucz wspólny dla trzech usług pozwala każdej podszyć się pod pozostałe.
  3. Jeden klucz na środowisko. Wspólny sekret staging i produkcji oznacza, że żądanie przechwycone na staging da się powtórzyć na produkcji. Paczka świadomie nie ma zabezpieczenia opartego na tag, bo skopiowana konfiguracja kopiuje też jego wartość — rozdzielne klucze są właściwym rozwiązaniem.
  4. Co najmniej 32 bajty entropii na sekret. openssl rand -base64 48.
  5. Klucze nigdy w systemie kontroli wersji. Ignoruj plik, dostarcz .sample albo czytaj ze zmiennych środowiskowych.

Polityka weryfikacji

Sam poprawny podpis nic nie znaczy. Klient może legalnie podpisać żądanie obejmujące wyłącznie @method; podpis się zweryfikuje, a te same bajty zadziałają wobec dowolnej ścieżki z dowolną treścią. RFC 9421 na to pozwala, więc każdy weryfikator musi narzucić własne minimum.

Policy jest tym minimum i działa domyślnie:

Wymagania można dokładać; zdejmowanie ich w działającym systemie nie ma sensu — Policy::none() istnieje wyłącznie do odtwarzania opublikowanych wektorów testowych i nie wolno go użyć na działającym endpointcie.

Niezależnie od polityki weryfikator zawsze przelicza Content-Digest z rzeczywistego ciała. Bez tego podpis obejmowałby jedynie deklarację nadawcy o ciele.

Ochrona przed powtórzeniem

nonce jest zawsze wysyłany. Jego sprawdzanie jest opcjonalne, bo kosztuje zapis przy każdym żądaniu, a przy więcej niż jednej instancji — wspólny magazyn. Lokalny cache daje złudzenie ochrony, podczas gdy instancje nie widzą swoich nonce.

Przy operacjach niedempotentnych pewniejszą ochroną jest idempotentność na poziomie aplikacji (naturalny klucz, ON CONFLICT, status operacji); nonce zatrzymuje wyłącznie to samo żądanie wysłane dwa razy.

Referencja API

Credential

new Credential($keyId, $key, $alg = 'hmac-sha256', $auth = 'signed') $key to surowy materiał klucza
Credential::fromConfig($keyId, $entry) z tablicy konfiguracyjnej: key albo key_base64, plus alg, auth. Wartość skalarna oznacza klucz w starym schemacie
$credential->isLegacy() true, gdy poświadczenie jest ustawione na schemat sprzed podpisów
$credential->legacyHeader() base64("<keyId>|<klucz>"), na potrzeby migracji

Algorytmy: hmac-sha256 (klucz = wspólny sekret) i ed25519 (klucz = 32-bajtowe ziarno, 64-bajtowy klucz prywatny albo PEM przy podpisywaniu; 32-bajtowy klucz publiczny albo PEM przy weryfikacji). Algorithm::ed25519PublicKeyFrom($privateKey) wyprowadza klucz publiczny do przekazania weryfikatorom.

Request

new Request($method, $path, $query, $body, $headers, $authority, $scheme) $body może być stringiem albo strumieniem
Request::fromUrl($method, $url, $body, $headers) rozkłada ścieżkę, query, host i schemat za Ciebie

ApiSigner::prepare(...) → SignedRequest

Zwraca ->method, ->url, ->body (dokładnie podpisane bajty) i ->headers. ->headerLines() daje linie Nazwa: wartość dla CURLOPT_HTTPHEADER.

Opcje: components, label, created, expires, nonce, tag, lifetime, headers.

Wysyłaniem żądań ta paczka się nie zajmuje — od tego jest silversoft/api-client.

Signer / Verifier

Signer::sign($credential, $request, $params = null) zwraca nagłówki do dodania. Podane $params są używane dosłownie — nic nie jest dopisywane za plecami wołającego, dzięki czemu opublikowane wektory testowe odtwarzają się co do bajta. null daje wartości domyślne. Signer::base($request, $params) wystawia bazę podpisu do diagnostyki.

Verifier::keyIdOf($request) zwraca keyid deklarowany przez żądanie, z obu schematów, żeby dało się znaleźć poświadczenie przed weryfikacją. Wartość jest z definicji nieuwierzytelniona: wybiera klucz do sprawdzenia, niczego nie przyznaje.

Verifier::verify($request, $credential, $policy = null) zwraca Result z polami failed, reason, label, params i components. reason jest do logu, nigdy do odpowiedzi — pokazanie wołającemu różnicy między „nieznany klucz” a „zły podpis” daje mu narzędzie do zgadywania.

Duże ładunki i strumienie

Ciało jest objęte przez Content-Digest (RFC 9530), więc da się je haszować przyrostowo:

multipart/form-data nie jest wspierane na podpisanych endpointach. PHP konsumuje takie ciało przed kodem aplikacji, php://input zostaje puste, więc serwer policzyłby skrót niczego, podczas gdy klient policzył skrót rzeczywistego ładunku — każde żądanie by odpadło. Duże pliki wysyłaj jako surowe ciało (application/octet-stream) albo base64 w JSON-ie, a multipart/form-data odrzucaj kodem 415.

Diagnostyka

Każde odrzucenie zwraca wołającemu to samo — celowo — więc zaczynaj od $result->reason w logu serwera.

Powód Prawdopodobna przyczyna Jak sprawdzić
signature does not match ciało zserializowane dwa razy — klient zakodował JSON, a klient HTTP zakodował go ponownie zaloguj $signed->body po stronie klienta i surowe ciało po stronie serwera; muszą być identyczne co do bajta
signature does not match proxy przepisało ścieżkę porównaj @path/@query z Signer::base() z REQUEST_URI serwera
signature does not match parametr podpisu zmieniony w locie porównaj odebrany Signature-Input z tym, który wysłał klient
Content-Digest does not match the body ciało się zmieniło albo middleware je przekodował przelicz Digest::of($body) po obu stronach
Content-Digest does not match the body endpoint dostał multipart/form-data odrzucaj ten typ zawartości kodem 415
signature has expired / created is in the future zegary różnią się o więcej niż clockSkew timedatectl status na obu hostach; NTP jest wymogiem twardym
signature lifetime exceeds the allowed maximum klient ustawił expires zbyt daleko dopasuj lifetime klienta do maxLifetime serwera
component "…" is not covered klient podpisał mniej komponentów, niż wymaga serwer porównaj components klienta z Policy::$requiredComponents
algorithm does not match the credential alg w nagłówku różni się od skonfigurowanego sprawdź alg we wpisie klucza po stronie serwera
credential is configured for the legacy scheme wpis klucza nadal ma auth => legacy przestaw na signed, gdy klient już przeszedł
missing Signature-Input or Signature header proxy usunęło nieznane nagłówki albo klient jest wciąż na starym schemacie zrzuć surowe nagłówki żądania na serwerze

Zgodność ze standardem

Zaimplementowane: podpisywanie i weryfikacja żądań; komponenty pochodne @method, @target-uri, @authority, @scheme, @request-target, @path, @query, @query-param; parametry podpisu created, expires, keyid, alg, nonce, tag; wiele podpisów na żądanie; hmac-sha256 i ed25519; Content-Digest z SHA-256 i SHA-512 (RFC 9530).

Niezaimplementowane: podpisywanie odpowiedzi i @status; algorytmy RSA i ECDSA; parametry komponentów spoza name (sf, key, bs, req, tr).

Jak zgodność jest wykazywana:

  1. Opublikowane wektory z RFC 9421, Appendix B, których nie wyprodukowała żadna z naszych implementacji, są odtwarzane co do bajta — bazy podpisu B.2.1–B.2.3 oraz pełne podpisanie i weryfikacja B.2.5 (hmac-sha256) i B.2.6 (ed25519).
  2. Test krzyżowy uruchamia tę paczkę i paczkę Node obok siebie: każda podpisuje, druga weryfikuje, a obie muszą wypuścić identyczne bajty. Wektory statyczne dowodzą tylko tego, że implementacja nadal zgadza się z nagraniem; to dowodzi, że obie zgadzają się ze sobą. Test mieszka w repozytorium Node i uruchamia się w CI obu, także cyklicznie, bo zmiana w jednym repozytorium jest dla drugiego niewidoczna.
  3. Kontrola interop z niezależną implementacją RFC 9421 (@misskey-dev/node-http-message-signatures) potwierdza, że wyprowadza ona tę samą bazę podpisu z naszych podpisanych żądań.

    Ta kontrola wykryła jedną rozbieżność, odnotowaną zamiast zamiecionej: dla żądania bez query stringu RFC 9421 §2.2.7 określa wartość komponentu @query jako „samo wiodące ?”, czyli linię "@query": ?. Tamta biblioteka emituje wartość pustą. My trzymamy się specyfikacji, a kontrola zgłasza błąd, gdyby rozbieżność przestała występować — wyjątek nie przeżyje poprawki po ich stronie.

Wektory testowe

vectors/ jest źródłem prawdy dla obu paczek i należy do tego repozytorium, bo generator jest w PHP:

vectors/rfc9421.json jest przepisany z RFC ręcznie i powinien się zmieniać tylko wtedy, gdy zmieni się RFC. Po regeneracji przenieś pliki do repozytorium Node (npm run sync-vectors) i zacommituj oba; test krzyżowy nie przejdzie, jeśli się różnią.

Wersjonowanie i zgodność

Semantyczne wersjonowanie, trzymane równo z paczką Node: obie mają ten sam major i minor dla tego samego formatu drutowego. Każda zmiana sposobu wyprowadzania bazy podpisu jest zmianą łamiącą i trafi wyłącznie do wydania głównego, bo po cichu unieważnia podpisy u wszystkich klientów. Dodanie algorytmu albo funkcji pomocniczej to wydanie minor.

Wspierane i testowane w CI: PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5. PHP 7.4 to świadomie przyjęta podłoga: dzięki niej paczka jest użyteczna w starszych aplikacjach, i to jest powód, dla którego powstała, zamiast sięgnięcia po jedną z paczek RFC 9421 wymagających 8.1 albo 8.4.

Migracja ze zwykłego klucza API

Poświadczenie niesie tryb auth, więc oba schematy dzielą jedno miejsce wywołania:

ApiSigner::prepare() zwraca ten sam obiekt w obu trybach, więc kod klienta pisze się raz, a przełącznik siedzi w konfiguracji. Po stronie serwera odrzucaj stary nagłówek od klienta już oznaczonego jako signed — inaczej wykradziony stary klucz działa dalej mimo migracji.

Rozwój

Test krzyżowy wymaga obu paczek:

Nowy przypadek brzegowy trafia do vectors/testvectors.json (regeneracja, synchronizacja do repozytorium Node, commit w obu), żeby obie implementacje były nim związane. CI wymusza 100% pokrycia linii w src/; kod podpisujący i weryfikujący ma około 500 linii, więc to podłoga, a nie ambicja.

Licencja

MIT — patrz LICENSE.


All versions of api-signer with dependencies

PHP Build Version
Package Version
Requires php Version >=7.4
ext-hash Version *
ext-json Version *
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package silversoft/api-signer contains the following files

Loading the files please wait ...