syft-enclave: attestation token is a de-facto bearer credential — never refreshed, ~30-day expiry grace, freshness-nonce slot never checked
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
- Ambito
- backend-api-design, cryptography, security
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
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 callsverify_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.jsonlets 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
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:
- Check the nonce. Add
AppraisalPolicy.expected_nonce: Optional[str] = Noneand a sixth check,freshness_nonce: skipped when unset (legacy path unchanged), fail on missing/mismatchedeat_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. - Refresh the published token. Republish in
tick()on a ~15-minute timer (tokens live ~30 min), retiring the reason the grace window exists. - Help verifiers generate nonces. A
generate_caller_nonce()helper intee_token.pyusingsecrets.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
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di OpenMined/PySyft
-
Type: Bug :bug:
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
Type: Bug :bug:
Difficoltà 3/5 1-2 giorni Idoneità per principianti 65/100
I maintainer di solito rispondono entro 1 giorno
-
Type: Testing :test_tube:
Difficoltà 3/5 1-2 giorni Idoneità per principianti 38/100
OpenMined/PySyft#9411 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Bug when using in_memory_cacheForse di nuovo libera @prabindersinghh l’ha presa 230 giorni fa e non c’è nessuna pull request aperta. ApertaType: Bug :bug:
OpenMined/PySyft#9410 · 4 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
Syft client log in is slowApertaType: Improvement :chart_with_upwards_trend:
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
OpenMined/PySyft#9409 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di OpenMined/PySyft
Issue simili
-
Broken links found in docsApertadocs pydanty:is-working
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
pydantic/pydantic-ai#8863 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
run-llama/llama_index#23278 ·
I maintainer di solito rispondono entro 2 giorni
-
documentation from-review-extraction github-actions priority: low severity:nit
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
LearningCircuit/local-deep-research#6946 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
oracle/langchain-oracle#323 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
tenstorrent/tt-metal#58057 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno