package com.ankurm.jackson3.part7security; import tools.jackson.core.StreamReadConstraints; import tools.jackson.core.json.JsonFactory; import tools.jackson.databind.json.JsonMapper; /** * BEYOND THE POST — the hardening the security post does not mention. * * Gadget attacks are not the only deserialisation risk. A small payload can also * exhaust the stack or the heap: deeply nested arrays, gigantic numbers, enormous * strings. Jackson 3 ships StreamReadConstraints with defaults for all three, and * they are tunable. Any service accepting external JSON should know what they are. */ public class H04StreamReadConstraints { public static void main(String[] args) { StreamReadConstraints defaults = StreamReadConstraints.defaults(); System.out.println("--- Jackson 3 defaults ---"); System.out.println("max nesting depth : " + defaults.getMaxNestingDepth()); System.out.println("max number length : " + defaults.getMaxNumberLength()); System.out.println("max string length : " + defaults.getMaxStringLength()); System.out.println("max name length : " + defaults.getMaxNameLength()); System.out.println("max doc length : " + defaults.getMaxDocumentLength() + " (-1 = unlimited)"); System.out.println(); JsonMapper plain = JsonMapper.builder().build(); String deep = "[".repeat(1200) + "]".repeat(1200); System.out.println("1200-deep nesting, default limits -> " + attempt(plain, deep)); // Tighten the limits for an endpoint that should never see nested data. JsonFactory strictFactory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder() .maxNestingDepth(10) .maxStringLength(2_000) .build()) .build(); JsonMapper strict = JsonMapper.builder(strictFactory).build(); System.out.println("20-deep nesting, strict limits -> " + attempt(strict, "[".repeat(20) + "]".repeat(20))); System.out.println("5-deep nesting, strict limits -> " + attempt(strict, "[".repeat(5) + "]".repeat(5))); System.out.println("3KB string, strict limits -> " + attempt(strict, "\"" + "x".repeat(3_000) + "\"")); } private static String attempt(JsonMapper mapper, String json) { try { mapper.readTree(json); return "accepted"; } catch (Exception e) { return "rejected (" + e.getClass().getSimpleName() + ")"; } } }