How redaction works¶
A HAR is full of credentials, so WireTrail masks what it prints. The masking is pattern-based: it recognizes secrets by the name of the header or the key they sit in, and by the shape of the value. It does not understand what a value means. That makes it reliable for the common cases and blind to anything outside the patterns, and this page lists both.
Masking is not a guarantee
Output is safe to paste into a ticket only if every secret in it is one of the kinds WireTrail recognizes. Several common places are not covered, and they are listed below with real output. Read what you are about to share.
The three rules¶
1. By header name. These request and response headers are always masked, whatever they hold: authorization, cookie, set-cookie,
proxy-authorization, x-api-key, x-auth-token and x-amz-security-token.
Three more headers hold URLs and are masked as URLs: location, referer and content-location. A relative value such as /cb?token=… cannot be read as a URL, so only rule 3 is applied to it, and
the query key names in it are not checked.
2. By key name in JSON. In a JSON body, any key whose name contains one of password, token, secret, authorization, access_token,
refresh_token, id_token, api_key, apikey or client_secret has its value replaced, at any depth, whatever the value is. The match
ignores case and is a substring match, so token_type is masked too.
In a URL query, the keys token, access_token, refresh_token, id_token, key, api_key, apikey, sig, signature, password and
secret are masked.
3. By shape. A value that looks like a random token is masked in a path segment, a query value, a header value or, in some commands,
a body. WireTrail calls such a value opaque. A path segment or a query value is tested as a whole. A header value, and a search snippet, is first split at spaces and at ; , & = / ? :, quotes
and brackets, and each piece is tested on its own, so a base64 value that contains = or / can slip through. A string is opaque when it is at least 16 characters long, is not all hexadecimal and not a UUID, and
either contains a %, or is in the base64 alphabet (letters, digits, +, /, =, _, -) and has either 24 or more characters with a
+, / or = and a digit, or 32 or more with a digit, an upper-case and a lower-case letter. A dot is not in the alphabet.
GET https://api.shop.example/v1/receipts/9921/download?d=<redacted>&token=<redacted>&sig=<redacted>&page=1 [200] OK
d = <redacted>
token = <redacted>
sig = <redacted>
page = 1
d, token and sig are masked, the first by shape and the other two by name. page is not. A blob in the path is masked by shape:
What each command masks¶
| Command | What can carry a secret | How it is protected |
|---|---|---|
show-entry, curl, handoff |
URL, query, headers, bodies | The three rules. Only JSON bodies get rule 2. Other bodies are printed as they are. |
errors |
body: excerpt, message, code, correlation ids |
The excerpt follows rule 2 for JSON. message, code and the correlation ids are copied from the body and the headers unmasked. |
diagnose, auto, summary (recommended next steps) |
The detail line of a finding | The server's error message is copied from the body unmasked. |
report |
Error messages | The messages are copied unmasked. |
search |
A window from 40 bytes before a match to 40 bytes after its start | Rule 3 only, by chunk. No header or key-name rule. |
extract |
The extracted value | Rule 3 only. No key-name rule. |
diff |
Query values that vary | Rules 2 and 3 for the query. No body content is printed. |
jwt |
Claims | The token and its signature are never printed. iss and aud are printed. sub is hashed. --json --unsafe-include-secrets adds raw_token. |
summary (text), and the fingerprint field of duplicates --json and retries --json |
The query pairs of a repeated call, in top duplicate calls and hints |
None. Query values are printed as they are. |
hosts, subsystems, endpoints, timeline, slowest, startup, cascade, storms, duplicates (text), retries (text), transitions, redirects, pagination, rate-limit, auth, export |
Host and normalized path | Opaque and numeric path segments become {blob} and {id}. No request headers, bodies or query values are printed. rate-limit prints the retry-after and x-ratelimit-* response headers, and export and timeline --json print one correlation id. |
--unsafe-include-secrets turns masking off for show-entry, curl, handoff, errors, search, extract, diff, jwt and auto (for its errors and diff drill-downs). It does not
change commands that print no secrets, and it changes nothing in report.
What gets through¶
Each of these was run and printed on a file in assets/downloads/quirks/, so you can reproduce it.
A JWT in a header with another name. A JWT has dots, and rule 3 does not allow them. Only the headers in rule 1 are masked by name, so a token in x-device-token
is printed in full by show-entry, curl and handoff. Here are the first characters of one:
A JWT in a field that extract reads. extract '$.access_token' prints the whole token, for the same reason. See Extract a field.
A form-encoded body. The key-name rule works on JSON. A body in application/x-www-form-urlencoded, which is how many sign-in and token calls are sent, is
not parsed, and nothing in it is masked:
curl and handoff print the same text in their --data line:
A short secret in a query, in the duplicate list. The fingerprint of a repeated call includes its query values, and summary prints fingerprints:
The key is session, which is not on the list, and the value is too short to look random. duplicates --json and retries --json carry the same fingerprint in their fingerprint field.
A secret in a server's message. errors copies the message out of the body before it applies any masking. diagnose, auto and the recommended next steps in summary show the same message as the detail of a finding:
A short secret that search finds, and a short value that extract returns. See Search bodies.
A secret in a path that does not look random. /reset/ab12cd stays as it is: numeric segments become {id}, but a short mixed string is kept.
What it masks that it did not need to¶
The key-name rule is a substring match, so it also hides harmless values. token_type in the sign-in response above is masked although it holds
Bearer. That is deliberate: a false mask costs you a value, and a false pass costs you a credential.
Using it safely¶
- Treat the output as sensitive until you have read it. Look at it the way you would look at a log you are about to send.
- Prefer the commands that print the least.
report,diagnoseandsummaryprint counts and the server's messages.show-entry,curl,handoff,searchandextractprint request content. - Search your own output for the words that should not be in it:
bearer,token,secret,password, a real user's name. - Strip the capture before you share the file. WireTrail does not do this for you. See Handle captures safely.
- Give an agent a stripped capture when you can, because it can quote anything it reads.
Related¶
- Handle captures safely for what to do about it.
- Limitations lists the same gaps with the other limitations.
- Show an entry is the command whose masking is the most complete.