Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

syft-enclave: attestation token is a de-facto bearer credential — never refreshed, ~30-day expiry grace, freshness-nonce slot never checked

Aperta
#9,489 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
55/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
python

Direzione di ricerca

Inizia da src/syft_enclaves/attestation.py, tee_token.py e runner.py, quindi confronta la gestione esistente di nonce in docker/attestation_server.py; segui attest_peer() in src/syft_enclaves/client.py per il percorso esplicitamente escluso. Il lavoro è completato quando i token sottoposti a challenge convalidano una caller nonce corrispondente con una scadenza ravvicinata, tick() aggiorna i token pubblicati e il percorso legacy senza challenge rimane compatibile.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Enclave pkg:syft-enclave

While building a trust-minimized audit framework on syft-enclave, we found that the attestation token behaves as a de-facto bearer credential. The enclave publishes its token once at boot into SYFT_version.json — a file that syncs over a transport the threat model already declares untrusted (e.g. Google Drive). The token then verifies for ~30 days past its real expiry and is bound to nothing its holder must possess. So anything that can read the sync folder can present itself as a live, attested enclave to a data owner deciding whether to release secrets. Reading the file is not an attack; it is the normal operation of the sync folder.

Before / After

Before
  • Trigger: a data owner runs attest_peer() (or any consumer calls verify_attestation_token()) before releasing secrets to an enclave.
  • Today: the check accepts a token minted up to a month ago, presented by anyone, with the caller-freshness slot in the EAT nonce empty and unchecked.
  • Cost: a captured SYFT_version.json lets a non-enclave impersonate an attested enclave for ~30 days — the cheapest attack in the model, with total-compromise damage (secrets released to the impersonator).
After
  • Same trigger.
  • Now: a verifier can pose a per-request nonce; the enclave mints a fresh token embedding it; verification checks the nonce and honours the token's real (~30 min) expiry. Replayed tokens fail.
  • Win: the token becomes a challenge-response, worthless to anyone who merely overheard it.
Don't regress
  • The legacy unchallenged path (boot-published token, no nonce) keeps working — peers of already-deployed enclaves must not break.
  • The grace window is only tightened on the challenged path, not removed globally.

What happens today vs. with the fix

TODAY: attacker replays the boot-published token and the data owner releases secrets. FIX: the owner sends a per-request nonce, the enclave mints a fresh token embedding it, and a replayed token is rejected on nonce mismatch or real expiry.

The three compounding problems

All paths under packages/syft-enclave/.

1. The token never expires in practice. Google mints Confidential Space tokens with a ~30-minute lifetime, but EnclaveRunner._publish_attestation() runs once during init() (src/syft_enclaves/runner.py:132-134) and never again — tick() (runner.py:77) does not republish. The stopgap for that is JWT_EXPIRY_GRACE_SECONDS = 30 * 24 * 60 * 60 (src/syft_enclaves/attestation.py:33), passed to the verifier as clock_skew_in_seconds (attestation.py:146), so an expired token still verifies for a month. The comment above the constant already names the intended fix:

TODO: remove this once the enclave periodically refreshes its attestation token in SYFT_version.json — then the real (short) expiry can be honoured.

2. Nothing binds the token to its holder. verify_attestation_token() checks the JWT signature, secboot, dbgstat, the syft-client version nonce, and the image digest. None of these require the presenter to possess anything — there is no key the enclave must prove it holds and no challenge the verifier chose. A token that verifies for one party verifies for whoever is holding it.

3. The freshness slot exists and is never filled or checked. build_eat_nonce(caller_nonce=None) documents slot 1 as a "caller-supplied freshness nonce" (src/syft_enclaves/tee_token.py:28), but runner.py:134 calls it with no argument, and verify_attestation_token() reads only eat_nonce[0] (attestation.py:201-205) — slot 1 is never inspected. Notably, docker/attestation_server.py already validates and embeds a caller nonce on demand (attestation_server.py:155,174,199): the enclave can already answer a challenge — nobody ever poses or checks one.

Put together: the token is a bearer credential with a one-month effective life, published into a file whose exposure is by-design.

Proposed fix

Three small changes — happy to open the PR:

  1. Check the nonce. Add AppraisalPolicy.expected_nonce: Optional[str] = None and a sixth check, freshness_nonce: skipped when unset (legacy path unchanged), fail on missing/mismatched eat_nonce[1]. When a nonce is expected, verify with a tight clock skew (~60 s) instead of the month grace — a challenged token is fresh by construction.
  2. Refresh the published token. Republish in tick() on a ~15-minute timer (tokens live ~30 min), retiring the reason the grace window exists.
  3. Help verifiers generate nonces. A generate_caller_nonce() helper in tee_token.py using secrets.token_urlsafe, which fits the existing _NONCE_PATTERN / 74-char cap (tee_token.py:21-22).

Scope note

attest_peer() (src/syft_enclaves/client.py:85) reads the boot-published token from SYFT_version.json, so the nonce path cannot cover it until a challenge-file protocol exists over syftbox — that is a follow-up, not this fix. This fix fully closes the HTTP/attestation_server path and makes the published token short-lived instead of month-long. The grace window remains only for the legacy unchallenged path.


Same consumer-finds-the-gap pattern as the merged digest-pinning fix (#9454) — and the commit that introduced the grace window (c7e2edbd07) already names periodic refresh as the intended fix, so this just finishes that sentence.

Lingua principale
Python
Stelle
10k
Fork
2k
Merge medio
23h 19m
PR unite (30g)
21

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di OpenMined/PySyft

Tutte le issue di OpenMined/PySyft

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.