Developer
News and Updates
Get Support
Sign in
Get Support
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Last updated Sep 25, 2025

Phase 1: Immediate security (Blocklist mode)

Goal: Rapidly protect against known threats with minimal code changes

Timeline: 1-2 weeks

Security improvement: Good protection against known dangerous classes

This phase provides immediate security benefits by switching to BlocklistRestrictedXStream in blocklist mode, which blocks known dangerous classes while maintaining broad compatibility.

Step 1.1: Add dependency

Replace existing XStream dependency in your pom.xml:

1
2
<!-- Remove or comment out existing XStream dependency -->
<!--
<dependency>
    <groupId>com.thoughtworks</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.x</version>
</dependency>
-->

<!-- Add blocklist adapter -->
<dependency>
    <groupId>com.atlassian.security.serialblocklist</groupId>
    <artifactId>blocklist-xstream-adapter</artifactId>
    <version>${serialblocklist.version}</version>
    <scope>provided</scope>
</dependency>

For Gradle projects:

1
2
// Remove existing XStream dependency
// implementation 'com.thoughtworks:xstream:1.4.x'

// Add blocklist adapter
implementation 'com.atlassian.security.serialblocklist:blocklist-xstream-adapter:${serialblocklist.version}'

Step 1.2: Replace XStream instantiation

Simple replacement

Before (unsafe):

1
2
XStream xstream = new XStream();
xstream.addPermission(AnyTypePermission.ANY);

After (blocklist mode):

1
2
import com.atlassian.security.serialblocklist.xstream.BlocklistRestrictedXStream;

XStream xstream = new BlocklistRestrictedXStream();
xstream.addPermission(AnyTypePermission.ANY);  // Blocklist mode

Constructor variations

Custom driver:

1
2
// Before
XStream xstream = new XStream(new DomDriver());

// After
XStream xstream = new BlocklistRestrictedXStream(new DomDriver());
xstream.addPermission(AnyTypePermission.ANY);

Custom reflection provider:

1
2
// Before
XStream xstream = new XStream(new PureJavaReflectionProvider());

// After
XStream xstream = new BlocklistRestrictedXStream(new PureJavaReflectionProvider());
xstream.addPermission(AnyTypePermission.ANY);

Step 1.3: Update factory methods

If you use factory methods or dependency injection:

Spring Framework

Before:

1
2
@Bean
public XStream xstream() {
    XStream xstream = new XStream();
    xstream.addPermission(AnyTypePermission.ANY);
    
    // Existing configuration continues to work
    xstream.alias("user", User.class);
    xstream.omitField(User.class, "password");
    
    return xstream;
}

After:

1
2
@Bean
public XStream xstream() {
    XStream xstream = new BlocklistRestrictedXStream();
    xstream.addPermission(AnyTypePermission.ANY);  // Blocklist mode
    
    // All existing configuration continues to work unchanged
    xstream.alias("user", User.class);
    xstream.omitField(User.class, "password");
    
    return xstream;
}

Static factory methods

Before:

1
2
public class XStreamFactory {
    public static XStream createXStream() {
        XStream xstream = new XStream();
        xstream.addPermission(AnyTypePermission.ANY);
        configureAliases(xstream);
        return xstream;
    }
}

After:

1
2
public class XStreamFactory {
    public static XStream createXStream() {
        XStream xstream = new BlocklistRestrictedXStream();
        xstream.addPermission(AnyTypePermission.ANY);  // Blocklist mode
        configureAliases(xstream);  // Existing configuration works
        return xstream;
    }
}

Step 1.4: Handle singleton patterns

Singleton XStream instances

Before:

1
2
public class XStreamSingleton {
    private static final XStream INSTANCE = createInstance();
    
    private static XStream createInstance() {
        XStream xstream = new XStream();
        xstream.addPermission(AnyTypePermission.ANY);
        return xstream;
    }
    
    public static XStream getInstance() {
        return INSTANCE;
    }
}

After:

1
2
public class XStreamSingleton {
    private static final XStream INSTANCE = createInstance();
    
    private static XStream createInstance() {
        XStream xstream = new BlocklistRestrictedXStream();
        xstream.addPermission(AnyTypePermission.ANY);  // Blocklist mode
        return xstream;
    }
    
    public static XStream getInstance() {
        return INSTANCE;
    }
}

Step 1.5: Test the migration

1. Unit tests

Verify existing functionality still works:

1
2
@Test
public void shouldMaintainExistingFunctionality() {
    XStream xstream = new BlocklistRestrictedXStream();
    xstream.addPermission(AnyTypePermission.ANY);
    
    // Configure as before
    xstream.alias("user", User.class);
    
    User user = new User("john", "john@example.com");
    String xml = xstream.toXML(user);
    User restored = (User) xstream.fromXML(xml);
    
    assertEquals("john", restored.getUsername());
    assertEquals("john@example.com", restored.getEmail());
}

2. Security tests

Verify dangerous classes are now blocked:

1
2
@Test
public void shouldBlockDangerousClasses() {
    XStream xstream = new BlocklistRestrictedXStream();
    xstream.addPermission(AnyTypePermission.ANY);
    
    // This should now throw ForbiddenClassException
    assertThrows(ForbiddenClassException.class, () -> {
        String maliciousXml = "<java.lang.ProcessBuilder>" +
            "<command><string>calc.exe</string></command>" +
            "</java.lang.ProcessBuilder>";
        xstream.fromXML(maliciousXml);
    });
}

