« Hein ? Pourquoi le mot de passe est visible ? »
Au début de ma carrière dans l’entreprise, je jonglais sans arrêt entre le frontend et le backend. Un jour, j’étais en train d’intégrer à l’interface frontend l’« API de liste des membres » développée par un collègue embauché en même temps que moi.
J’ai ouvert l’onglet Network des outils de développement Chrome (F12) pour vérifier que les données arrivaient bien, et au moment où j’ai vu la réponse, j’ai cru halluciner.
[
{
"id": 1,
"username": "tester",
"password": "$2a$10$D...", // Mot de passe chiffre expose
"ssn": "900101-1...", // Numero de securite sociale expose
"createdAt": "2024-01-01"
}
]
Je lui ai demandé, paniqué : « Hé, pourquoi on voit le mot de passe et même le numéro d’identité dans la réponse de cette API ? »
Il a répondu avec détachement : « Ah ça ? De toute façon, à l’écran on n’affiche que le nom. J’avais la flemme, donc j’ai juste renvoyé le résultat de userRepository.findAll() tel quel. Il y a un problème ? »
J’ai eu un frisson dans le dos. Il cherchait seulement la facilité, mais en pratique il venait de semer la graine d’un incident de sécurité majeur.
« Ce n’est pas parce que ce n’est pas visible à l’écran que c’est sûr ! »
N’importe qui peut voir l’onglet réseau du navigateur. Et si un attaquant malveillant appelait cette API ?
Exposer directement une entité, c’était comme transformer notre chambre à coucher en mur entièrement vitré.

DTO : le ‘papier cadeau’ des données
Une ‘Entity’ est une sorte de ‘matière première’ que l’on manipule uniquement à l’intérieur de l’usine. On y trouve des informations qu’un client ne devrait jamais voir, comme des secrets (mots de passe) ou des champs de gestion interne (date de création, date de modification, indicateur de suppression).
Un ‘DTO(Data Transfer Object)’, à l’inverse, est le ‘produit fini’, proprement emballé pour être livré au client.
Retourner directement une entité, c’est comme jeter à un client un morceau de viande crue et sanglante dans son assiette au lieu de lui servir un steak cuisiné. Il faut absolument passer par une étape de ‘mapping’.
[Code Verification] Le marécage des boucles infinies (référence circulaire)
Ce qui rend les développeurs encore plus fous que les problèmes de sécurité, c’est le problème des ‘références circulaires’. Vous vous souvenez du mapping bidirectionnel JPA vu la dernière fois, User <-> Team ?
Que se passerait-il si l’on renvoyait l’entité User telle quelle après l’avoir convertie en JSON ?
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne // Un utilisateur appartient a une equipe
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team") // Une equipe a plusieurs utilisateurs
private List<User> users = new ArrayList<>();
}
Si l’on retourne User dans cet état, le sérialiseur JSON (Jackson) fonctionne de cette manière.
Résultat : le serveur meurt avec panache dans un StackOverflowError. Les relations entre entités se poursuivent les unes les autres sans fin. Si on les expose telles quelles sans couper cette chaîne, on tombe dans une boucle infinie. C’est la raison décisive pour utiliser un DTO et n’y mettre que les champs nécessaires, comme teamName.

La condition d’une bonne API : être RESTful
Une fois les données correctement emballées avec des DTO, il faut maintenant définir la bonne ‘adresse’ à laquelle envoyer ce colis. C’est exactement cela, la conception d’API REST.
À la fac, je nommais mes URL comme bon me semblait.
Mettre des verbes comme join ou update dans l’URL n’est pas une bonne idée. Dans une conception RESTful, la ‘ressource’ (nom) se place dans l’URL et l’‘action’ (verbe) est portée par la méthode HTTP.
Construite ainsi, l’URL permet à elle seule de comprendre : « Ah, on manipule la ressource utilisateur. » Et la communication avec les développeurs frontend devient bien plus simple. C’est la convention commune des développeurs du monde entier.
Conseil pratique : le nom fait déjà la moitié du travail (stratégie de nommage DTO)
Quand j’ouvre aujourd’hui le code du projet actuel de l’entreprise, il m’arrive de soupirer dès les premiers instants. UserDto, MemberVo, InfoParam, ResultData… les prédécesseurs ont nommé les choses comme ils voulaient, si bien qu’on ne sait pas avant d’ouvrir le code si l’objet sert à recevoir une requête ou à envoyer une réponse.
Un DTO est un ‘récipient’ pour les données. Si l’usage de ce récipient n’apparaît pas dans son nom, on obtient vite une confusion du genre soupe dans le bol à riz et eau dans le bol à soupe. La règle de nommage que je recommande le plus en pratique est la suivante.
public class UserDto {
// Requete d inscription
public static class SignUpRequest {
private String email;
private String password;
}
// Reponse des infos utilisateur
public static class Response {
private String name;
private String email;
}
}
Fin de la série 2 : la maison est maintenant construite
Ainsi s’achève la série [Monolith Builder]. Nous avons posé les bases avec Spring Boot (DI), séparé frontend et backend (CORS), appris à manipuler la base de données avec JPA, et même appris à communiquer via DTO et API REST.
Désormais, nous avons la capacité de construire seuls un service web solide. Mais le parcours d’un développeur ne s’arrête pas ici. À quoi bon si le service que j’ai créé ne fonctionne que sur mon ordinateur, en localhost ? Pour le montrer à de vrais utilisateurs, il faut le déployer sur un serveur.
Sauf que ce serveur n’est pas sous Windows. C’est Linux, un écran noir et rien d’autre. Sans souris, comment installe-t-on et exécute-t-on des programmes dans cet environnement ?
Dans la prochaine série, [Works on My Machine], partons à la découverte de Linux et Docker pour résoudre l’éternel casse-tête des développeurs : « Sur ma machine ça marche, mais pas sur le serveur. »