-
Notifications
You must be signed in to change notification settings - Fork 5.8k
8264859: Implement Context-Specific Deserialization Filters #3996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8264859: Implement Context-Specific Deserialization Filters #3996
Conversation
/csr |
👋 Welcome back rriggs! A progress list of the required criteria for merging this PR into |
@RogerRiggs this pull request will not be integrated until the CSR request JDK-8264860 for issue JDK-8264859 has been approved. |
@RogerRiggs The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
Webrevs
|
…d improve robustness of examples
…leanup the example
* used for each {@link ObjectInputStream} that does not set its own filter. | ||
* A utility class to set and get the JVM-wide deserialization filter factory, | ||
* the static JVM-wide filter, or to create a filter from a pattern string. | ||
* If a JVM-wide filter factory or static JVM-wide filter is set, it will determine the filter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This concerns me, "A JVM-wide filter factory". I was going to suggest that it should be "The ..", but then realised that there can only ever be one present at a time, but in the lifetime of a JVM there can be two (since getSerialFilterFactory if invoked before setSerialFilterFactory will subsequently return a different JVM-wide factory). Is this intentional? It would great if this could be "The ..", so that setXXX can only be invoked successfully if getXXX has not been. This may seen somewhat insignificant, but the fact that the JVM-wide factory can change make the model harder understand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is reasonable to require that the factory be set before any OIS is constructed.
Similar to the restriction that the filter on a stream cannot be changed after the first call to readObject.
So an IllegalStateException added to Config.setSerialFilterFactory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, great. So setSerialFilterFactory cannot be successfully invoked after any of i) getSerialFilterFactory, or ii) an OIS is constructed. I don't yet see this in the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec/code is forthcoming.
ii) is sufficient to prevent ambiguity in which filter is used throughout the Java runtime;
though it requires a bit of package-private plumbing.
i) is too limiting. It should be possible for an application to check whether a filter factory has been provided on the command line (by calling getSerialFilterFactory) and if not setting the factory itself. It may also want to install its own filter factory that delegates to the builtin factory without needed to re-implement the builtin behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i) is too limiting. It should be possible for an application to check whether a filter factory has been provided on the command line (by calling getSerialFilterFactory) and if not setting the factory itself. It may also want to install its own filter factory that delegates to the builtin factory without needed to re-implement the builtin behavior.
How is this supposed to work in practice? getSerialFilterFactory always returns a non-null factory, so how does one know whether or not the returned factory is the built-in factory, a factory set by the command line (or security property) ? (without resorting to implementation assumptions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the app does not recognize the filter factory (by its class or module) then try to replace it.
If it succeeds, the app is good to go.
If it throws an exception then your app is not likely to run and should fail.
Newer APIs generally make it possible to detect the state of things without throwing but in this case it would add extra API surface.
Some alternatives, check the module of the filter returned, if it is java.base then replacing it is ok to replace. (yes an implementation assumption).
An alternative would be to add a static method to return the builtin filter; creating it if has not already been created.
Add filter tracing support
To avoid spamming folks with individual emails, I marked as 'resolved' comments that didn't need a response other than 'ok, will fix'. Feel free to re-open them or re-comment if you don't see the fix. |
Clarified behavior of rejectUndecidedClass method. Example test added to check status returned from file.
Added @implSpec for default methods in OIF; Added restriction that the filter factory cannot be set after an ObjectInputStream has been created and applied the current filter factory
As default methods on OIF, their implementations were not concrete and not trustable
Rearranged the class javadoc of OIF to describe the parts of deserialization filtering, filters, composite filters, and the filter factory. And other review comment updates...
Updated java.security properties to include jdk.serialFilterFactory Added test cases to SerialFilterFactoryTest for java.security properties and enabling of the SecurityManager with existing policy permission files Corrected a test that OIS.setObjectInputFilter could not be called twice. Removed a Factory test that was not intended to be committed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More suggestions for ObjectInputFilter.java. I hope they're helpful.
Simplify the builtin filter factory implementation. Add atomic update to setting the filter factory. Clarify the description of OIS.setObjectInputFilter. Cleanup of the example code.
Added test for preventing disabling filter factory Test cleanup
public void current(ObjectInputFilter current) { | ||
this.current = current; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this current() used anywhere ?
public void next(ObjectInputFilter next) { | ||
this. next = next; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this next() used anywhere ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither is used, IntellJ was happy to create getters and setters... Can be removed.
* classes may be sufficient to manage the risk of deserializing unexpected classes. | ||
* <p>For an application composed from multiple modules or libraries, the structure | ||
* of the application can be used to identify the classes to be allowed or rejected | ||
* by each {@linkplain ObjectInputStream} in each context of the application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: should be {@link }
here.
* are {@code REJECTED}. Either the class is not {@code ALLOWED} or | ||
* if the class is an array and the base component type is not allowed, | ||
* otherwise the result is {@code UNDECIDED}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some part of the sentence missing here? I don't fully understand the "Either, or, otherwise" construct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an extra "if" at line 395.
it should be a more readable version of the list below implementing checkfilter.
If it does not aid in understanding, it can be removed.
* </code></pre> | ||
* | ||
* @param predicate a predicate to test a non-null Class, non-null | ||
* @param otherStatus a Status to use if the predicate is {@code false} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it be specified that the otherStatus
must also be non-null?
Is there a blanket statement somewhere for NPE, or are @throws NPE
clauses missing everywhere?
I'm asking because elsewhere in the JDK we usually specify that "unless otherwise specified, null parameters are not allowed and a NullPointerException will be thrown". But here it seems the opposite direction has been taken (which is fine), but the fact that NPE will be thrown if null
is passed for a parameter specified as non-null seems to be implicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the end of FIlterInputStream javadoc, there is the blanket statement.
* Unless otherwise noted, passing a {@code null} argument to a
* method in this interface and its nested classes will cause a
* {@link NullPointerException} to be thrown.```
including non-null on the @ param line reinforces the point
* @return an {@link ObjectInputFilter} that merges the status of the filter and another filter | ||
* @since 17 | ||
*/ | ||
static ObjectInputFilter merge(ObjectInputFilter filter, ObjectInputFilter anotherFilter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same remark about NPE. Should it @throws
or is there a blanket statement that makes it unnecessary?
configLog.log(INFO, | ||
"Creating deserialization filter from {0}", filterString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just double checking that you really want an INFO
message here. With the default logging configuration, INFO
messages will show up on the console.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is unchanged in the PR, though DEBUG might be more appropriate.
configLog.log(INFO, | ||
"Creating deserialization filter factory for {0}", factoryClassName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same remark about using INFO
level here.
* | ||
* @param filter the serialization filter to set as the system-wide filter; not null | ||
* @param filter the deserialization filter to set as the JVM-wide filter; not null | ||
* @throws SecurityException if there is security manager and the | ||
* {@code SerializablePermission("serialFilter")} is not granted | ||
* @throws IllegalStateException if the filter has already been set {@code non-null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @throws IllegalStateException if the filter has already been set {@code non-null}
Is there a typo/word missing ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non-null is unnecessary.
* or based on a {@linkplain Predicate predicate of a class} to | ||
* {@linkplain #allowFilter(Predicate, Status) allow} or | ||
* {@linkplain #rejectFilter(Predicate, Status) reject} classes. | ||
*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra '.' on this line
ensure effective filtering and compatibility with previous releases. Fixed a bug in allow/rejectFilter() Cleanup of error stages and messages related setting filter factory with Config.setSerialFilterFactory. Updated tests to match.
Added javadoc to describe throwing of ExceptionInInitializerError if the class named by system property jdk.serialFilterFactory is not valid. Added description of jdk.serialFilterFactory to java.security file.
* If the filter factory constructor is not invoked successfully, an {@link ExceptionInInitializerError} | ||
* is thrown. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you also say that later attempts to create an ObjectInputStream
or to call ObjectInputStream::setObjectInputFilter
will result in an IllegalStateException
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and setObjectInputFilter should throw ISE if the initialization from the system property has failed.
* If the filter factory constructor is not invoked successfully, an {@link ExceptionInInitializerError}
* is thrown and subsequent use of the filter factory for deserialization fails with
* {@link IllegalStateException}.
… jdk.serialFilterFactory fails
@RogerRiggs This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 178 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
/integrate |
@RogerRiggs Since your change was applied there have been 184 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 13d6180. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
JEP 415: Context-specific Deserialization Filters extends the deserialization filtering mechanisms with more flexible and customizable protections against malicious deserialization. See JEP 415: https://openjdk.java.net/jeps/415.
The
java.io.ObjectInputFilter
andjava.io.ObjectInputStream
classes are extended with additionalconfiguration mechanisms and filter utilities.
javadoc for
ObjectInputFilter
,ObjectInputFilter.Config
, andObjectInputStream
:http://cr.openjdk.java.net/~rriggs/filter-factory/java.base/java/io/ObjectInputFilter.html
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/3996/head:pull/3996
$ git checkout pull/3996
Update a local copy of the PR:
$ git checkout pull/3996
$ git pull https://git.openjdk.java.net/jdk pull/3996/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 3996
View PR using the GUI difftool:
$ git pr show -t 3996
Using diff file
Download this PR as a diff file:
https://git.openjdk.java.net/jdk/pull/3996.diff