mardi 7 mars 2023

[Spring Boot] Comment éviter de retourner la stack trace au client dans une application Spring?

 Lorsque vous développez une application Spring Boot, il est important de gérer correctement les erreurs et les exceptions pour éviter de renvoyer des informations sensibles à un client.

Voici quelques conseils et exemples de bonnes pratiques pour éviter de retourner la stack trace au client : A partir de java 8 et avec l'introduction des optionnelles je préfére les utiliser pour gérer les exceptions proporements Voiçi un exemple de code

java
@RequestMapping("/api/users/{id}")
public User getUserById(@PathVariable Long id) {
 log.info("Fetching user with id: {}", id);
User user = userRepository.findById(id) 
 .orElseThrow(() -> {
     log.error("User with id {} not found", id);
    return new UserNotFoundException("User not found with id: " + id);
 });
return user;
}

Sinon je vous laisse lire les autres manières de faire :

  1. Ne renvoyez jamais la stack trace directement au client

Mauvaise pratique :

java
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    return userService.getUserById(id);
}

Dans cet exemple, si une exception se produit lors de la recherche d'un utilisateur, la stack trace sera retournée au client.

Bonne pratique :

Dans l'exemple de mauvaise pratique, le renvoi de l'objet User directement peut renvoyer la stack trace au client en cas d'erreur, ce qui est peu convivial. En revanche, dans la bonne pratique, en renvoyant des réponses HTTP appropriées, vous offrez au client une réponse claire et structurée, améliorant ainsi l'expérience utilisateur.

java
@GetMapping("/users/{id}")
public
ResponseEntity<User> getUser(@PathVariable Long id) {
try {
    User user = userService.getUserById(id);
    return
ResponseEntity.ok(user);
 }
catch (UserNotFoundException e) {
    return
ResponseEntity.notFound().build();
 }
}

Dans cet exemple, nous retournons une réponse HTTP personnalisée en utilisant ResponseEntity. Si l'utilisateur n'est pas trouvé, nous retournons un code de statut 404 Not Found, plutôt que de renvoyer la stack trace au client.

  1. Loguez l'erreur pour faciliter le debugging

Mauvaise pratique :

java
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    return userService.getUserById(id);
}

Dans cet exemple, si une exception se produit lors de la recherche d'un utilisateur, aucune information sur l'erreur n'est enregistrée.

Bonne pratique :

La journalisation des erreurs est essentielle pour le débogage.

Dans l'exemple de mauvaise pratique, aucune information sur l'erreur n'est enregistrée, ce qui complique le processus de débogage. En revanche, la bonne pratique enregistre les erreurs dans les fichiers journaux, ce qui facilite l'identification et la résolution des problèmes.

java
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
try {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
 }
catch (UserNotFoundException e) {
 
logger.error("L'utilisateur avec l'ID {} n'a pas été trouvé", id);
return ResponseEntity.notFound().build();
 }
catch (Exception e) {
 logger.error("Une erreur s'est produite lors de la recherche de l'utilisateur avec l'ID {}", id, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
 }
}

Dans cet exemple, nous utilisons un logger pour enregistrer

3. Évitez d'utiliser les exceptions globales dans votre code. Cela peut renvoyer une stack trace complète au client, ce qui peut être exploité pour des attaques. Au lieu de cela, vous pouvez utiliser les exceptions personnalisées pour gérer les erreurs et renvoyer des messages d'erreur clairs aux clients.

Mauvaise pratique :

java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<Object> handleException(Exception exception) {
return new ResponseEntity<>("Erreur interne du serveur", HttpStatus.INTERNAL_SERVER_ERROR);
 }
}

Bonne pratique :

Les exceptions personnalisées permettent de renvoyer des messages d'erreur clairs et explicites aux clients sans divulguer la stack trace complète. Dans l'exemple de bonne pratique, en utilisant @ExceptionHandler pour gérer l'exception personnalisée MyCustomException, vous assurez que le client reçoit un message d'erreur compréhensible, améliorant ainsi la convivialité de l'API.

java
@ExceptionHandler(MyCustomException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleMyException(MyCustomException ex) {
return ex.getMessage();
}
  1. Utilisez les fichiers de propriétés de configuration pour masquer les messages d'erreur dans la sortie JSON. Vous pouvez utiliser le fichier application.properties pour activer la configuration suivante :

Mauvaise pratique :


spring.jackson.serialization.fail-on-empty-beans=false
Pourquoi c'est une mauvaise pratique ?
Si vous avez des objets vides qui sont sérialisés en JSON et que vous les renvoyez au client, assurez-vous que cela ne compromet pas la sécurité. Parfois, un objet vide peut contenir des informations sensibles, et le fait de renvoyer un JSON vide peut exposer ces informations.

Bonne pratique :

La configuration des propriétés Jackson pour masquer les erreurs dans la sortie JSON est essentielle pour la sécurité. En configurant ces propriétés correctement, vous empêchez les informations sensibles, y compris la stack trace, d'être renvoyées au client. Cela réduit le risque d'exposition d'informations confidentielles.


spring.jackson.default-property-inclusion=NON_NULL spring.jackson.serialization.fail-on-empty-beans=true spring.jackson.deserialization.fail-on-unknown-properties=true

en résumé voici un tableau comparatif des annotations et classes que vous pouvez utiliser pour gérer les exceptions dans votre application :

Annotation/ClasseAvantagesInconvénients
@ExceptionHandlerPermet de définir une méthode de gestion des exceptions pour un contrôleur spécifique.La méthode doit être appelée explicitement à partir du contrôleur pour être exécutée.
@ResponseStatusPermet de définir un code de réponse HTTP personnalisé pour une exception.Ne fournit pas de mécanisme de traitement de l'exception en soi.
@ControllerAdvicePermet de définir une classe qui fournit des méthodes de gestion des exceptions pour tous les contrôleurs de l'application.Peut être moins efficace que la gestion des exceptions au niveau du contrôleur en termes de performances, car toutes les méthodes de gestion des exceptions de la classe seront appelées pour chaque exception levée dans l'application.
ResponseStatusException.classPermet de créer une exception personnalisée qui renvoie un code de réponse HTTP spécifique au client.Ne fournit pas de mécanisme de traitement de l'exception en soi.

La configuration de ces propriétés dans votre fichier de configuration de l'application Spring Boot peut influencer la façon dont les erreurs sont gérées et renvoyées au client.

  • server.error.include-binding-errors: cette propriété contrôle l'inclusion des erreurs de liaison dans la réponse d'erreur envoyée au client. Si elle est définie sur "never", les erreurs de liaison ne seront pas incluses dans la réponse d'erreur.

  • server.error.include-exception: cette propriété contrôle l'inclusion de l'exception dans la réponse d'erreur envoyée au client. Si elle est définie sur "false", l'exception ne sera pas incluse dans la réponse d'erreur.

  • server.error.include-message: cette propriété contrôle l'inclusion du message d'erreur dans la réponse d'erreur envoyée au client. Si elle est définie sur "never", le message d'erreur ne sera pas inclus dans la réponse d'erreur.

  • server.error.include-stacktrace: cette propriété contrôle l'inclusion de la stack trace dans la réponse d'erreur envoyée au client. Si elle est définie sur "never", la stack trace ne sera pas incluse dans la réponse d'erreur.

Si ces propriétés sont correctement configurées, les erreurs ne devraient pas être renvoyées avec la stack trace au client, ce qui peut améliorer la sécurité

    Aucun commentaire:

    Enregistrer un commentaire

    to criticize, to improve