Whitelist problem records for spring security and ObjectMapper (json deserialization)

In the past few days, I've been working on a unified authentication platform supporting multi tenancy, so I thought of using oauth as an authorization protocol for distribution through jwt containing open user information. Some pits were encountered using this, and the records are as follows:

First of all, the problem encountered is the white list of json deserialization of fasterxml. Because the framework for dealing with json is vulnerable to attack, there are often some deserialization vulnerabilities. If a new one adopts a blacklist, the list will continue to grow and need to be updated. Therefore, fasterxml adopts the white list mechanism to realize json deserialization.

The problem arises from the use of the OAuth2AuthorizationService implementation class, which contains new ObjectMapper(); We can't control its own new ObjectMapper, so we must redefine an oauthauthorizationservice,

    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
                                                           RegisteredClientRepository registeredClientRepository,
                                                           ObjectMapper objectMapper) {
        JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
        JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper authorizationRowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
        authorizationRowMapper.setLobHandler(new DefaultLobHandler());
        return service;

By injecting ObjectMapper here, we can customize it.

    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            //jackson deserializes the security whitelist
            builder.mixIn(OpenAppUser.class, OpenAppUserMixin.class);
            List<Module> securityModules = SecurityJackson2Modules.getModules(LocalDateTimeSerializerConfig.class.getClassLoader());
            securityModules.add(new OAuth2AuthorizationServerJackson2Module());
            builder.modulesToInstall(securityModules.toArray(new Module[securityModules.size()]));

You can take a look at these modules, which are all security defined classes added to the security whitelist. Here, I define a user class myself, because the user who logs in to query uses this class
This enables json secure deserialization of custom user objects.

//In fact, when using security and objectmapper, three methods are provided to add the deserialized whitelist. In the above loading Module code, it will be called first
//Method, and then call it inside
//The returned is a TypeResolverBuilder. The implementation class here is AllowlistTypeResolverBuilder
private static TypeResolverBuilder<? extends TypeResolverBuilder> createAllowlistedDefaultTyping() {
        TypeResolverBuilder<? extends TypeResolverBuilder> result = new AllowlistTypeResolverBuilder(
        result = result.init(JsonTypeInfo.Id.CLASS, null);
        result = result.inclusion(JsonTypeInfo.As.PROPERTY);
        return result;
//The idResolver() method in the AllowlistTypeResolverBuilder class returns TypeIdResolver, and the implementation is an AllowlistTypeIdResolver()
protected TypeIdResolver idResolver(MapperConfig<?> config, JavaType baseType, PolymorphicTypeValidator subtypeValidator, Collection<NamedType> subtypes, boolean forSer, boolean forDeser) {
    TypeIdResolver result = super.idResolver(config, baseType,subtypeValidator, subtypes, forSer, forDeser);
    return new AllowlistTypeIdResolver(result);
//The key point is this class. The typeFromId method in it
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
    DeserializationConfig config = (DeserializationConfig) context.getConfig();
    JavaType result = this.delegate.typeFromId(context, id);
    String className = result.getRawClass().getName();
    if (isInAllowlist(className)) {
        return result;
    boolean isExplicitMixin = config.findMixInClassFor(result.getRawClass()) != null;
    if (isExplicitMixin) {
        return result;
    JacksonAnnotation jacksonAnnotation = AnnotationUtils.findAnnotation(result.getRawClass(),
    if (jacksonAnnotation != null) {
        eturn result;
    throw new IllegalArgumentException("The class with " + id + " and name of " + className
                    + " is not in the allowlist. "
                    + "If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. "
                    + "If the serialization is only done by a trusted source, you can also enable default typing. "
                    + "See https://github.com/spring-projects/spring-security/issues/4370 for details");

You can see the implementation of this method provided by security. The three methods are
1. Is isinallowlist() in this whitelist collection
2. Did you join MixIn? This is the way we used earlier
3. Is there @ JacksonAnnotation annotation on the deserialization class
For mode 1, it is a static set and cannot be modified; The other two are OK

In fact, the security provided by objectMapper is implemented through the PolymorphicTypeValidator class. The default security is

AllowlistTypeResolverBuilder(ObjectMapper.DefaultTyping defaultTyping) {
        // we do explicit validation in the TypeIdResolver

This means that as long as all subclasses of object pass, security customizes the white list when obtaining JavaType instead of using objectMapper.

Tags: JSON jackson spring-security

Posted on Thu, 11 Nov 2021 01:01:28 -0500 by steeveherris