SHL-005
Never let a provider error leak your API token
The request URL contains your key. Flash it in an error and anyone who can make the provider fail can read it.
Shortlink APIs put the token in the query string, so the request URL is a credential. The natural debugging move — showing the failed URL in the error — hands that credential to anyone who can trigger a failure, which on most sites is anyone who clicks a task while the provider is down.
This has happened on real sites. The fix is trivial and the exposure is total, which is a bad combination to leave in place.
Log server-side with the full detail, show the user a generic message, and make the admin view redact the token when it displays the log.
function fetch_short_url(array $provider, string $destination): ?string
{
$url = build_provider_url($provider, $destination); // contains the token
$body = fetch_guarded($url, 12);
if ($body !== null) {
return parse_short_url($body);
}
// Server-side log keeps the detail; the user sees nothing sensitive.
error_log(sprintf(
'shortlink provider %d failed for user %d: %s',
(int) $provider['id'],
(int) ($_SESSION['uid'] ?? 0),
redact_token($url, (string) $provider['token'])
));
return null; // caller shows a generic message
}
/** Even the log should not carry the whole key. */
function redact_token(string $text, string $token): string
{
if ($token === '') {
return $text;
}
$masked = substr($token, 0, 4) . str_repeat('*', max(4, strlen($token) - 8)) . substr($token, -4);
return str_replace($token, $masked, $text);
}
// In the page:
// flash('error', 'That link provider is not responding. Try another task.');
Using it
Redact in the admin log viewer as well as in the file. A support person does not need the key.
Keep tokens in the database for providers added through admin, but never render them in a page except as a masked value with a reveal action.
Rotate any token that has ever appeared in a user-visible error. Assume it is public.
What bites people
Exception messages propagate. If your error handler prints the exception on a staging site, the URL ends up there too.
A failed request logged by your HTTP client library may include the full URL by default. Check what it writes.
Masking in the middle still leaks length and prefix, which is fine for a log and not fine for a screenshot in a public support thread.
This one touches real money. Point it at a throwaway wallet and watch a full cycle before you trust it with a live balance.