“Eh? Perché si vede la password?”
All’inizio della mia esperienza in azienda, sviluppavo freneticamente passando avanti e indietro tra frontend e backend. Un giorno stavo collegando alla schermata frontend la ‘API per l’elenco dei membri’ sviluppata da un collega entrato in azienda insieme a me.
Ho aperto la scheda Network degli strumenti di sviluppo di Chrome (F12) per controllare se i dati arrivassero correttamente e, nel momento in cui ho visto la risposta, ho pensato di aver visto male.
[
{
"id": 1,
"username": "tester",
"password": "$2a$10$D...", // Password cifrata esposta
"ssn": "900101-1...", // Codice fiscale esposto
"createdAt": "2024-01-01"
}
]
Spaventato, gli ho chiesto: “Ehi, perché nella risposta di questa API si vedono la password e perfino il numero di documento?”
Lui ha risposto con nonchalance: “Ah, quello? Tanto a schermo mostriamo solo il nome. Mi scocciava farlo bene, quindi ho restituito direttamente il risultato di userRepository.findAll(). C’è qualche problema?”
Mi è corso un brivido lungo la schiena. Lui stava solo cercando comodità, ma in pratica aveva piantato il seme di un grave incidente di sicurezza.
“Solo perché non si vede a schermo non vuol dire che sia sicuro!”
Chiunque può aprire la scheda Network del browser. Cosa succederebbe se un attaccante malevolo chiamasse questa API?
Esportare un’entità così com’è equivaleva a trasformare la camera da letto di casa nostra in una parete completamente di vetro.

DTO: la ‘carta regalo’ dei dati
Un’‘Entity’ è una ‘materia prima’ che si maneggia solo all’interno della fabbrica. Porta addosso cose che non dovrebbero mai essere mostrate al cliente, come segreti (password) o informazioni di gestione interna (data di creazione, data di modifica, flag di cancellazione).
Un ‘DTO(Data Transfer Object)’, invece, è il ‘prodotto finito’, ben confezionato per essere consegnato al cliente.
Restituire direttamente un’entità è come servire a un cliente, invece di una bistecca cotta, un pezzo di carne cruda e sanguinante lanciato nel piatto. Dobbiamo assolutamente passare per una fase di ‘mapping’.
[Code Verification] La palude del ciclo infinito (riferimento circolare)
Più dei problemi di sicurezza, ciò che fa impazzire gli sviluppatori è il problema dei ‘riferimenti circolari’. Ti ricordi il mapping bidirezionale JPA visto l’altra volta, User <-> Team?
Cosa succede se restituisci direttamente l’entità User convertita in JSON?
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne // Un utente appartiene a un team
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team") // Un team ha piu utenti
private List<User> users = new ArrayList<>();
}
Se restituisci User in questo stato, il serializzatore JSON (Jackson) si comporta così.
Risultato: il server cade eroicamente con un StackOverflowError. Le relazioni tra entità continuano a inseguirsi all’infinito, quindi se le esponi senza spezzare quella catena, precipiti in un ciclo infinito. Questo è il motivo decisivo per usare un DTO e contenere solo i campi necessari, come teamName.

La condizione di una buona API: essere RESTful
Se hai già confezionato bene i dati con i DTO, adesso devi scegliere bene l’‘indirizzo’ a cui spedire quel pacco. Ed è proprio questo il senso della progettazione di una REST API.
Ai tempi dell’università, davo alle URL i nomi che mi passavano per la testa.
Non è una buona idea inserire verbi come join o update nella URL. In una progettazione RESTful, la ‘risorsa’ (sostantivo) sta nella URL, mentre l’‘azione’ (verbo) viene affidata al metodo HTTP.
Costruendola così, basta guardare la URL per capire: “Ah, qui si sta gestendo la risorsa utente.” Inoltre comunicare con i frontend developer diventa molto più semplice. Questo è l’accordo implicito condiviso dagli sviluppatori di tutto il mondo.
Consiglio pratico: il nome fa già metà del lavoro (strategia di naming dei DTO)
Quando apro il codice del progetto attuale in azienda, a volte mi scappa subito un sospiro. UserDto, MemberVo, InfoParam, ResultData… chi c’era prima ha dato i nomi come gli pareva, quindi non puoi capire se un oggetto serve a ricevere una richiesta o a restituire una risposta finché non vai a leggere il codice.
Un DTO è il ‘contenitore’ dei dati. Se lo scopo di quel contenitore non è scritto nel nome, nasce la classica confusione per cui metti il riso nella scodella della zuppa e l’acqua nella ciotola del riso. La regola di naming che consiglio di più nel lavoro reale è questa.
public class UserDto {
// Richiesta di registrazione
public static class SignUpRequest {
private String email;
private String password;
}
// Risposta con le info utente
public static class Response {
private String name;
private String email;
}
}
Chiudendo la serie 2: ora la casa è costruita
Con questo si chiude la serie [Monolith Builder]. Abbiamo gettato le fondamenta con Spring Boot (DI), separato frontend e backend (CORS), imparato a usare il database con JPA e anche a comunicare attraverso DTO e REST API.
Ora abbiamo acquisito la capacità di costruire da soli un servizio web davvero dignitoso. Ma il viaggio di uno sviluppatore non finisce qui. A che serve se il servizio che ho creato funziona solo sul mio computer, cioè su localhost? Per mostrarlo a utenti reali, bisogna portarlo su un server.
Peccato che quel server non sia Windows, ma Linux: uno schermo nero e basta. Senza nemmeno un mouse, come si installano ed eseguono i programmi lì sopra?
Nella prossima serie, [Works on My Machine], entriamo nel mondo di Linux e Docker per risolvere il problema eterno degli sviluppatori: “Sul mio computer funziona, ma sul server no.”