25
25
26
26
package java .io ;
27
27
28
+ import java .io .ObjectInputFilter .Config ;
28
29
import java .io .ObjectStreamClass .WeakClassKey ;
29
30
import java .io .ObjectStreamClass .RecordSupport ;
30
31
import java .lang .System .Logger ;
66
67
* practices for defensive use of serial filters.
67
68
* </strong></p>
68
69
*
70
+ * <p>The key to disabling deserialization attacks is to prevent instances of
71
+ * arbitrary classes from being deserialized, thereby preventing the direct or
72
+ * indirect execution of their methods.
73
+ * {@link ObjectInputFilter} describes how to use filters and
74
+ * {@link ObjectInputFilter.Config} describes how to configure the filter and filter factory.
75
+ * Each stream has an optional deserialization filter
76
+ * to check the classes and resource limits during deserialization.
77
+ * The JVM-wide filter factory ensures that a filter can be set on every {@link ObjectInputStream}
78
+ * and every object read from the stream can be checked.
79
+ * The {@linkplain #ObjectInputStream() ObjectInputStream constructors} invoke the filter factory
80
+ * to select the initial filter which may be updated or replaced by {@link #setObjectInputFilter}.
81
+ * <p>
82
+ * If an ObjectInputStream has a filter, the {@link ObjectInputFilter} can check that
83
+ * the classes, array lengths, number of references in the stream, depth, and
84
+ * number of bytes consumed from the input stream are allowed and
85
+ * if not, can terminate deserialization.
86
+ *
69
87
* <p>ObjectOutputStream and ObjectInputStream can provide an application with
70
88
* persistent storage for graphs of objects when used with a FileOutputStream
71
89
* and FileInputStream respectively. ObjectInputStream is used to recover
188
206
* protected) or that there are get and set methods that can be used to restore
189
207
* the state.
190
208
*
191
- * <p>The contents of the stream can be filtered during deserialization.
192
- * If a {@linkplain #setObjectInputFilter(ObjectInputFilter) filter is set}
193
- * on an ObjectInputStream, the {@link ObjectInputFilter} can check that
194
- * the classes, array lengths, number of references in the stream, depth, and
195
- * number of bytes consumed from the input stream are allowed and
196
- * if not, can terminate deserialization.
197
- * A {@linkplain ObjectInputFilter.Config#setSerialFilter(ObjectInputFilter) system-wide filter}
198
- * can be configured that is applied to each {@code ObjectInputStream} unless replaced
199
- * using {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter}.
200
- *
201
209
* <p>Any exception that occurs while deserializing an object will be caught by
202
210
* the ObjectInputStream and abort the reading process.
203
211
*
@@ -347,14 +355,20 @@ private static class Logging {
347
355
*/
348
356
private ObjectInputFilter serialFilter ;
349
357
358
+ /**
359
+ * True if the stream-specific filter has been set; initially false.
360
+ */
361
+ private boolean streamFilterSet ;
362
+
350
363
/**
351
364
* Creates an ObjectInputStream that reads from the specified InputStream.
352
365
* A serialization stream header is read from the stream and verified.
353
366
* This constructor will block until the corresponding ObjectOutputStream
354
367
* has written and flushed the header.
355
368
*
356
- * <p>The serialization filter is initialized to the value of
357
- * {@linkplain ObjectInputFilter.Config#getSerialFilter() the system-wide filter}.
369
+ * <p>The constructor initializes the deserialization filter to the filter returned
370
+ * by invoking the {@link Config#getSerialFilterFactory()} with {@code null} for the current filter
371
+ * and the {@linkplain Config#getSerialFilter() static JVM-wide filter} for the requested filter.
358
372
*
359
373
* <p>If a security manager is installed, this constructor will check for
360
374
* the "enableSubclassImplementation" SerializablePermission when invoked
@@ -377,7 +391,8 @@ public ObjectInputStream(InputStream in) throws IOException {
377
391
bin = new BlockDataInputStream (in );
378
392
handles = new HandleTable (10 );
379
393
vlist = new ValidationList ();
380
- serialFilter = ObjectInputFilter .Config .getSerialFilter ();
394
+ streamFilterSet = false ;
395
+ serialFilter = Config .getSerialFilterFactorySingleton ().apply (null , Config .getSerialFilter ());
381
396
enableOverride = false ;
382
397
readStreamHeader ();
383
398
bin .setBlockDataMode (true );
@@ -388,8 +403,9 @@ public ObjectInputStream(InputStream in) throws IOException {
388
403
* ObjectInputStream to not have to allocate private data just used by this
389
404
* implementation of ObjectInputStream.
390
405
*
391
- * <p>The serialization filter is initialized to the value of
392
- * {@linkplain ObjectInputFilter.Config#getSerialFilter() the system-wide filter}.
406
+ * <p>The constructor initializes the deserialization filter to the filter returned
407
+ * by invoking the {@link Config#getSerialFilterFactory()} with {@code null} for the current filter
408
+ * and the {@linkplain Config#getSerialFilter() static JVM-wide filter} for the requested filter.
393
409
*
394
410
* <p>If there is a security manager installed, this method first calls the
395
411
* security manager's {@code checkPermission} method with the
@@ -412,7 +428,8 @@ protected ObjectInputStream() throws IOException, SecurityException {
412
428
bin = null ;
413
429
handles = null ;
414
430
vlist = null ;
415
- serialFilter = ObjectInputFilter .Config .getSerialFilter ();
431
+ streamFilterSet = false ;
432
+ serialFilter = Config .getSerialFilterFactorySingleton ().apply (null , Config .getSerialFilter ());
416
433
enableOverride = true ;
417
434
}
418
435
@@ -431,7 +448,7 @@ protected ObjectInputStream() throws IOException, SecurityException {
431
448
* priorities. The callbacks are registered by objects (in the readObject
432
449
* special methods) as they are individually restored.
433
450
*
434
- * <p>The serialization filter, when not {@code null}, is invoked for
451
+ * <p>The deserialization filter, when not {@code null}, is invoked for
435
452
* each object (regular or class) read to reconstruct the root object.
436
453
* See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details.
437
454
*
@@ -443,7 +460,7 @@ protected ObjectInputStream() throws IOException, SecurityException {
443
460
* @throws ClassNotFoundException Class of a serialized object cannot be
444
461
* found.
445
462
* @throws InvalidClassException Something is wrong with a class used by
446
- * serialization .
463
+ * deserialization .
447
464
* @throws StreamCorruptedException Control information in the
448
465
* stream is inconsistent.
449
466
* @throws OptionalDataException Primitive data was found in the
@@ -564,7 +581,7 @@ protected Object readObjectOverride()
564
581
* invocation of readObject or readUnshared on the ObjectInputStream,
565
582
* even if the underlying data stream has been manipulated.
566
583
*
567
- * <p>The serialization filter, when not {@code null}, is invoked for
584
+ * <p>The deserialization filter, when not {@code null}, is invoked for
568
585
* each object (regular or class) read to reconstruct the root object.
569
586
* See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details.
570
587
*
@@ -872,7 +889,7 @@ protected Class<?> resolveProxyClass(String[] interfaces)
872
889
* <p>When a subclass is replacing objects it must insure that the
873
890
* substituted object is compatible with every field where the reference
874
891
* will be stored. Objects whose type is not a subclass of the type of the
875
- * field or array element abort the serialization by raising an exception
892
+ * field or array element abort the deserialization by raising an exception
876
893
* and the object is not be stored.
877
894
*
878
895
* <p>This method is called only once when each object is first
@@ -1223,22 +1240,37 @@ public String readUTF() throws IOException {
1223
1240
}
1224
1241
1225
1242
/**
1226
- * Returns the serialization filter for this stream.
1227
- * The serialization filter is the most recent filter set in
1228
- * {@link #setObjectInputFilter setObjectInputFilter} or
1229
- * the initial system-wide filter from
1230
- * {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter }.
1243
+ * Returns the deserialization filter for this stream.
1244
+ * The filter is the result of invoking the
1245
+ * {@link Config#getSerialFilterFactory() JVM-wide filter factory}
1246
+ * either by the {@linkplain #ObjectInputStream() constructor} or the most recent invocation of
1247
+ * {@link #setObjectInputFilter setObjectInputFilter }.
1231
1248
*
1232
- * @return the serialization filter for the stream; may be null
1249
+ * @return the deserialization filter for the stream; may be null
1233
1250
* @since 9
1234
1251
*/
1235
1252
public final ObjectInputFilter getObjectInputFilter () {
1236
1253
return serialFilter ;
1237
1254
}
1238
1255
1239
1256
/**
1240
- * Set the serialization filter for the stream.
1241
- * The filter's {@link ObjectInputFilter#checkInput checkInput} method is called
1257
+ * Set the deserialization filter for the stream.
1258
+ *
1259
+ * The deserialization filter is set to the filter returned by invoking the
1260
+ * {@linkplain Config#getSerialFilterFactory() JVM-wide filter factory}
1261
+ * with the {@linkplain #getObjectInputFilter() current filter} and the {@code filter} parameter.
1262
+ * The current filter was set in the
1263
+ * {@linkplain #ObjectInputStream() ObjectInputStream constructors} by invoking the
1264
+ * {@linkplain Config#getSerialFilterFactory() JVM-wide filter factory} and may be {@code null}.
1265
+ * {@linkplain #setObjectInputFilter(ObjectInputFilter)} This method} can be called
1266
+ * once and only once before reading any objects from the stream;
1267
+ * for example, by calling {@link #readObject} or {@link #readUnshared}.
1268
+ *
1269
+ * <p>It is not permitted to replace a {@code non-null} filter with a {@code null} filter.
1270
+ * If the {@linkplain #getObjectInputFilter() current filter} is {@code non-null},
1271
+ * the value returned from the filter factory must be {@code non-null}.
1272
+ *
1273
+ * <p>The filter's {@link ObjectInputFilter#checkInput checkInput} method is called
1242
1274
* for each class and reference in the stream.
1243
1275
* The filter can check any or all of the class, the array length, the number
1244
1276
* of references, the depth of the graph, and the size of the input stream.
@@ -1247,21 +1279,14 @@ public final ObjectInputFilter getObjectInputFilter() {
1247
1279
* and the current object being deserialized.
1248
1280
* The number of references is the cumulative number of objects and references
1249
1281
* to objects already read from the stream including the current object being read.
1250
- * The filter is invoked only when reading objects from the stream and for
1251
- * not primitives.
1282
+ * The filter is invoked only when reading objects from the stream and not for
1283
+ * primitives.
1252
1284
* <p>
1253
1285
* If the filter returns {@link ObjectInputFilter.Status#REJECTED Status.REJECTED},
1254
1286
* {@code null} or throws a {@link RuntimeException},
1255
1287
* the active {@code readObject} or {@code readUnshared}
1256
1288
* throws {@link InvalidClassException}, otherwise deserialization
1257
1289
* continues uninterrupted.
1258
- * <p>
1259
- * The serialization filter is initialized to the value of
1260
- * {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter}
1261
- * when the {@code ObjectInputStream} is constructed and can be set
1262
- * to a custom filter only once.
1263
- * The filter must be set before reading any objects from the stream;
1264
- * for example, by calling {@link #readObject} or {@link #readUnshared}.
1265
1290
*
1266
1291
* @implSpec
1267
1292
* The filter, when not {@code null}, is invoked during {@link #readObject readObject}
@@ -1303,9 +1328,10 @@ public final ObjectInputFilter getObjectInputFilter() {
1303
1328
* @param filter the filter, may be null
1304
1329
* @throws SecurityException if there is security manager and the
1305
1330
* {@code SerializablePermission("serialFilter")} is not granted
1306
- * @throws IllegalStateException if the {@linkplain #getObjectInputFilter() current filter}
1307
- * is not {@code null} and is not the system-wide filter, or
1308
- * if an object has been read
1331
+ * @throws IllegalStateException if an object has been read,
1332
+ * if the filter factory returns {@code null} when the
1333
+ * {@linkplain #getObjectInputFilter() current filter} is non-null, or
1334
+ * if the filter has already been set.
1309
1335
* @since 9
1310
1336
*/
1311
1337
public final void setObjectInputFilter (ObjectInputFilter filter ) {
@@ -1314,20 +1340,25 @@ public final void setObjectInputFilter(ObjectInputFilter filter) {
1314
1340
if (sm != null ) {
1315
1341
sm .checkPermission (ObjectStreamConstants .SERIAL_FILTER_PERMISSION );
1316
1342
}
1317
- // Allow replacement of the system-wide filter if not already set
1318
- if (serialFilter != null &&
1319
- serialFilter != ObjectInputFilter .Config .getSerialFilter ()) {
1320
- throw new IllegalStateException ("filter can not be set more than once" );
1321
- }
1322
1343
if (totalObjectRefs > 0 && !Caches .SET_FILTER_AFTER_READ ) {
1323
1344
throw new IllegalStateException (
1324
1345
"filter can not be set after an object has been read" );
1325
1346
}
1326
- this .serialFilter = filter ;
1347
+ if (streamFilterSet ) {
1348
+ throw new IllegalStateException ("filter can not be set more than once" );
1349
+ }
1350
+ streamFilterSet = true ;
1351
+ // Delegate to serialFilterFactory to compute stream filter
1352
+ ObjectInputFilter next = Config .getSerialFilterFactory ()
1353
+ .apply (serialFilter , filter );
1354
+ if (serialFilter != null && next == null ) {
1355
+ throw new IllegalStateException ("filter can not be replaced with null filter" );
1356
+ }
1357
+ serialFilter = next ;
1327
1358
}
1328
1359
1329
1360
/**
1330
- * Invokes the serialization filter if non-null.
1361
+ * Invokes the deserialization filter if non-null.
1331
1362
*
1332
1363
* If the filter rejects or an exception is thrown, throws InvalidClassException.
1333
1364
*
0 commit comments