@Test
public void shouldBlockOtherDangerousClasses() {
    XStream xstream = new BlocklistRestrictedXStream();
    xstream.addPermission(AnyTypePermission.ANY);
    
    // Test various dangerous classes
    String[] dangerousClasses = {
        "java.lang.Runtime",
        "java.lang.ProcessBuilder",
        "javax.script.ScriptEngineManager"
        // Add more based on your security requirements
    };
    
    for (String className : dangerousClasses) {
        assertThrows(ForbiddenClassException.class, () -> {
            String xml = "<" + className + "></" + className + ">";
            xstream.fromXML(xml);
        }, "Should block " + className);
    }
}

3. Integration tests

Test end-to-end workflows:

1
2
@SpringBootTest
@TestPropertySource(properties = {
    "app.xstream.mode=blocklist"  // Test configuration
})
class XStreamIntegrationTest {
    
    @Autowired
    private XStream xstream;
    
    @Test
    void shouldProcessLegitimateData() {
        // Test your actual application data flows
        ApplicationData data = createTestData();
        
        String xml = xstream.toXML(data);
        ApplicationData restored = (ApplicationData) xstream.fromXML(xml);
        
        assertThat(restored).isEqualTo(data);
    }
    
    @Test
    void shouldRejectMaliciousData() {
        String maliciousXml = loadMaliciousTestPayload();
        
        assertThrows(ForbiddenClassException.class, () -> {
            xstream.fromXML(maliciousXml);
        });
    }
}

Step 1.6: Deploy gradually

1. Canary deployment

Deploy to a small subset of traffic first:

1
2
@Component
@ConditionalOnProperty("feature.secure-xstream.enabled")
public class SecureXStreamConfiguration {
    
    @Bean
    @Primary
    public XStream secureXStream() {
        XStream xstream = new BlocklistRestrictedXStream();
        xstream.addPermission(AnyTypePermission.ANY);
        // Configure as needed
        return xstream;
    }
}

2. Feature flag controlled rollout

1
2
@Component
public class XStreamFactory {
    
    @Value("${features.secure-xstream:false}")
    private boolean useSecureXStream;
    
    public XStream createXStream() {
        if (useSecureXStream) {
            XStream xstream = new BlocklistRestrictedXStream();
            xstream.addPermission(AnyTypePermission.ANY);
            return xstream;
        } else {
            // Fallback to old implementation during transition
            XStream xstream = new XStream();
            xstream.addPermission(AnyTypePermission.ANY);
            return xstream;
        }
    }
}

3. Monitor deployment

Add monitoring to track the migration:

1
2
@Component
public class XStreamMigrationMonitor {
    
    private final MeterRegistry meterRegistry;
    
    @EventListener
    public void onSecurityBlock(ForbiddenClassException e) {
        // Track blocked attempts
        meterRegistry.counter("xstream.security.blocked", 
            "class", e.getMessage(),
            "phase", "migration-phase1").increment();
        
        // Log for analysis
        logger.info("Phase 1 migration blocked dangerous class: {}", e.getMessage());
    }
    
    @EventListener
    public void onSuccessfulProcessing(XStreamProcessingEvent e) {
        meterRegistry.counter("xstream.processing.success",
            "phase", "migration-phase1").increment();
    }
}

Step 1.7: Validate security improvement

Create test scenarios

1
2
public class Phase1SecurityValidation {
    
    @Test
    public void validateSecurityImprovement() {
        XStream oldXStream = new XStream();
        oldXStream.addPermission(AnyTypePermission.ANY);
        
        XStream newXStream = new BlocklistRestrictedXStream();
        newXStream.addPermission(AnyTypePermission.ANY);
        
        String[] attackPayloads = loadKnownAttackPayloads();
        
        for (String payload : attackPayloads) {
            // Old XStream should be vulnerable
            assertDoesNotThrow(() -> oldXStream.fromXML(payload));
            
            // New XStream should block the attack
            assertThrows(ForbiddenClassException.class, () -> newXStream.fromXML(payload));
        }
    }
}

Common issues and solutions

Issue 1: NoSuchMethodError or ClassNotFoundException

Cause: Dependency conflicts or missing dependencies

Solution: Check dependency tree and resolve conflicts:

1
2
mvn dependency:tree | grep -i xstream

Ensure you're not mixing different XStream versions.

Issue 2: Existing functionality breaks

Cause: Missing AnyTypePermission.ANY permission

Solution: Ensure you add the permission for blocklist mode:

1
2
// Don't forget this line for Phase 1
xstream.addPermission(AnyTypePermission.ANY);

Issue 3: Custom converters not working

Cause: Converter priority conflicts

Solution: Ensure custom converters use appropriate priority:

1
2
// Use priority lower than blocklist converter
xstream.registerConverter(new MyConverter(), XStream.PRIORITY_NORMAL);

Success criteria for Phase 1

Phase 1 is complete when:

  1. All XStream instances use BlocklistRestrictedXStream
  2. All instances are configured with AnyTypePermission.ANY (blocklist mode)
  3. Existing functionality works unchanged
  4. Security tests confirm dangerous classes are blocked
  5. Production deployment is stable
  6. Monitoring shows blocked attack attempts (if any)

Next steps

After successfully completing Phase 1:

  1. Monitor for 1-2 weeks to ensure stability
  2. Analyze blocked attempts to understand attack patterns
  3. Begin Phase 2 to analyze actual type usage patterns
  4. Plan for Phase 3 migration to allowlist mode (maximum security)

Phase 1 provides immediate security benefits while maintaining full compatibility, making it an ideal first step in the migration process.

Rate this page: