Skip to content

Signed key-value-store record links in resources/read never expire #1004

Description

@RobertCrupa

Context

When a key-value-store record is too large to inline (over KV_RECORD_MAX_INLINE_BYTES, 256 KB), resources/read links out with the store's signed recordPublicUrl so the client can fetch it without a token.

Problem

That signature never expires.

getRecordDownloadUrl() in src/resources/api_resources.ts calls apifyClient.keyValueStore(id).getRecordPublicUrl(key), which signs via createHmacSignatureAsync(urlSigningSecretKey, key) — a plain HMAC over the record key, no timestamp. The link stays valid until the store's urlSigningSecretKey rotates (effectively never).

Why it matters

The signed link is a bearer credential: anyone holding the URL can read that record, unauthenticated, indefinitely. It is emitted into model-visible resources/read output, so it can leak via conversation logs, shared transcripts, or third-party LLM providers — and the leak does not expire.

Note: signed record URLs already flow through resources/read today via the /keys endpoint (recordPublicUrl per item), so this is not unique to the large-binary path — but the large-binary change makes the server mint them deliberately.

Options

  1. Short-lived signature. @apify/utilities createStorageContentSignature({ urlSigningSecretKey, resourceId, expiresInMillis }) supports expiry, but getRecordPublicUrl does not expose it — would mean bypassing the helper. The record vs content signature formats differ, so confirm the platform validates a content-style signature on a record URL first.
  2. Only sign when there is no usable token (hosted case); fall back to the unsigned, token-gated API URL otherwise.
  3. Accept it (matches existing /keys behavior) and document the exposure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    security relatedThe issue concerns security but does not expose a direct attack vector.t-aiIssues owned by the AI team.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions