Logovanie
Keď má DI kontajner službu implementujúcu Tomaj\NetteApi\Logger\ApiLoggerInterface, ApiPresenter ju zavolá po behu handlera.
Od verzie 4.0.0 sa logy predvolene maskujú
Presenter nahradí hodnoty tajných hlavičiek a tajných query parametrov reťazcom *** skôr, než sa dostanú do log(). Pozrite Maskovanie. V 3.x dostal logger hlavičky presne tak, ako prišli, ak ste nezavolali setMaskedLogHeaders().
Rozhranie
public function log(
int $responseCode,
string $requestMethod,
string $requestHeader,
string $requestUri,
string $requestIp,
string $requestAgent,
int $responseTime
): bool;| Argument | Hodnota |
|---|---|
$responseCode | odoslaný stavový kód |
$requestHeader | všetky hlavičky požiadavky, jedna Name: value na riadok, zamaskované |
$requestUri | REQUEST_URI aj s query stringom, zamaskované |
$requestIp | zo služby IpDetectorInterface, ktorá musí existovať |
$responseTime | milisekundy |
Príklad
Príklad logovania zapisuje JSON riadky:
<?php
declare(strict_types=1);
namespace App;
use Tomaj\NetteApi\Logger\ApiLoggerInterface;
/**
* Writes one JSON line per API request.
* The headers arrive as "Name: value" lines; secret header and query parameter values are already *** because the presenter masks them.
*/
final class FileApiLogger implements ApiLoggerInterface
{
public function __construct(private string $file)
{
}
public function log(
int $responseCode,
string $requestMethod,
string $requestHeader,
string $requestUri,
string $requestIp,
string $requestAgent,
int $responseTime
): bool {
$headers = [];
foreach (explode(PHP_EOL, trim($requestHeader)) as $line) {
[$name, $value] = array_map('trim', explode(':', $line, 2)) + [1 => ''];
$headers[$name] = $value;
}
$line = json_encode([
'code' => $responseCode,
'method' => $requestMethod,
'uri' => $requestUri,
'ip' => $requestIp,
'agent' => $requestAgent,
'time_ms' => $responseTime,
'headers' => $headers,
], JSON_UNESCAPED_SLASHES);
return file_put_contents($this->file, $line . "\n", FILE_APPEND | LOCK_EX) !== false;
}
}services:
- Tomaj\NetteApi\Misc\IpDetector
- App\FileApiLogger(%appDir%/../log/api.log)Maskovanie
Od verzie 4.0.0
Maskovanie je predvolene zapnuté. Vo verzii 3.6.0 bolo len setMaskedLogHeaders(), vypnuté, kým ste ho nezavolali. Pozrite UPGRADE-4.0.md.
Pred zavolaním log() presenter nahradí hodnotu každej tajnej hlavičky a každého tajného query parametra. Názov ostane, takže stále vidíte, čo klient poslal. Táto požiadavka:
GET /api/v1/users?api_key=sk_live_4f9a8b7c6d5e&page=2
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9
X-Api-Key: abc123
Cookie: lang=en; PHPSESSID=9f8e7d6c5b4a
X-Request-Id: abc-123príde do log() ako:
$requestUri = /api/v1/users?api_key=***&page=2
$requestHeader = Authorization: ***
X-Api-Key: ***
Cookie: ***
X-Request-Id: abc-123Maskovanie nepotrebuje žiadnu konfiguráciu. Meníte ho na jednom z dvoch miest:
- v
setup:službyapiPresenter, metódami presentera, alebo - v sekcii
logging:DI rozšírenia.
Čo sa predvolene maskuje
| Čo | Predvolene | Konštanta |
|---|---|---|
| mená hlavičiek | Authorization, Proxy-Authorization, Cookie, Set-Cookie, X-Api-Key | ApiPresenter::DEFAULT_MASKED_LOG_HEADERS |
| vzory hlavičiek | *token*, *secret*, *key*, x-auth-* | ApiPresenter::DEFAULT_MASKED_LOG_HEADER_PATTERNS |
| query parametre | api_key, apikey, key, token, access_token, refresh_token, id_token, auth, password, secret, signature, sig, code, client_secret | ApiPresenter::DEFAULT_MASKED_LOG_QUERY_PARAMS |
| mená z autorizácií | hlavička každej HeaderApiKeyAuthentication, parameter každej QueryApiKeyAuthentication, cookie každej CookieApiKeyAuthentication | načítané zo zaregistrovaných endpointov pri každej požiadavke |
| režim | *** | LogMaskMode::Full |
Ako sa položky porovnávajú
- Mená hlavičiek sa porovnávajú bez ohľadu na veľkosť písmen:
authorization,AuthorizationaAUTHORIZATIONsú jedna položka. - Vzor hlavičky je meno hlavičky, v ktorom
*znamená ľubovoľné znaky, aj žiadne. Musí zodpovedať celému menu, bez ohľadu na veľkosť písmen.*token*zodpovedáX-Auth-Token,X-Refresh-TokenajToken.x-auth-*zodpovedáX-Auth-User, ale nieX-Auth(-je povinná). Iný zástupný znak nie je:?a[...]v mene hlavičky byť nemôžu, takže sa odmietnu. - Query parametre sa porovnávajú bez ohľadu na veľkosť písmen po URL-dekódovaní mena. Každé meno zamaskuje aj svoje poľové tvary:
tokenzamaskujetoken=,TOKEN=,token[]=ajtoken[a]=. Parameter bez=(?token) nemá hodnotu a ostane. Prázdna hodnota (?token=) sa zamaskuje ako každá iná. - Mená z autorizácií sa pridajú k zoznamom, keď sa požiadavka loguje. Mená hlavičiek a query parametrov sa porovnávajú bez ohľadu na veľkosť písmen, mená cookies presne, tak ako ich číta PHP. Po jednom ich odstrániť nejde, lebo v zoznamoch nie sú a
getMasked*()ich nevracia. Zmiznú, len keď daný druh maskovania vypnete. Ak niektoré chcete logovať, vypnite daný druh maskovania a ostatné maskujte vo vlastnom loggeri, alebo logujte jeho hash cezmasking: hash. Cookieje medzi predvolenými menami, takže celá hlavička bude***. KeďCookieodstránite, hlavička sa zaloguje a zamaskujú sa v nej len cookies, ktoré čítaCookieApiKeyAuthentication:Cookie: lang=en; api_session=***.- Vyhráva posledný zápis:
addMaskedLogHeader('COOKIE')ponechá jednu položku agetMaskedLogHeaders()vrátiCOOKIE. - Poradie zoznamov:
getMasked*()vráti položky v poradí pridania, predvolené ako prvé.
Rýchle recepty
Každý recept ukazuje najprv setup: presentera, potom to isté s DI rozšírením. Pre každý zoznam použite len jedno z nich; pozrite Setup presentera a neon spolu.
Zamaskovať ďalšiu hlavičku, query parameter alebo vzor hlavičky:
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
- addMaskedLogHeader(X-Session)
- addMaskedLogHeaderPattern(x-signature-*)
- addMaskedLogQueryParam(session_id)api:
logging:
maskHeaders: {add: [X-Session], patterns: {add: [x-signature-*]}}
maskQueryParams: {add: [session_id]}Logovať jednu predvolenú položku celú (tu Set-Cookie, vzor *key* a code):
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
- removeMaskedLogHeader(Set-Cookie)
- removeMaskedLogHeaderPattern('*key*')
- removeMaskedLogQueryParam(code)api:
logging:
maskHeaders: {remove: [Set-Cookie], patterns: {remove: ['*key*']}}
maskQueryParams: {remove: [code]}Nahradiť celý zoznam (vzory hlavičiek a mená z autorizácií ostanú):
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
- setMaskedLogHeaders([Authorization, X-Session])
- setMaskedLogHeaderPatterns(['x-secret-*'])
- setMaskedLogQueryParams([api_key, token])api:
logging:
maskHeaders: {names: [Authorization, X-Session], patterns: ['x-secret-*']}
maskQueryParams: [api_key, token]Vypnúť maskovanie, aby sa všetko logovalo ako v 3.x:
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
- setMaskedLogHeaders([])
- setMaskedLogQueryParams([])api:
logging:
maskHeaders: false
maskQueryParams: falsesetMaskedLogHeaders([]) vypne mená, vzory aj hlavičky a cookies z autorizácií. setMaskedLogQueryParams([]) vypne parametre aj parametre z autorizácií. Vypnúť môžete aj len jedno z nich.
Zmeniť režim, pozrite Režimy maskovania:
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
- setLogMasking(Tomaj\NetteApi\Logger\LogMaskMode::Partial)api:
logging:
masking: partialPoužiť vlastný masker, pozrite Vlastný masker:
services:
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
- setLogMasking(App\Api\LengthMasker())api:
logging:
masking: App\Api\LengthMaskerapiPresenter vyššie je jediná služba zo stránky Začíname: pridajte k nej riadky setup:, alebo dajte blok pod rovnakým názvom do iného konfiguračného súboru a Nette ich zlúči. Druhú službu ApiPresenter neregistrujte; dve skončia chybou „Multiple services of type Tomaj\NetteApi\Presenters\ApiPresenter found“. Vzor, ktorý začína *, dajte v NEON do úvodzoviek: '*key*'.
Metódy presentera
| Metóda | Účinok |
|---|---|
setMaskedLogHeaders(array $headers = DEFAULT_MASKED_LOG_HEADERS) | nahradí mená hlavičiek. Bez argumentu obnoví predvolené. Vzory ostanú. |
setMaskedLogHeaders([]) | vypne maskovanie hlavičiek: vymaže mená aj vzory a vynechá hlavičky a cookies z autorizácií |
addMaskedLogHeader(string $header) | pridá jedno meno. Po setMaskedLogHeaders([]) maskovanie hlavičiek znova zapne, s menami z autorizácií, ale bez vymazaných vzorov. |
removeMaskedLogHeader(string $header) | odstráni jedno meno. Zlyhá, keď meno v zozname nie je. Po odstránení posledného mena ostanú vzory aj mená z autorizácií. |
setMaskedLogHeaderPatterns(array $patterns = DEFAULT_MASKED_LOG_HEADER_PATTERNS) | nahradí vzory. [] vymaže len vzory; mená ostanú. Neprázdny zoznam maskovanie hlavičiek znova zapne. |
addMaskedLogHeaderPattern(string $pattern) | pridá jeden vzor a maskovanie hlavičiek znova zapne |
removeMaskedLogHeaderPattern(string $pattern) | odstráni jeden vzor. Zlyhá, keď vzor v zozname nie je. |
setMaskedLogQueryParams(array $params = DEFAULT_MASKED_LOG_QUERY_PARAMS) | nahradí parametre. [] vypne maskovanie query aj s parametrami z autorizácií. |
addMaskedLogQueryParam(string $param) | pridá jeden parameter a maskovanie query znova zapne |
removeMaskedLogQueryParam(string $param) | odstráni jeden parameter. Zlyhá, keď parameter v zozname nie je. |
getMaskedLogHeaders(), getMaskedLogHeaderPatterns(), getMaskedLogQueryParams() | list<string>: aktuálne zoznamy, bez mien z autorizácií |
setLogMasking(LogMaskMode|LogValueMaskerInterface $masking) | ako sa hodnota zamaskuje. LogMaskMode sa zmení na new LogValueMasker($mode). Pre kľúč hashu odovzdajte new LogValueMasker(LogMaskMode::Hash, $key). |
getLogMasking() | aktuálny LogValueMaskerInterface, predvolene LogValueMasker s LogMaskMode::Full |
Volania bežia v poradí setup:. Každé mení to, čo nechali predchádzajúce, takže setMaskedLogHeaders([X-A]) a potom addMaskedLogHeader(X-B) zamaskuje obe, opačné poradie len X-A.
Každá položka sa skontroluje pri volaní. Chyba zlyhá s tým, čo je zle a ako to opraviť:
removeMaskedLogHeader('Set-Cokie'): 'Set-Cokie' is not in the masked headers (Authorization, Proxy-Authorization, Cookie, Set-Cookie, X-Api-Key), so there is nothing to remove. Did you mean 'Set-Cookie'? Entries compare case-insensitively; fix the spelling or leave the entry out.
removeMaskedLogHeader('X-Auth-Token'): 'X-Auth-Token' is not in the masked headers (Authorization, Proxy-Authorization, Cookie, Set-Cookie, X-Api-Key), so there is nothing to remove. It is masked by the pattern '*token*'; remove the pattern, or replace the patterns with a narrower list.
addMaskedLogHeader('X-*'): it contains *, which is only for header patterns. Add it as a pattern, or write the full header name.
setMaskedLogHeaders(): 'X Secret' in the list: it is not an HTTP header name, a header name has no spaces, colons or other separators. Write only the name, for example X-Session.
addMaskedLogHeaderPattern('token'): it has no *, so it matches only one header. Add it as a header name, or use * for any characters, for example *token*.
addMaskedLogHeaderPattern('*'): it matches every header, so nothing useful would be logged. Name a part of the header, for example *token*, or leave the headers out in your logger.
addMaskedLogQueryParam('token[]'): it is not a query parameter name. Write only the name without =, &, ?, # or brackets, for example api_key (it also masks api_key[] and api_key[x]).
removeMaskedLogQueryParam('cod'): 'cod' is not in the masked query parameters (api_key, apikey, key, token, access_token, refresh_token, id_token, auth, password, secret, signature, sig, code, client_secret), so there is nothing to remove. Did you mean 'code'? Entries compare case-insensitively; fix the spelling or leave the entry out.
new LogValueMasker(LogMaskMode::Partial, hashKey): the hash key is used only by LogMaskMode::Hash. Use new LogValueMasker(LogMaskMode::Hash, $key), or leave the key out.Chybné volanie v setup: zlyhá pri prvej požiadavke, keď sa presenter vytvára. S DI rozšírením zlyhajú rovnaké chyby pri kompilácii kontajnera.
Referencia neon
S DI rozšírením sa maskovanie nastavuje v api: logging:. Bez tejto sekcie platia predvolené hodnoty presentera.
api:
logging:
maskHeaders: true # true | false | zoznam mien | mapa, pozrite nižšie
maskQueryParams: true # true | false | zoznam mien | mapa, pozrite nižšie
masking: full # full | partial | hash | omit | Class | Class(args) | @service
hashKey: null # reťazec, len s masking: hash| Kľúč | Typ | Predvolené | Účinok |
|---|---|---|---|
maskHeaders | true | áno | predvolené mená, vzory a mená z autorizácií |
false | maskovanie hlavičiek vypnuté: žiadne mená, vzory ani hlavičky a cookies z autorizácií (setMaskedLogHeaders([])) | ||
zoznam, [Authorization, X-Session] | nahradí predvolené mená; predvolené vzory a mená z autorizácií ostanú (setMaskedLogHeaders([...])) | ||
mapa {names, add, remove, patterns} | mení predvolené hodnoty po jednej, pozrite nižšie | ||
maskHeaders.names | zoznam | predvolené mená | nahradí predvolené mená pred add a remove |
maskHeaders.add | zoznam | [] | pridá mená |
maskHeaders.remove | zoznam | [] | odstráni mená, ktoré sú v predvolených alebo v names |
maskHeaders.patterns | true | áno | predvolené vzory |
false | žiadne vzory; mená ostanú | ||
zoznam, ['*token*', 'x-auth-*'] | nahradí predvolené vzory | ||
mapa {add, remove} | pridá alebo odstráni vzory | ||
maskQueryParams | true | áno | predvolené parametre a parametre z autorizácií |
false | maskovanie query vypnuté, aj pre parametre z autorizácií | ||
zoznam, [api_key, token] | nahradí predvolené parametre; parametre z autorizácií ostanú | ||
mapa {names, add, remove} | to isté ako pri maskHeaders, bez patterns | ||
masking | full, partial, hash, omit | full | režim |
App\Api\MyMasker, App\Api\MyMasker(args), @myMasker | váš masker; trieda musí implementovať LogValueMaskerInterface | ||
hashKey | reťazec, %env.API_LOG_HASH_KEY% | null | HMAC kľúč pre masking: hash. Bez neho sa použije obyčajný SHA-256. |
Mapa použije najprv names, potom add, potom remove a zmení ich na rovnaké volania presentera. Každý kľúč mapy je nepovinný, ale mapa musí niečo meniť:
api:
logging:
maskHeaders:
names: [Authorization, Cookie, X-Session] # namiesto 5 predvolených
add: [X-Device-Id] # potom pridať
remove: [Cookie] # potom odstrániť, len z names alebo predvolených
patterns:
add: [x-signature-*]
remove: ['*key*']
maskQueryParams:
add: [session_id]
remove: [code]
masking: hash
hashKey: %env.API_LOG_HASH_KEY%Toto zamaskuje Authorization, X-Session a X-Device-Id, hlavičky, ktoré zodpovedajú *token*, *secret*, x-auth-* alebo x-signature-*, a mená z autorizácií. X-Api-Key sa zaloguje, lebo nie je v names a *key* bol odstránený. Cookie sa zaloguje celá, okrem cookies CookieApiKeyAuthentication. Query parametre sú 13 predvolených bez code, plus session_id. Hodnoty budú hash: a 16 hex znakov HMAC.
true a masking: full nepridajú žiadne volanie, takže presenter ostane pri predvolených. Ostatné hodnoty pridajú pri kompilácii kontajnera volania setMaskedLog* / add* / remove* / setLogMasking.
Setup presentera a neon spolu
Rozšírenie pridá svoje volania za setup: služby apiPresenter, takže by nahradili to, čo zmenil setup. Keď sa kľúč logging:, ktorý nie je na predvolenej hodnote, stretne s volaním v setupe pre to isté, kompilácia zlyhá:
api.logging.maskHeaders and the setup of the Tomaj\NetteApi\Presenters\ApiPresenter service call addMaskedLogHeader() both change the masked headers. The extension runs after the setup and would replace it. Keep one place: move the setup call into api.logging.maskHeaders (for example addMaskedLogHeader(X-Session) is maskHeaders: {add: [X-Session]}), or leave api.logging.maskHeaders out.Kľúč logging: | konfliktné volania v setup: presentera |
|---|---|
maskHeaders | setMaskedLogHeaders, addMaskedLogHeader, removeMaskedLogHeader, setMaskedLogHeaderPatterns, addMaskedLogHeaderPattern, removeMaskedLogHeaderPattern |
maskQueryParams | setMaskedLogQueryParams, addMaskedLogQueryParam, removeMaskedLogQueryParam |
masking, hashKey | setLogMasking |
Kľúče sú samostatné. Môžete napríklad nastaviť hlavičky v setup: a masking: hash v neon. Kľúč na predvolenej hodnote (maskHeaders: true, masking: full) nikdy nie je v konflikte.
Chyby v neon zlyhajú pri kompilácii kontajnera a uvedú kľúč:
api.logging.maskHeaders.remove[0]: 'Set-Cokie' is not in the masked headers (Authorization, Proxy-Authorization, Cookie, Set-Cookie, X-Api-Key), so there is nothing to remove. Did you mean 'Set-Cookie'? Entries compare case-insensitively; fix the spelling or leave the entry out.
api.logging.maskHeaders.remove[0] 'X-A' is also in api.logging.maskHeaders.add, so it would be added and removed again. Keep it in one of them.
api.logging.maskHeaders is an empty map, so it changes nothing. Use true for the defaults, false to turn the masking off, or names, add or remove, for example maskHeaders: {add: [X-Session], remove: [Set-Cookie]}.
api.logging.maskHeaders is an empty list. To turn the masking off write false; to change the defaults use add or remove, for example maskHeaders: {add: [X-Session], remove: [Set-Cookie]}.
api.logging.maskHeaders.patterns[0] 'token': it has no *, so it matches only one header. Add it as a header name, or use * for any characters, for example *token*.
api.logging.maskQueryParams.add[0] 'x[]': it is not a query parameter name. Write only the name without =, &, ?, # or brackets, for example api_key (it also masks api_key[] and api_key[x]).
api.logging.masking 'parital' is not a masking mode. Did you mean 'partial'? Use 'full', 'partial', 'hash', 'omit', or a class implementing Tomaj\NetteApi\Logger\LogValueMaskerInterface, @serviceName or a statement like App\Api\MyMasker(%tempDir%) for your own.
api.logging.hashKey is set, but api.logging.masking is 'full' and only hash uses the key. Use masking: hash with the key, or remove hashKey.
api.logging.hashKey is empty, so it would not protect short secrets. Use a long random secret, for example hashKey: %env.API_LOG_HASH_KEY%, or remove hashKey for a plain SHA-256.
Unexpected item 'api › logging › maskHeaders › ad', did you mean 'add'? Allowed keys in api.logging.maskHeaders: names, add, remove, patterns.Celý zoznam je v tabuľke Chyby v konfigurácii.
Režimy maskovania
Režim určuje, čím sa zamaskovaná hodnota nahradí. Je rovnaký pre hlavičky, query parametre aj cookies. Požiadavka zo začiatku stránky sa zaloguje ako:
| Režim | $requestUri | $requestHeader |
|---|---|---|
full (predvolený) | /api/v1/users?api_key=***&page=2 | Authorization: ***X-Api-Key: ***Cookie: ***X-Request-Id: abc-123 |
partial | /api/v1/users?api_key=sk_l***&page=2 | Authorization: Bearer eyJh***X-Api-Key: ***Cookie: lang***X-Request-Id: abc-123 |
hash | /api/v1/users?api_key=hash:2592602aa93aef79&page=2 | Authorization: hash:5d78b6eb771afd1eX-Api-Key: hash:6ca13d52ca70c883Cookie: hash:83e1eafde364581eX-Request-Id: abc-123 |
hash s hashKey: my-long-random-key | /api/v1/users?api_key=hash:97c5041103206d22&page=2 | Authorization: hash:7fc0ed98d7c6352eX-Api-Key: hash:e4984104dab10337Cookie: hash:6f6bf54c6f1caeb4X-Request-Id: abc-123 |
omit | /api/v1/users?page=2 | X-Request-Id: abc-123 |
| Režim | Pravidlo | Použitie |
|---|---|---|
LogMaskMode::Full | hodnota bude *** | nič z tajomstva v logu |
LogMaskMode::Partial | prvé 4 znaky ostanú a zvyšok bude ***. Hodnota kratšia ako 12 znakov bude len ***. Úvodná autentifikačná schéma (Basic, Bearer, Digest, DPoP, HOBA, Mutual, Negotiate, NTLM, Token, ApiKey, Hawk, AWS4-HMAC-SHA256, SCRAM-SHA-1, SCRAM-SHA-256, vapid, s ľubovoľnou veľkosťou písmen) a medzera za ňou ostanú a pravidlo platí pre zvyšok: Bearer short bude Bearer ***. | rozlíšiť kľúče, vidieť, ktorý klient poslal ktorý kľúč |
LogMaskMode::Hash | hash: a prvých 16 hex znakov SHA-256 celej hodnoty, alebo HMAC-SHA-256 s hashKey. Zahašuje sa aj schéma. | nájsť všetky požiadavky s rovnakým kľúčom bez jeho zalogovania |
LogMaskMode::Omit | riadok hlavičky sa vynechá. Dvojica name=value sa z query vynechá a bez ďalších parametrov zmizne aj ? (/x?token=a sa zaloguje ako /x). Cookie CookieApiKeyAuthentication sa z hlavičky Cookie vynechá. | nič o tajomstve, ani to, že bolo poslané |
Partial počíta bajty, takže ho nechajte pre ASCII tajomstvá. Pri Basic sú 4 ponechané znaky base64 začiatku mena používateľa. Pri celej hlavičke Cookie sú to začiatok prvej cookie, lang***.
Hash nie je šifrovanie
Bez hashKey je hodnota prvých 16 hex znakov obyčajného SHA-256. Ktokoľvek s logom môže zahašovať všetkých kandidátov a porovnať. 6-miestny PIN, slovo alebo krátky či známy kľúč sa nájde za pár sekúnd. Nastavte hashKey na dlhé náhodné tajomstvo, ktoré nie je v logu ani v repozitári, napríklad %env.API_LOG_HASH_KEY%. Hodnota je potom HMAC-SHA-256, ktorý sa bez kľúča overiť nedá. Bez DI rozšírenia odovzdajte new LogValueMasker(LogMaskMode::Hash, $key). Keď kľúč zmeníte, rovnaké tajomstvo dá iný hash, takže staršie riadky logu sa už nezhodujú. Kľúč funguje len s hash; s iným režimom zlyhá a zlyhá aj prázdny kľúč.
Vlastný masker
Na čokoľvek iné implementujte Tomaj\NetteApi\Logger\LogValueMaskerInterface:
namespace App\Api;
use Tomaj\NetteApi\Logger\LogValueMaskerInterface;
final class LengthMasker implements LogValueMaskerInterface
{
public function mask(string $value): ?string
{
return '*** (' . strlen($value) . ' chars)';
}
}mask()dostane pôvodnú hodnotu: celú hodnotu hlavičky, URL-dekódovanú query hodnotu alebo hodnotu jednej cookie.- Vráti text do logu, alebo
null, aby sa hlavička, parameter či cookie vynechali, ako priomit. - Vrátená query hodnota sa znova URL-zakóduje (len
*a:ostanú, ako sú), takže*** (3 chars)sa zaloguje ako?api_key=***%20%283%20chars%29. Hlavička dostane text tak, ako je:Authorization: *** (27 chars). - Masker je jeden objekt pre všetky požiadavky. Tajomstvá, napríklad kľúče, mu dajte v konštruktore.
api:
logging:
masking: App\Api\LengthMasker # alebo App\Api\LengthMasker(%tempDir%), alebo @myMaskerBez rozšírenia setLogMasking(App\Api\LengthMasker()) v setup:.
Čo sa nemaskuje
- Telo:
log()nikdy nedostane telo požiadavky ani odpovede. Ak telá logujete inde, maskujte ich tam. - Argumenty
$requestAgenta$requestIp. - Konzola a Tracy. Konzola ukazuje požiadavku, ktorú zadáte, a lišta a bluescreen Tracy ukazujú surové hlavičky a
$_GET. Tracy nechajte v produkčnom režime, kde sa bluescreen neukazuje, a ak treba, jej logy maskujte sami. - Segmenty cesty:
/api/v1/users/secret-idostane, ako je. Tajomstvá do cesty nedávajte. - Hlavičky odpovede: logujú sa len hlavičky požiadavky.
- Vaše vlastné volania
log()inde, napríklad do Tracy.
Bezpečnostné poznámky
- Predvolené hodnoty pokrývajú len bežné mená. Overte, čo posielajú vaši klienti, a pridajte vlastné mená, napríklad
X-Sessionalebosession_id. removeMaskedLog*(),falsea užší zoznam logujú skutočné tajomstvá. Radšej odstráňte jednu položku, než by ste maskovanie vypli.- Mená z autorizácií sa maskujú, aj keď zoznamy nahradíte, takže kľúč, ktorý API číta, sa do logu nedostane. Vypne ich len
false,setMaskedLogHeaders([])alebosetMaskedLogQueryParams([]). - Logy sa často uchovávajú dlhšie a číta ich viac ľudí než databázu. Zamaskovaný log je stále osobný údaj, keď obsahuje IP a user agentov.
Čo sa loguje
| Odpoveď | Loguje sa v 3.x |
|---|---|
| výsledok handlera, ľubovoľný kód | áno |
500 z výnimky alebo zlyhanej kontroly výstupu | áno |
404 Unknown api endpoint | áno |
400 zlý vstup, 401, 429, 500 z výnimky v authorized() | nie, presenter skončí pred loggerom |
Časté otázky
Prečo v logu chýbajú moje odpovede 401?
V 3.x končia predčasne, pozrite tabuľku. Ak ich potrebujete, logujte ich vo vlastnom error handleri.
Potrebujem na logovanie detektor IP?
Áno, presenter berie IpDetectorInterface z kontajnera. Vstavaný IpDetector sa nedá podvrhnúť hlavičkami; za reverse proxy ju uveďte v http: proxy:, pozrite si Autorizáciu.
Kam idú výnimky?
Do Tracy, cez Debugger::log() v DefaultErrorHandler. Pozrite si Spracovanie chýb.
Môžem logovať telo odpovede?
Cez toto rozhranie nie, dostane len kód. Ak potrebujete viac, obaľte handler alebo error handler.
Záleží na návratovej hodnote log()?
Nie, presenter ju ignoruje.
Mám maskovanie nechať aj vo vlastnom loggeri?
Pre hlavičky a query parametre nie, tie zamaskuje presenter. Nechajte ho pre všetko ostatné, čo logujete, napríklad segmenty cesty alebo telá.
Prečo je moja hlavička X-Request-Key zamaskovaná?
Zodpovedá jej vzor *key*. Odstráňte ho cez removeMaskedLogHeaderPattern('*key*') alebo maskHeaders: {patterns: {remove: ['*key*']}}. X-Api-Key ostane zamaskovaná, lebo je aj medzi menami.
Ako zalogujem hlavičku mojej HeaderApiKeyAuthentication?
Meno z autorizácie samostatne odstrániť nejde; vypne ho len maskHeaders: false (alebo setMaskedLogHeaders([])) spolu s celým maskovaním hlavičiek. Namiesto toho logujte hash, masking: hash s hashKey, a kľúče rozlíšite.
Prečo kompilácia zlyhá s „both change the masked headers“?
setup: presentera aj sekcia logging: menia ten istý zoznam a rozšírenie by setup nahradilo. Nechajte ho na jednom mieste, pozrite Setup presentera a neon spolu.
Je režim rovnaký pre hlavičky aj query parametre?
Áno, jeden masker spracuje každú zamaskovanú hodnotu. Vlastný masker dostane len hodnotu, nie meno hlavičky či parametra, takže ani on ich nerozlíši.