0
# Security and Class Resolution
1
2
Apache Fury provides comprehensive security features to protect against deserialization attacks and ensure safe handling of untrusted data through class validation and resolution mechanisms.
3
4
## Class Resolution System
5
6
### ClassResolver
7
8
Core class resolution and management system for handling class metadata during serialization.
9
10
```java { .api }
11
public class ClassResolver {
12
// Class registration
13
public void register(Class<?> clazz);
14
public void register(Class<?> clazz, short classId);
15
public void register(Class<?> clazz, boolean createSerializer);
16
17
// Class resolution
18
public ClassInfo getClassInfo(Class<?> clazz);
19
public ClassInfo getClassInfo(short classId);
20
public ClassInfo getClassInfo(String className);
21
22
// Class ID management
23
public short getClassId(Class<?> clazz);
24
public Class<?> getClass(short classId);
25
26
// Security validation
27
public void checkClass(Class<?> clazz);
28
public boolean isRegistered(Class<?> clazz);
29
public boolean isAllowed(Class<?> clazz);
30
31
// Metadata management
32
public void addClassInfo(ClassInfo classInfo);
33
public void setClassChecker(ClassChecker classChecker);
34
}
35
```
36
37
### ClassInfo
38
39
Metadata container for class information used during serialization.
40
41
```java { .api }
42
public class ClassInfo {
43
// Class information
44
public Class<?> getCls();
45
public String getClassName();
46
public short getClassId();
47
48
// Serialization metadata
49
public Serializer<?> getSerializer();
50
public boolean needToWriteRef();
51
public boolean isEnum();
52
public boolean isFinal();
53
54
// Type information
55
public Type getFuryType();
56
public boolean isCollection();
57
public boolean isMap();
58
public boolean isArray();
59
60
// Factory methods
61
public static ClassInfo of(Class<?> clazz);
62
public static ClassInfo of(Class<?> clazz, Serializer<?> serializer);
63
}
64
```
65
66
## Security Framework
67
68
### ClassChecker Interface
69
70
Interface for implementing custom class validation logic.
71
72
```java { .api }
73
public interface ClassChecker {
74
/**
75
* Check if a class is allowed for deserialization.
76
* Throws InsecureException if class is not allowed.
77
*/
78
void checkClass(ClassResolver classResolver, String className);
79
80
/**
81
* Check if a class is allowed for deserialization.
82
*/
83
default boolean isAllowed(String className) {
84
try {
85
checkClass(null, className);
86
return true;
87
} catch (InsecureException e) {
88
return false;
89
}
90
}
91
}
92
```
93
94
### AllowListChecker
95
96
Allow-list based class checker for restricting deserialization to approved classes.
97
98
```java { .api }
99
public class AllowListChecker implements ClassChecker {
100
// Constructors
101
public AllowListChecker(Set<String> allowedClasses);
102
public AllowListChecker(Set<String> allowedClasses, Set<String> allowedPackages);
103
104
// Allow-list management
105
public void addAllowedClass(String className);
106
public void addAllowedClass(Class<?> clazz);
107
public void addAllowedPackage(String packageName);
108
public void removeAllowedClass(String className);
109
public void removeAllowedPackage(String packageName);
110
111
// Validation
112
@Override
113
public void checkClass(ClassResolver classResolver, String className);
114
public boolean isClassAllowed(String className);
115
public boolean isPackageAllowed(String packageName);
116
117
// Utilities
118
public Set<String> getAllowedClasses();
119
public Set<String> getAllowedPackages();
120
}
121
```
122
123
### DisallowedList
124
125
Utility for managing commonly dangerous classes that should be blocked.
126
127
```java { .api }
128
public class DisallowedList {
129
// Dangerous class patterns
130
public static final Set<String> DANGEROUS_CLASSES;
131
public static final Set<String> DANGEROUS_PACKAGES;
132
133
// Utility methods
134
public static boolean isDangerous(String className);
135
public static boolean isDangerousPackage(String packageName);
136
public static ClassChecker createSecureChecker();
137
public static ClassChecker createSecureChecker(Set<String> additionalAllowed);
138
}
139
```
140
141
## Security Configuration
142
143
### Secure Fury Builder
144
145
```java { .api }
146
public class SecureFuryBuilder {
147
// Create secure Fury instance with class registration required
148
public static Fury createSecureFury();
149
public static Fury createSecureFury(Set<String> allowedClasses);
150
public static ThreadSafeFury createSecureThreadSafeFury();
151
152
// Security-focused configuration
153
public static FuryBuilder secureBuilder();
154
public static FuryBuilder secureBuilder(ClassChecker classChecker);
155
}
156
```
157
158
## Exception Handling
159
160
### Security Exceptions
161
162
```java { .api }
163
public class InsecureException extends FuryException {
164
public InsecureException(String message);
165
public InsecureException(String message, Throwable cause);
166
167
// Factory methods for common scenarios
168
public static InsecureException classNotAllowed(String className);
169
public static InsecureException registrationRequired(String className);
170
public static InsecureException dangerousClass(String className);
171
}
172
173
public class ClassNotCompatibleException extends FuryException {
174
public ClassNotCompatibleException(String message);
175
public ClassNotCompatibleException(String message, Throwable cause);
176
177
// Compatibility validation
178
public static ClassNotCompatibleException schemaIncompatible(Class<?> clazz);
179
public static ClassNotCompatibleException versionMismatch(Class<?> clazz);
180
}
181
```
182
183
## Usage Examples
184
185
### Basic Security Configuration
186
187
```java
188
import org.apache.fury.Fury;
189
import org.apache.fury.config.FuryBuilder;
190
import org.apache.fury.resolver.AllowListChecker;
191
192
// Enable class registration requirement (recommended for production)
193
Fury secureFury = Fury.builder()
194
.requireClassRegistration(true) // Security: only registered classes allowed
195
.build();
196
197
// Register only trusted classes
198
secureFury.register(User.class);
199
secureFury.register(Order.class);
200
secureFury.register(String.class);
201
secureFury.register(Integer.class);
202
203
// Deserialization will fail for unregistered classes
204
try {
205
byte[] maliciousData = createMaliciousPayload();
206
Object result = secureFury.deserialize(maliciousData); // Throws InsecureException
207
} catch (InsecureException e) {
208
// Handle security violation
209
logger.warn("Attempted deserialization of unregistered class: " + e.getMessage());
210
}
211
```
212
213
### Allow-List Based Security
214
215
```java
216
import org.apache.fury.resolver.AllowListChecker;
217
import java.util.Set;
218
219
// Create allow-list of safe classes
220
Set<String> safeClasses = Set.of(
221
"com.myapp.User",
222
"com.myapp.Order",
223
"com.myapp.Product",
224
"java.lang.String",
225
"java.lang.Integer",
226
"java.lang.Long",
227
"java.util.ArrayList",
228
"java.util.HashMap"
229
);
230
231
// Create allow-list checker
232
AllowListChecker allowListChecker = new AllowListChecker(safeClasses);
233
234
// Configure Fury with allow-list
235
ClassResolver classResolver = new ClassResolver();
236
classResolver.setClassChecker(allowListChecker);
237
238
Fury fury = Fury.builder()
239
.requireClassRegistration(false) // Can be disabled with allow-list
240
.build();
241
242
// Set the class checker (internal API)
243
// Note: This requires access to Fury internals
244
```
245
246
### Package-Based Allow-List
247
248
```java
249
// Allow entire packages
250
Set<String> allowedClasses = Set.of("java.lang.String", "java.lang.Integer");
251
Set<String> allowedPackages = Set.of("com.myapp.model", "java.util");
252
253
AllowListChecker packageBasedChecker = new AllowListChecker(allowedClasses, allowedPackages);
254
255
// All classes in com.myapp.model.* and java.util.* are allowed
256
// Plus specifically allowed individual classes
257
```
258
259
### Custom Class Checker
260
261
```java
262
import org.apache.fury.resolver.ClassChecker;
263
import org.apache.fury.exception.InsecureException;
264
265
public class CustomSecurityChecker implements ClassChecker {
266
private final Set<String> trustedPackages;
267
private final Set<String> blockedClasses;
268
269
public CustomSecurityChecker() {
270
this.trustedPackages = Set.of("com.myapp", "java.lang", "java.util");
271
this.blockedClasses = Set.of(
272
"java.lang.Runtime",
273
"java.lang.Process",
274
"java.io.FileInputStream",
275
"java.io.FileOutputStream"
276
);
277
}
278
279
@Override
280
public void checkClass(ClassResolver classResolver, String className) {
281
// Block dangerous classes
282
if (blockedClasses.contains(className)) {
283
throw InsecureException.dangerousClass(className);
284
}
285
286
// Check if class is in trusted package
287
boolean inTrustedPackage = trustedPackages.stream()
288
.anyMatch(pkg -> className.startsWith(pkg + "."));
289
290
if (!inTrustedPackage) {
291
throw InsecureException.classNotAllowed(className);
292
}
293
294
// Additional custom validation logic
295
if (className.contains("$Proxy")) {
296
throw InsecureException.classNotAllowed("Dynamic proxies not allowed: " + className);
297
}
298
}
299
}
300
301
// Use custom checker
302
CustomSecurityChecker customChecker = new CustomSecurityChecker();
303
// Apply to Fury instance...
304
```
305
306
### Production Security Setup
307
308
```java
309
public class ProductionSecurityConfig {
310
311
public static Fury createProductionFury() {
312
// Create secure allow-list
313
Set<String> productionAllowList = createProductionAllowList();
314
AllowListChecker checker = new AllowListChecker(productionAllowList);
315
316
return Fury.builder()
317
.requireClassRegistration(true) // Mandatory registration
318
.suppressClassRegistrationWarnings(false) // Show security warnings
319
.withLanguage(Language.JAVA) // Java-only for security
320
.build();
321
}
322
323
private static Set<String> createProductionAllowList() {
324
Set<String> allowList = new HashSet<>();
325
326
// Application model classes
327
allowList.addAll(Set.of(
328
"com.myapp.model.User",
329
"com.myapp.model.Order",
330
"com.myapp.model.Product",
331
"com.myapp.dto.UserDTO",
332
"com.myapp.dto.OrderDTO"
333
));
334
335
// Java standard library safe classes
336
allowList.addAll(Set.of(
337
"java.lang.String",
338
"java.lang.Integer", "java.lang.Long", "java.lang.Double",
339
"java.lang.Boolean", "java.lang.Byte", "java.lang.Short",
340
"java.lang.Float", "java.lang.Character",
341
"java.math.BigDecimal", "java.math.BigInteger",
342
"java.time.LocalDate", "java.time.LocalDateTime", "java.time.Instant"
343
));
344
345
// Safe collection classes
346
allowList.addAll(Set.of(
347
"java.util.ArrayList", "java.util.LinkedList",
348
"java.util.HashMap", "java.util.LinkedHashMap", "java.util.TreeMap",
349
"java.util.HashSet", "java.util.LinkedHashSet", "java.util.TreeSet"
350
));
351
352
return allowList;
353
}
354
355
public static void registerProductionClasses(Fury fury) {
356
// Register all allowed classes explicitly
357
Set<String> allowList = createProductionAllowList();
358
359
for (String className : allowList) {
360
try {
361
Class<?> clazz = Class.forName(className);
362
fury.register(clazz);
363
} catch (ClassNotFoundException e) {
364
// Log warning but continue
365
logger.warn("Could not register class: " + className, e);
366
}
367
}
368
}
369
}
370
```
371
372
### Secure Deserialization Pattern
373
374
```java
375
public class SecureDeserializer {
376
private final Fury secureFury;
377
private final ClassChecker classChecker;
378
379
public SecureDeserializer(Set<String> allowedClasses) {
380
this.classChecker = new AllowListChecker(allowedClasses);
381
this.secureFury = Fury.builder()
382
.requireClassRegistration(true)
383
.build();
384
385
// Register allowed classes
386
for (String className : allowedClasses) {
387
try {
388
secureFury.register(Class.forName(className));
389
} catch (ClassNotFoundException e) {
390
throw new IllegalArgumentException("Cannot register class: " + className, e);
391
}
392
}
393
}
394
395
public <T> T deserializeSecurely(byte[] data, Class<T> expectedType) {
396
try {
397
Object result = secureFury.deserialize(data);
398
399
// Additional type validation
400
if (!expectedType.isInstance(result)) {
401
throw InsecureException.classNotAllowed(
402
"Unexpected type: expected " + expectedType.getName() +
403
", got " + result.getClass().getName());
404
}
405
406
return expectedType.cast(result);
407
} catch (InsecureException e) {
408
// Log security violation
409
logger.error("Security violation during deserialization", e);
410
throw e;
411
} catch (Exception e) {
412
// Log and wrap other exceptions
413
logger.error("Deserialization failed", e);
414
throw new FuryException("Secure deserialization failed", e);
415
}
416
}
417
}
418
```
419
420
### Environment-Based Security
421
422
```java
423
public class EnvironmentSecurityConfig {
424
425
public static Fury createFuryForEnvironment() {
426
String environment = System.getProperty("app.environment", "development");
427
428
switch (environment.toLowerCase()) {
429
case "production":
430
return createProductionFury();
431
case "staging":
432
return createStagingFury();
433
case "development":
434
return createDevelopmentFury();
435
default:
436
throw new IllegalArgumentException("Unknown environment: " + environment);
437
}
438
}
439
440
private static Fury createProductionFury() {
441
return Fury.builder()
442
.requireClassRegistration(true)
443
.suppressClassRegistrationWarnings(false)
444
.withLanguage(Language.JAVA)
445
.build();
446
}
447
448
private static Fury createStagingFury() {
449
return Fury.builder()
450
.requireClassRegistration(true)
451
.suppressClassRegistrationWarnings(true) // Less noise in staging
452
.withLanguage(Language.JAVA)
453
.build();
454
}
455
456
private static Fury createDevelopmentFury() {
457
return Fury.builder()
458
.requireClassRegistration(false) // More flexible for development
459
.suppressClassRegistrationWarnings(true)
460
.withLanguage(Language.JAVA)
461
.build();
462
}
463
}
464
```
465
466
## Security Best Practices
467
468
### Mandatory Security Measures
469
470
1. **Always Enable Class Registration in Production**:
471
```java
472
.requireClassRegistration(true)
473
```
474
475
2. **Use Allow-List Validation**:
476
```java
477
AllowListChecker checker = new AllowListChecker(trustedClasses);
478
```
479
480
3. **Validate Input Sources**:
481
```java
482
// Only deserialize from trusted sources
483
if (!isTrustedSource(dataSource)) {
484
throw new SecurityException("Untrusted data source");
485
}
486
```
487
488
4. **Register Minimal Class Set**:
489
```java
490
// Only register classes that are actually needed
491
fury.register(RequiredClass.class);
492
```
493
494
### Defense in Depth
495
496
1. **Network Security**: Use TLS/HTTPS for data transmission
497
2. **Input Validation**: Validate data before deserialization
498
3. **Monitoring**: Log deserialization attempts and failures
499
4. **Sandboxing**: Run deserialization in restricted environments
500
5. **Regular Updates**: Keep Fury library updated with security patches
501
502
### Common Security Pitfalls
503
504
1. **Disabling Class Registration**: Never disable in production
505
2. **Over-broad Allow-Lists**: Be specific about allowed classes
506
3. **Ignoring Security Warnings**: Address all security-related warnings
507
4. **Deserializing Untrusted Data**: Always validate data sources
508
5. **Missing Exception Handling**: Properly handle security exceptions
509
510
### Monitoring and Alerting
511
512
```java
513
public class SecurityMonitor {
514
private final Logger securityLogger = LoggerFactory.getLogger("SECURITY");
515
516
public void logSecurityViolation(String event, String details) {
517
securityLogger.error("SECURITY_VIOLATION: {} - {}", event, details);
518
519
// Send alert to security team
520
alertSecurityTeam(event, details);
521
}
522
523
public void logSuccessfulDeserialization(String className, String source) {
524
securityLogger.info("DESERIALIZATION_SUCCESS: {} from {}", className, source);
525
}
526
}
527
```