Skip to content

Harden JDBCJobStore deserialization: centralize getObjectFromBlob and support a JEP-290 ObjectInputFilter #1495

Description

@Nexory

Summary

JDBCJobStore deserializes the job-data-map, trigger and calendar BLOBs it reads back from the database with a raw new ObjectInputStream(...).readObject(), with no look-ahead deserialization filter. The same unfiltered pattern is duplicated across the base delegate and every database-specific delegate, so there is currently no single place where a filter could be enforced (CWE-502 / JEP-290 gap).

The read sites (all on the current main):

  • StdJDBCDelegate.getObjectFromBlob(), the base implementation, reached for COL_JOB_DATAMAP (selectJobDetail), COL_BLOB (selectTrigger) and COL_CALENDAR (selectCalendar).
  • The per-dialect overrides that duplicate the same new ObjectInputStream(binaryInput).readObject(): PostgreSQLDelegate, MSSQLDelegate, OracleDelegate, HSQLDBDelegate, SybaseDelegate, WebLogicDelegate, PointbaseDelegate, CUBRIDDelegate, CacheDelegate, GaussDBDelegate (the last added recently via Support Gauss DB #1355, i.e. new delegates keep copying the pattern).

Why

Java object deserialization of data that may be influenced by an attacker is the classic gadget-chain risk. For the JDBC job store this is defense-in-depth (it presumes influence over the stored BLOBs, e.g. via a shared/compromised job-store database), but there is currently no way to constrain it at all short of a JVM-wide jdk.serialFilter. Since Java 9 (ObjectInputFilter, this project targets Java 11) a per-store filter is a standard, low-cost hardening, and having one central choke point is the prerequisite for it.

Proposal

  1. Centralize the deserialization into a single protected helper, StdJDBCDelegate.readObjectFromBinaryStream(InputStream), that every delegate calls instead of duplicating new ObjectInputStream(...).readObject(). The dialect delegates keep their own dialect-specific stream-fetching; only the readObject step is shared.
  2. Support an optional ObjectInputFilter on the delegate, configured through the existing delegate init-string as objectInputFilter=<JEP-290 pattern> (e.g. org.quartz.jobStore.driverDelegateInitString = objectInputFilter=maxdepth=20;java.**;org.quartz.**;!*). When unset, behavior is identical to today.

This also gives a natural home for the class-loading fix proposed in #1153 (ClassLoaderObjectInputStream), which touched exactly these delegates but was closed only for procedural reasons (ownership change / re-signing); both concerns can live behind the one helper.

Open question (the reason I'm opening an issue rather than just a PR)

What should the default be? Because a job-data map serialized with useProperties=false can legitimately contain arbitrary application classes, a strict default allow-list would break existing users, so the draft below defaults the filter to null (no change) and leaves it opt-in. Options, happy to implement whichever you prefer:

  • (a) keep it opt-in (draft as-is), zero behavior change;
  • (b) apply a conservative, non-breaking resource-limit default (e.g. a generous maxdepth/maxrefs) that never rejects legitimate object graphs but stops depth/reference bombs, still fully overridable;
  • (c) a documented default reject-list of the well-known gadget packages.

Draft PR

I've opened a draft PR implementing option (a): #1494. It compiles on the Java 11 toolchain and StdJDBCDelegateTest passes (7/7), including two added tests proving (1) no filter = unchanged round-trip and (2) a configured filter rejects a non-allow-listed class while still deserializing legitimate data. Commits are DCO signed-off. I'll adjust the default policy to whatever the team prefers before it leaves draft.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

is:enhancementEnhancement to an existing feature

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions