Part 7: the safe pattern - compile-time subtype registry
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
package com.ankurm.jackson3.part7security;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import tools.jackson.databind.json.JsonMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post: Jackson Security Best Practices — https://ankurm.com/jackson-security-best-practices/
|
||||||
|
* Section: "Use @JsonTypeInfo Instead of Default Typing"
|
||||||
|
*
|
||||||
|
* The safe pattern: the permitted types are fixed at compile time, so no JSON payload
|
||||||
|
* can introduce a class name of its own.
|
||||||
|
*/
|
||||||
|
public class H01SafePolymorphismByAnnotation {
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = EmailNotification.class, name = "email"),
|
||||||
|
@JsonSubTypes.Type(value = SmsNotification.class, name = "sms")
|
||||||
|
})
|
||||||
|
public abstract static class Notification { }
|
||||||
|
|
||||||
|
public static class EmailNotification extends Notification {
|
||||||
|
public String recipientEmail;
|
||||||
|
@Override public String toString() { return "EmailNotification[" + recipientEmail + "]"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SmsNotification extends Notification {
|
||||||
|
public String recipientPhone;
|
||||||
|
@Override public String toString() { return "SmsNotification[" + recipientPhone + "]"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
JsonMapper mapper = JsonMapper.builder().build();
|
||||||
|
|
||||||
|
System.out.println("email : " + mapper.readValue(
|
||||||
|
"{\"type\":\"email\",\"recipientEmail\":\"a@example.com\"}", Notification.class));
|
||||||
|
System.out.println("sms : " + mapper.readValue(
|
||||||
|
"{\"type\":\"sms\",\"recipientPhone\":\"+441234567890\"}", Notification.class));
|
||||||
|
|
||||||
|
// A class name supplied by an attacker is not a registered logical name.
|
||||||
|
try {
|
||||||
|
mapper.readValue("{\"type\":\"com.malicious.Gadget\"}", Notification.class);
|
||||||
|
System.out.println("attack: UNEXPECTEDLY ACCEPTED");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("attack: rejected with " + e.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user