L'API di password.es risponde: come si usa

Pubblicato il di David Carrero

Questo blog ripete da mesi la stessa cosa: la tua password non dovrebbe uscire dal browser. Il generatore prende il caso da crypto.getRandomValues() sulla tua macchina e il controllo analizza quello che scrivi senza mandarlo da nessuna parte.

E oggi pubblichiamo un’API che genera password su un server.

La contraddizione è evidente e non abbiamo intenzione di mascherarla: ogni risposta dell’API porta un avviso che dice esattamente questo. Ma ci sono posti dove il browser non c’è — uno script che crea cento account, un server che produce una credenziale temporanea, un assistente a cui qualcuno chiede una password e che se la inventa — e lì l’alternativa non è «il browser»: è un random() scelto male o una stringa scritta a mano. Questa API serve a quello.

La chiamata intera

Senza chiave, senza registrazione, senza header. È tutta qui:

curl -X POST https://api.password.es/v1/generate

Restituisce una password di 16 caratteri e la sua analisi. Eseguita mentre si scriveva questo paragrafo: x*7$9,BUy9PPvOI?, presa da un alfabeto di 89 caratteri, 103,6 bit di entropia e un tempo di crack di 2,5 × 10¹¹ anni. Livello 4 su 4.

Nessuno di questi numeri è nuovo. Escono dallo stesso motore che disegna l’indicatore della home: la stessa formula dei bit, lo stesso modello di attacco — 10¹² tentativi al secondo, offline, hash veloce — e la stessa scala di livello del controllo. Se il sito e l’API dessero numeri diversi per la stessa password, uno dei due starebbe mentendo.

Cosa restituisce, campo per campo

La risposta porta passwords — sempre un array, anche se ne chiedi una sola — e un blocco analysis con length, pool, bits, log10_guesses, crack_time_log10_seconds, crack_time, level, level_scale e ceiling.

Due meritano un paragrafo. level_scale dice da dove viene il livello (oggi sempre "time"), così che una futura divergenza tra il sito e l’API si veda nel campo invece di doverla dedurre confrontando numeri. E ceiling dice se la cifra è esatta o un tetto: in generate è sempre false, perché la password l’ha fatta il server e sa con quale alfabeto; nel controllo del sito non sempre può esserlo, perché lì la password la porti tu.

Poi arriva notice, l’avviso, e un blocco _meta con il piano, la lingua applicata, i limiti e un link alla documentazione.

Le opzioni

Tutte opzionali, e con i nomi in inglese perché chi integra un’API scrive i campi tali e quali: length (4–64, di default 16), count (1–20), lower, upper, digits, symbols, exclude_ambiguous, no_repeats e lang.

curl -X POST https://api.password.es/v1/generate -H 'content-type: application/json' -d '{"length":20,"exclude_ambiguous":true}'

Due dettagli che la documentazione spiega e che conviene sapere prima di integrare. exclude_ambiguous toglie sei caratteri, non sette: scarta 0 O 1 I l | o, ma la barra verticale non era nell’insieme dei simboli fin dall’inizio, quindi l’alfabeto scende da 89 a 83 e non a 82. Per questo la risposta dice pool 83. E no_repeats non è una garanzia assoluta: riprova dieci volte, esattamente come il generatore del sito, e a 64 caratteri una ripetizione contigua passa circa lo 0,1 % delle volte. Dirlo è più utile che promettere il contrario.

La lingua

Di default risponde in inglese, che è quello che si aspetta chi integra senza dire niente. Si cambia con ?lang= nell’URL, con "lang" nel corpo o con l’header Accept-Language, e se sono in disaccordo vince il primo. Una lingua che non esiste non è un errore: ricade sull’inglese.

Qui c’è un’asimmetria che non si indovina, ed è per questo che l’API la dichiara: i messaggi esistono in inglese e spagnolo; i link, in tutte e diciotto le lingue del sito. Chiedere il tedesco dà link in tedesco e messaggi ancora in inglese. Non serve supporlo: _meta.lang porta { "messages", "links" } con quello che è stato applicato a ciascuna metà.

Il server MCP

