Milo's Launch Check
Un controllo di sicurezza dall'esterno per le app costruite con l'AI
Cosa fa
Chi costruisce un'app con l'AI spesso lascia aperte falle di sicurezza senza saperlo. Questo strumento controlla l'app dall'esterno prima del lancio.
Come guadagna
Il progetto è presentato in vetrina su SideProjectors; come guadagna non è indicato pubblicamente.
Rendilo tuo
- Controllo specifico per app con Supabase: regole di sicurezza mancanti, chiavi segrete esposte.
- Report in italiano che spiega ogni problema in parole semplici.
Le frasi da scrivere all'AI
1. Base del sito e grafica
Crea un'app web in italiano chiamata "Prima del Lancio": controlla dall'esterno un'app creata con Lovable, Bolt o altri strumenti AI e trova le falle di sicurezza più comuni (chiavi segrete esposte, tabelle senza regole, intestazioni mancanti) prima di pubblicarla. Usa React, Vite, TypeScript e Tailwind CSS.
Pagine da creare:
1. Pagina iniziale con i controlli fatti e un report di esempio
2. Registrazione e verifica di essere proprietario del sito
3. Avvio del controllo
4. Report: problemi per gravità, spiegazione semplice e frase da dare all'AI per correggerli
5. Storico dei controlli
Stile: da sicurezza ma non spaventoso: sfondo bianco, blu scuro #0F172A, rosso #DC2626 per i problemi gravi, arancione #F59E0B, verde #16A34A, carattere "Inter", report chiaro con icone. Nessuna immagine generica di stock.
Tutto deve funzionare perfettamente da telefono. Tutti i testi in italiano, chiari e senza parole tecniche.
2. Database, account e sicurezza
Collega il progetto a Supabase e attiva la registrazione con email e password.
Crea queste tabelle:
- "siti": dominio, codice di verifica, verificato o no, collegati all'utente
- "controlli": esito di ogni verifica in JSON, punteggio, data
Regole di sicurezza obbligatorie:
- si può controllare solo un sito di cui si è dimostrata la proprietà
- ogni utente vede solo i suoi siti e report
- i crediti non si modificano dal browser
Usa esattamente il codice SQL che trovi nei "Dettagli tecnici" di questa scheda: eseguilo nel SQL Editor di Supabase prima di continuare, poi collega le pagine alle tabelle.
3. Solo siti propri, controlli passivi
Prima di tutto la proprietà: l'utente aggiunge un record TXT al dominio o carica un file con un codice nel sito. Senza verifica non parte nessun controllo: si controllano solo i propri siti.
La Edge Function "controlla" fa solo letture come un normale visitatore, senza attacchi:
- scarica la pagina e i file JavaScript e cerca chiavi segrete (codice nei Dettagli tecnici): chiavi Stripe live, chiavi service_role di Supabase, chiavi di OpenAI/Anthropic;
- se trova la chiave pubblica di Supabase, prova a leggere le tabelle con quella chiave: se risponde con dati, la tabella non ha regole di sicurezza;
- controlla HTTPS e le intestazioni di sicurezza (Content-Security-Policy, X-Frame-Options...).
4. Report con le correzioni
Il report divide i problemi per gravità (grave, medio, consiglio) e per ognuno spiega in parole semplici il rischio e dà una frase pronta da incollare in Lovable o Bolt per correggerlo (es. "Sposta la chiave Stripe segreta in una Edge Function e usa solo quella pubblicabile nel sito").
Storico dei controlli per vedere che i problemi spariscono. Nota: "Questo controllo trova i problemi più comuni, non sostituisce un test di sicurezza professionale".
5. Pagamenti con Stripe
Aggiungi pacchetti di crediti: 3 controlli a 9 €, 10 controlli a 24 €. Ogni nuovo utente riceve 1 crediti gratis. Ogni controllo completo costa un credito. Mostra sempre in alto quanti crediti restano e, quando finiscono, un riquadro per comprarne altri.
A pagamento riuscito il webhook aggiunge i crediti del pacchetto con la funzione aggiungi_crediti.
Per la sessione di pagamento e il webhook segui la guida "Pagamenti con Stripe" di Fattibile.
6. Controlli specifici di questo progetto
Oltre ai controlli della guida "Controllo finale", verifica in particolare:
1. Proprietà: prova ad avviare un controllo su un dominio non verificato chiamando direttamente la Edge Function: deve essere rifiutato.
Correggi quello che non va e scrivimi cosa hai cambiato.
Dettagli tecnici
Cercare chiavi segrete nei file del sito
Cerca nei file JavaScript pubblici le forme tipiche delle chiavi segrete. Per le chiavi Supabase legge il ruolo scritto dentro il token: "service_role" non deve mai stare nel sito.
const MODELLI = [
{ nome: "Chiave segreta Stripe (live)", regola: /sk_live_[0-9a-zA-Z]{20,}/g, gravita: "grave" },
{ nome: "Chiave segreta Stripe (test)", regola: /sk_test_[0-9a-zA-Z]{20,}/g, gravita: "medio" },
{ nome: "Chiave segreta Supabase", regola: /sb_secret_[0-9a-zA-Z_-]{20,}/g, gravita: "grave" },
{ nome: "Chiave Anthropic", regola: /sk-ant-[0-9a-zA-Z_-]{20,}/g, gravita: "grave" },
{ nome: "Chiave OpenAI", regola: /sk-(proj-)?[0-9a-zA-Z_-]{32,}/g, gravita: "grave" },
];
// I token JWT di Supabase contengono il ruolo: "anon" va bene, "service_role" è grave
function ruoloJwt(token: string): string | null {
try {
const parte = token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
return JSON.parse(atob(parte)).role ?? null;
} catch { return null; }
}
export function cercaSegreti(codice: string, file: string) {
const trovati: { nome: string; gravita: string; file: string; anteprima: string }[] = [];
for (const m of MODELLI) {
for (const r of codice.matchAll(m.regola)) {
trovati.push({ nome: m.nome, gravita: m.gravita, file, anteprima: r[0].slice(0, 10) + "…" });
}
}
for (const r of codice.matchAll(/eyJ[\w-]+\.eyJ[\w-]+\.[\w-]+/g)) {
if (ruoloJwt(r[0]) === "service_role") {
trovati.push({ nome: "Chiave service_role di Supabase", gravita: "grave", file, anteprima: r[0].slice(0, 10) + "…" });
}
}
return trovati; // mai salvare la chiave intera: solo l'inizio
}Verifica della proprietà con un record DNS
Controlla che il dominio abbia il record TXT con il codice assegnato all'utente, usando il DNS pubblico.
export async function proprietarioVerificato(dominio: string, codice: string): Promise<boolean> {
const r = await fetch(
"https://cloudflare-dns.com/dns-query?name=" + encodeURIComponent(dominio) + "&type=TXT",
{ headers: { Accept: "application/dns-json" } },
);
const dati = await r.json();
return (dati.Answer ?? []).some((a: { data: string }) =>
a.data.replace(/"/g, "") === "prima-del-lancio-verifica=" + codice,
);
}
Pezzi comuni a tutti i progetti
Glossario
- Record TXT
- Una riga di testo aggiunta alle impostazioni del dominio: serve a dimostrare che il dominio è tuo.
- JWT
- Un token in tre parti separate da punti: la parte centrale contiene informazioni leggibili, come il ruolo.