[JAVA] Lassen Sie uns bei der Ausgabe des Spring Binding Result (Errors) zum Protokollieren richtig maskieren

In einer Webanwendung, die Spring (Spring MVC) verwendet, werden Fehlerinformationen zur Eingabeprüfung von der Schnittstelle "BindingResult" ausgedrückt und können wie unten gezeigt als Argument für die Handlermethode des Controllers empfangen werden.

@PostMapping
public String change(@AuthenticationPrincipal AccountUserDetails userDetails,
    @Validated PasswordChangeForm form, BindingResult result) { // 
  if (result.hasErrors()) {
    return changeForm();
  }
  accountService.changePassword(userDetails.getAccount(), form.getNewPassword());
  return "welcome/home";
}

Es gibt kein Problem mit dem obigen Code, aber fügen wir den folgenden Code hinzu und geben die Fehlerinformationen an die Konsole aus.

System.out.println(result);

Die folgenden Informationen werden an die Konsole ausgegeben.

org.springframework.validation.BeanPropertyBindingResult: 6 errors
Field error in object 'passwordChangeForm' on field 'confirmPassword': rejected value [ccc]; codes [com.example.validation.Confirm.message.passwordChangeForm.confirmPassword,com.example.validation.Confirm.message.confirmPassword,com.example.validation.Confirm.message.java.lang.String,com.example.validation.Confirm.message]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.confirmPassword,confirmPassword]; arguments []; default message [confirmPassword],org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@41663a9a,PROPERTY,EQUAL,false,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@7dd16c6]; default message [must same value with "newPassword"]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [bb]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must contain 1 or more special characters.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [bb]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must be 8 or more characters in length.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [bb]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must contain 1 or more uppercase characters.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [bb]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must contain 1 or more digit characters.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [bb]; codes [error.passwordChangeForm.newPassword,error.newPassword,error.java.lang.String,error]; arguments []; default message [error!]

Der Fehler "Feld", "tatsächlicher Eingabewert", "Fehlercode", "Nachrichtenargument", "Standardnachricht" usw. werden ausgegeben, aber ** "tatsächlicher Eingabewert (abgelehnter Wert [ccc];" Teil Bitte beachten Sie, dass ")" ausgegeben wird **. Im obigen Beispiel wird ** der Wert von "Passwort", der als vertrauliche Information behandelt werden soll, so ausgegeben, wie er ist. ** ** ** ** Mit anderen Worten ... Wenn ein Eingabeprüfungsfehler protokolliert werden muss, führt die einfache Verwendung von "BindingResult # toString ()" zu einer Anwendung, die die Sicherheitsanforderungen nicht erfüllt. ** ** **

Note:

Ähnliche Informationen können in Protokollen usw. aufgezeichnet werden, ohne den Code, der BindingResult # toString () aufruft, explizit zu implementieren, wie im obigen Beispiel. Beispiel: Wenn Sie im Argument der Handler-Methode von Controller kein BindingResult angeben (= weggelassen), generiert Spring "BondException" oder "MethodArgumentNotValidException". Da diese Ausnahmemeldungen denselben Inhalt wie "BindingResult # toString ()" enthalten, werden vertrauliche Informationen unbeabsichtigt angezeigt, wenn die Implementierung so ist, dass das Protokoll bedingungslos mit einem Ausnahmehandler usw. ausgegeben wird. Es wird in das Protokoll ausgegeben.

Was soll ich machen?

Einfach zu beantworten! Verwenden Sie "BindingResult # toString ()" nicht für Protokolle, die in einer kommerziellen Umgebung ausgegeben werden! !! darüber. Eine Webanwendung für die Benutzeroberfläche gibt einen Fehler bei der Eingabeprüfung im Protokoll aus! Es scheint, dass es nicht viele spezifische Anforderungen gibt, aber ... In einigen Fällen ist es erforderlich, Protokolle für die Fehleranalyse in Webanwendungen (Web-API, REST-API) für die systemübergreifende Zusammenarbeit auszugeben. Verwenden Sie in einem solchen Fall nicht einfach "BindingResult # toString ()", sondern erstellen Sie eine entsprechende Protokollmeldung aus den Fehlerinformationen von BindingResult. Versuchen Sie außerdem, BindException und MethodArgumentNotValidException in Ihrer Exception-Handler-Implementierung separat zu behandeln.

Versuchen Sie, vertrauliche Informationen zu maskieren

Hier,

Lassen Sie uns eine Protokollnachricht mit der Anforderung zusammenstellen.

final StringJoiner joiner = new StringJoiner("\n")
    .add(result.getClass().getName() + ":" + result.getErrorCount() + " errors");
result.getGlobalErrors().forEach(error -> joiner.add(error.toString()));
result.getFieldErrors().forEach(error -> {
  if (error.getField().toLowerCase().contains("password")) {
    String message = error.toString();
    int sIndex = message.indexOf("rejected value [") + 16;
    int eIndex = message.indexOf("]; codes");
    joiner.add(message.substring(0, sIndex) + IntStream.range(0, eIndex - sIndex)
        .mapToObj(value -> "*").collect(Collectors.joining()) + message.substring(eIndex));
  } else {
    joiner.add(error.toString());
  }
});
System.out.println(joiner.toString());

Ich denke, dass es ein wenig gewaltsames Dribbeln (Implementierung) ist, aber die Nachricht, die mit der obigen Logik zusammengestellt wurde, lautet wie folgt.

org.springframework.validation.BeanPropertyBindingResult:6 errors
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [**]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must contain 1 or more uppercase characters.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [**]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must be 8 or more characters in length.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [**]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must contain 1 or more digit characters.]
Field error in object 'passwordChangeForm' on field 'confirmPassword': rejected value [***]; codes [com.example.validation.Confirm.message.passwordChangeForm.confirmPassword,com.example.validation.Confirm.message.confirmPassword,com.example.validation.Confirm.message.java.lang.String,com.example.validation.Confirm.message]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.confirmPassword,confirmPassword]; arguments []; default message [confirmPassword],org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@6d154140,PROPERTY,EQUAL,false,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@41678dbe]; default message [must same value with "newPassword"]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [**]; codes [Password.passwordChangeForm.newPassword,Password.newPassword,Password.java.lang.String,Password]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [passwordChangeForm.newPassword,newPassword]; arguments []; default message [newPassword],true]; default message [must contain 1 or more special characters.]
Field error in object 'passwordChangeForm' on field 'newPassword': rejected value [**]; codes [error.passwordChangeForm.newPassword,error.newPassword,error.java.lang.String,error]; arguments []; default message [error!]

Zusammenfassung

Je nach System kann es erforderlich sein, vertrauliche Informationen bei der Ausgabe einer Nachricht (Nachricht?) Im Protokoll zu maskieren, das Anwendungsprotokoll ist jedoch auch dasselbe. Es war eine Geschichte. In diesem Beitrag habe ich ein Implementierungsbeispiel mit einer Technik namens Maskierung vorgestellt. Die Maskierung ist jedoch nicht die einzige Möglichkeit, vertrauliche Informationen zu schützen. Lassen Sie uns dies auf eine Weise tun, die den Eigenschaften und Anforderungen des Systems entspricht! !!

Recommended Posts

Lassen Sie uns bei der Ausgabe des Spring Binding Result (Errors) zum Protokollieren richtig maskieren
Um Fehler beim Starten von miChecker zu vermeiden