MCP è il protocollo con cui un assistente — Claude, ChatGPT — usa strumenti esterni. https://api.password.es/mcp è un server MCP senza registrazione e senza chiave, e pubblica un solo strumento: generate_password, con gli stessi parametri di sopra.

È la parte che ci interessa di più, e non per ragioni tecniche: un assistente che non ha da dove prendere una password se la inventa, e quello che ne esce non è casuale, è ciò che il modello considera abbia la forma di una password. Con il server collegato smette di improvvisare e ne restituisce una fatta con crypto.getRandomValues(), con la sua analisi e con l’avviso incluso nella descrizione dello strumento e in ogni risultato. Quell’avviso è esattamente quello che l’assistente finisce per leggere a chi ha chiesto la password, ed è per questo che sta lì.

Se arrivi da altri server MCP, un dato solo: questo è senza stato. Il POST riceve una risposta JSON e non apre nessuno stream, Mcp-Session-Id non viene né emesso né atteso, una notifica riceve 202 senza corpo e la GET restituisce 405. La specifica 2025-06-18 lo permette esplicitamente, ma chi si aspetta sessioni e SSE finirà a debuggare alla cieca se nessuno glielo dice.

Quello che non fa

/v1/check restituisce 501. Non è una svista né un endpoint lasciato a metà: è voluto, e la risposta stessa spiega perché. Restituire gli stessi numeri del controllo richiede lo stesso motore di pattern che gira sul sito, e quello costa tra 11 ms e 3,6 s di CPU per richiesta a seconda di cosa gli mandi. Su un servizio senza registrazione e senza chiave, quella forbice è una decisione di prodotto — dove mettere il tetto di lunghezza — che ancora non è stata presa. Nel frattempo il 501 porta un checker_url che punta al controllo del sito nella lingua richiesta, che fa esattamente questo senza inviare nulla.

Non esistono nemmeno gli account, né le chiavi, né i piani. E siccome non esistono, in tutta l’API non c’è un solo link a una registrazione: nemmeno nell’errore di limite superato, che è dove lo mettono tutti.

I limiti

Uno, ed è quello che viene davvero applicato: 60 richieste al minuto per IP. Non serve credere a questa pagina: il numero viaggia dentro ogni risposta, in _meta.limits.burst. Se lo superi arriva un 429 con Retry-After e header RateLimit-*.

In _meta vedrai anche un blocco quota con i valori a null. È lo spazio riservato per quando ci saranno gli account, e va vuoto di proposito: un limite annunciato e non applicato è peggio che non annunciarne nessuno, perché chi integra lo rispetta e programma contro un numero che nessuno sta contando.

E anche così, pensaci due volte

Un’API che genera password è, in fondo, un antipattern: la password viaggia in rete e passa da una macchina che non è la tua. Che noi non conserviamo niente non cambia la forma del problema, solo la nostra parte — e sai già quanto vale una promessa che non puoi verificare.

Per questo l’avviso non vive solo su questa pagina: viaggia in ogni risposta, nella lingua che chiedi. E per questo il paragrafo sta qui e non nascosto in fondo alla documentazione. Per una password che userai tu, il generatore di questo sito gira interamente nel tuo browser e non invia niente. L’API è per l’altra cosa: quello che succede senza nessuno davanti a uno schermo.

I dettagli stanno in due pagine. Cos’è l’API racconta per chi è, cosa risponde oggi e cosa no. Il riferimento ha i nove parametri, tutti i campi spiegati uno a uno, i codici di errore e i limiti; è quella che si apre con l’editor di fianco. E se quello che vuoi è che lo legga una macchina, c’è openapi.json.


Fonti: l’API stessa, verificata in produzione il 31 agosto 2026 — POST /v1/generate, POST /v1/check (501), POST /mcp e GET /openapi.json · i numeri dell’analisi escono dallo stesso motore del generatore e del controllo di password.es · il modello di attacco è 10¹² tentativi al secondo, offline e con hash veloce, lo stesso del resto del sito · il server MCP implementa la specifica 2025-06-18 con trasporto Streamable HTTP senza stato.

Foto di Stanislav Kondratiev · Pexels

← Torna al blog