Join our Newsletter — 33% off our NHI Course
Home› Guides› Oracle Java Secure Coding Guidelines Guide: Applying the…
Guide Application Security

Oracle Java Secure Coding Guidelines Guide: Applying the Java SE Rules in Real Code

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 27 October 2026 13 min read
On this page

Oracle's Secure Coding Guidelines for Java SE are the canonical rule set for Java code, yet most teams have never read them and many of their rules were written for a sandbox that no longer exists. The document, now at version 11.0 (last updated June 2025), groups more than 70 numbered guidelines into ten sections, from Fundamentals to Access Control, and covers problems that frameworks do not solve for you: deserialisation filtering, defensive copying of mutable state, integer overflow in limit checks, XML parser hardening and native code wrappers. This guide explains which sections matter most for server-side Java in 2026, how to turn them into code review rules and static analysis, and which Access Control guidance became legacy when the Security Manager was permanently disabled in Java 24. It is for Java developers, reviewers and application security engineers. Spring-specific controls are in the Java and Spring Security Guide.

Key takeaways

  • Use the guideline IDs as a shared vocabulary. IDs such as SERIAL-6, MUTABLE-3 and INJECT-5 make code review comments, SAST rule mappings and training precise and checkable.
  • Serialisation is still the highest-impact section. SERIAL-6 "Filter untrusted serial data" calls an allowlist of safe classes "the most secure approach", and JDK 17 added context-specific filters through a JVM-wide filter factory (JEP 415).
  • Mutability bugs are security bugs. MUTABLE-2 and MUTABLE-3 require copies of mutable outputs and inputs; without them, a caller can change a value after it has been validated (time of check to time of use).
  • Section 9 is largely legacy. The guidelines state the Security Manager was deprecated in Java 17 and permanently disabled in Java 24, so guidelines 9-1 to 9-15 apply only to earlier releases; isolation now comes from processes, containers and modules.
  • Several rules defend against denial of service, not just compromise. DOS-1, DOS-3 and DOS-5 cover zip bombs, XML entity expansion, overflow in size checks and hash-collision attacks on untrusted map keys.

What the guidelines are and how they are organised

The Oracle document is a set of principles with concrete Java examples, maintained alongside the JDK. Each guideline has a section number and a mnemonic ID (for example 6-3 / MUTABLE-3). The structure is:

SectionID prefixCoversRelevance for server-side Java today
0 FundamentalsFUNDAMENTALS-0 to 8Design principles: obviously no flaws, restrict privileges, trust boundaries, encapsulation, securing third-party codeHigh: the basis for design review
1 Denial of ServiceDOS-1 to 5Disproportionate resource use, resource release, overflow in limit checks, exception handling, untrusted hash keysHigh for any service parsing client input
2 Confidential InformationCONFIDENTIAL-1 to 3Sensitive data in exceptions, logs and memoryHigh
3 Injection and InclusionINJECT-1 to 9Formatting, dynamic SQL, HTML and XML generation, command lines, XML inclusion, BMP files, Swing HTML, interpreting untrusted code, floating point valuesHigh
4 Accessibility and ExtensibilityEXTEND-1, 4 to 6Limiting accessibility and extensibility, ClassLoader exposure, superclass effectsMedium to high for libraries
5 Input ValidationINPUT-1 to 4Validating inputs and outputs of untrusted objects, wrapping native methodsHigh
6 MutabilityMUTABLE-1 to 12Immutability, defensive copies, public statics, modifiable collectionsHigh for libraries and shared models
7 Object ConstructionOBJECT-1, 4, 5Constructor exposure, overridable calls in constructors, cloningMedium
8 Serialisation and DeserialisationSERIAL-1 to 4, 6Avoiding serialisation for sensitive classes, guarding data, deserialisation as construction, filteringVery high
9 Access ControlACCESS-1 to 15, plus 9-16 to 9-20Permission checks, doPrivileged, caller-sensitive APIs, isolation of unrelated codeMostly legacy after Java 24, except module and class loader isolation

Some IDs (EXTEND-2, EXTEND-3, OBJECT-2, OBJECT-3, SERIAL-5) were moved into section 9 as guidelines 9-16 to 9-20, so older references to those IDs now point elsewhere. An appendix, "Defensive use of the JNI", covers native code.

Serialisation and deserialisation (section 8)

Java native serialisation reconstructs object graphs from bytes, running code in readObject and related methods of any class on the classpath that the stream names. SERIAL-3 tells developers to "view deserialization the same as object construction", meaning every invariant and security check in a constructor must also run on deserialisation (SERIAL-4). SERIAL-1 says to avoid serialisation for security-sensitive classes, and SERIAL-2 to guard sensitive data in serialised form.

Filtering untrusted serial data (SERIAL-6)

  • Allowlist, not blocklist: the guidelines state an allowlist of safe classes that rejects everything else is the most secure approach; reject lists must also cover subclasses and fall behind new gadget chains.
  • Inventory per context: list what each component or library serialises and deserialises, and build a filter for each context.
  • JVM-wide factory (Java 17 and later): JEP 415 adds jdk.serialFilterFactory and ObjectInputFilter.Config.setSerialFilterFactory(), so a factory can choose a filter per stream. A process-wide pattern can be set with jdk.serialFilter, for example example.*;java.base/*;!*, where !* rejects everything not allowed.
  • Older JDKs: filter support was added in CPU releases 8u121, 7u131 and 6u141; RMI supports filters for remote invocations.
  • Better still, avoid it: for data crossing a trust boundary, use a data format such as JSON with explicit schema binding and no polymorphic type information taken from the input.

INJECT-8 adds the related risk of "interpreting untrusted code": javax.script, XSLT extensions, XMLDecoder-style bean persistence, RMI and LDAP codebase loading and JNDI lookups. It recommends keeping java.rmi.server.useCodebaseOnly and com.sun.jndi.ldap.object.trustURLCodebase at their safe settings, setting the jdk.jndi.*.factoriesFilter properties and applying deserialisation filters to JNDI. Deeper patterns are in the Secure Deserialisation Guide.

Injection and XML handling (section 3)

GuidelineRuleConcrete practice
INJECT-2 Avoid dynamic SQLBind, do not concatenatePreparedStatement or CallableStatement with setString() and similar; prefer a higher-level data library. See the SQL Injection Prevention Guide.
INJECT-3 XML and HTML generation requires careEncode untrusted data on outputAllowlist safe characters, use a tested library to build markup, take extra care in JSP
INJECT-4 Avoid untrusted data on the command lineKeep data out of argument stringsPass data Base64-encoded, through a temporary file or an inherited channel
INJECT-5 Restrict XML inclusionMost restrictive parser configurationDisallow external entities or disable DTDs; reduce privileges of parsing code
INJECT-8 Interpreting untrusted codeSandbox in a separate processAvoid script engines and XSLT on untrusted input
INJECT-9 Exceptional floating point valuesReject NaN and infinity where they break logicCheck Double.isNaN and isInfinite on untrusted numbers

XML parser settings for INJECT-5 and DOS-1

The OWASP XML External Entity Prevention Cheat Sheet gives the matching JAXP settings. For DocumentBuilderFactory, set http://apache.org/xml/features/disallow-doctype-decl to true; if DTDs are needed, set external-general-entities, external-parameter-entities and load-external-dtd to false together. For StAX XMLInputFactory, set SUPPORT_DTD and IS_SUPPORTING_EXTERNAL_ENTITIES to false, since their defaults are not safe. On JDK 22 and later the built-in parsers honour jdk.xml.dtd.support (deny or ignore). Use newDefaultInstance() to get the built-in parser predictably, and fail closed if a setting is rejected. DOS-1 adds XMLConstants.FEATURE_SECURE_PROCESSING and parser limits against entity expansion. See the XXE Guide.

Denial of service and confidential information (sections 1 and 2)

  • DOS-1 disproportionate resources: check image dimensions and input sizes before loading; limit decompressed size rather than trusting compressed size or metadata (zip bombs); validate untrusted lengths and offsets before allocating.
  • DOS-3 overflow in limit checks: write current > max - extra rather than current + extra > max; use Math.addExact and multiplyExact (Java 8 and later), and Objects.checkIndex, checkFromIndexSize and checkFromToIndex (Java 9 and later). Remember -Integer.MIN_VALUE is still negative.
  • DOS-5 untrusted hash keys: attacker-chosen keys in HashMap or HashSet can force collisions; prefer Comparable keys and bound collection sizes.
  • CONFIDENTIAL-1 exceptions: catch internal exceptions and rethrow only known information; even an exception type such as FileNotFoundException reveals whether a file exists; never show stack traces to users.
  • CONFIDENTIAL-2 logging: keep passwords and identifiers such as national insurance or social security numbers out of logs, including logs from low-level libraries that log their input. The same applies to API keys and tokens, which are non-human identity credentials.
  • CONFIDENTIAL-3 memory: consider purging highly sensitive data from memory after use, which is why APIs take passwords as char[] rather than String.

Mutability, extensibility and object construction (sections 4, 6 and 7)

These sections matter most for libraries, SDKs and shared domain models, where the caller is not trusted to leave objects alone.

GuidelineFailure it preventsPractice
MUTABLE-1 Prefer immutability for value typesShared state changed unexpectedlyRecords and final fields for value objects
MUTABLE-2 Create copies of mutable output valuesCallers change internal state through a returned referenceReturn new Date(date.getTime()) or an unmodifiable view
MUTABLE-3 Create safe copies of mutable and subclassable input valuesTime-of-check to time-of-use: value changed after validationCopy first, then validate and use; deep-copy collections of mutable elements; do not trust clone() on non-final input types
MUTABLE-9 to 12 Public statics and collectionsGlobal state tamperingPublic static fields final, values truly constant, no exposed modifiable collections
EXTEND-1 Limit accessibilityInternal APIs used as attack surfaceLeast visibility; module exports only for public API
EXTEND-5 Limit extensibilityMalicious subclasses override security behaviourfinal classes or sealed hierarchies (Java 15 and later) with permits
OBJECT-4 No overridable calls in constructorsSubclass observes a partially built thisApplies equally to clone, readObject and readObjectNoData
OBJECT-5 Defend against cloning of non-final classesClones bypass constructor checksMake classes final or override clone safely

Native code and the JNI (INPUT-3 and Appendix A)

Native code runs outside the JVM's memory safety. INPUT-3 says to declare native methods private and expose them only through a public Java wrapper that validates arguments and clones mutable arrays, with overflow-safe bounds checks such as offset < 0 || len < 0 || offset > data.length - len. The guidelines' appendix, "Defensive use of the JNI", goes further; review it before approving any new native library, and treat third-party native libraries as part of your software supply chain under FUNDAMENTALS-8 "Secure third-party code".

Access control after the Security Manager (section 9)

The guidelines now say the Security Manager was deprecated in Java 17 and permanently disabled in Java 24, and that guidelines 9-1 to 9-15 (for example ACCESS-3 on doPrivileged and ACCESS-8 to ACCESS-11 on caller-sensitive APIs) apply only to earlier releases. What this means in practice:

  • Do not design new sandboxes on the Security Manager: run untrusted code in a separate process or container with operating system controls, which INJECT-8 also recommends.
  • Keep module and class loader isolation: guidance on not exposing ClassLoader instances (EXTEND-4) and isolating unrelated code still applies on current JDKs.
  • Legacy estates: applications still on Java 8, 11 or 17 that rely on a policy file need the full section 9 review, and a migration plan before upgrading to Java 24 or later.

Putting the guidelines into practice

  1. Select the rules that apply: for a typical service, sections 0, 1, 2, 3, 5 and 8 in full; sections 4, 6 and 7 for shared libraries; section 9 only for legacy runtimes.
  2. Map rules to tooling: map each selected guideline ID to a SAST rule in your analyser (most Java analysers cover dynamic SQL, XXE parser settings, unsafe deserialisation and command execution), and mark the ones that need manual review, such as MUTABLE-3 and SERIAL-4.
  3. Set JVM-level defaults first: a jdk.serialFilter or filter factory in report-and-tune mode, hardened XML factory helpers in a shared library, and JNDI factory filters. Roll out to non-production, review rejected classes in logs, then enforce.
  4. Put the IDs in the review checklist and pull request template, so reviewers cite "SERIAL-6" rather than "looks unsafe".
  5. Train on the high-impact sections using your own past findings as examples.
  6. Roll back safely: serial filters and parser settings are configuration; keep previous values so a rejected legitimate class can be added to the allowlist quickly instead of disabling the filter.

Small teams can start with steps 3 and 4. Large organisations should own the shared hardened helpers (XML factories, filter factory, safe process launching) centrally, as FUNDAMENTALS-2 "Avoid duplication" suggests, so fixes land once. For broader practice see the Secure Coding Guide.

Detection and measurement

  • Runtime: log deserialisation filter rejections (the filter's status) and alert on rejected classes from external inputs, which often indicate probing.
  • Static: SAST findings for ObjectInputStream without a filter, Statement.execute with concatenation, unsafe XML factories and Runtime.exec with untrusted strings.
  • Dependencies: track libraries known for gadget chains and older JDKs without filter support.
MetricTarget
Services running with a deserialisation filter or factory configured100% of services that deserialise
XML parsers created outside the hardened helperZero
Dynamic SQL findings open beyond SLAZero
Selected guideline IDs mapped to an automated rule or review step100%
Applications relying on the Security Manager without a migration planZero

Common failure patterns

  • Blocklist deserialisation filters: new gadget classes and subclasses slip through; SERIAL-6 prefers allowlists.
  • Hardening one XML factory but not the others: DOM is fixed while StAX or TransformerFactory remain at unsafe defaults.
  • Validate then copy: checking a mutable argument and then storing it lets the caller change it afterwards; MUTABLE-3 requires copy then validate.
  • Stack traces in API responses: framework default error pages leak internals, against CONFIDENTIAL-1.
  • Size checks that overflow: offset + len > length wraps negative and passes, against DOS-3.
  • Relying on a Security Manager policy on new JDKs: it is disabled from Java 24, so the protection silently disappears on upgrade.

Framework mapping

FrameworkReferenceGuideline sections
NIST SP 800-53 Rev. 5SA-8 Security and Privacy Engineering Principles0 Fundamentals
NIST SP 800-53 Rev. 5SI-10 Information Input Validation3 Injection, 5 Input Validation, 8 Serialisation
NIST SP 800-53 Rev. 5SI-11 Error Handling2 Confidential Information
NIST SP 800-53 Rev. 5SC-39 Process IsolationINJECT-8, section 9 replacement
NIST SP 800-53 Rev. 5SA-11 Developer Testing; SA-15 Development Process, Standards, and ToolsSAST mapping, review checklist
NIST CSF 2.0PR.PS-06Secure development practice across the SDLC

Practitioner checklist

  • Read sections 0, 3 and 8 of the Oracle guidelines with your lead developers and agree which IDs you adopt.
  • Configure a deserialisation allowlist (jdk.serialFilter or a JEP 415 filter factory) for every service that uses Java serialisation, or remove serialisation.
  • Ship hardened XML factory helpers covering DOM, SAX, StAX and transformers, and block direct factory use in review.
  • Use PreparedStatement or a data library for all SQL; no concatenation.
  • Apply copy-then-validate to mutable inputs and return copies of mutable internals in shared libraries.
  • Use overflow-safe arithmetic (Math.addExact, Objects.checkFromIndexSize) in size and offset checks.
  • Sanitise exceptions and keep secrets, tokens and personal data out of logs.
  • Wrap native methods and review new JNI libraries against the appendix.
  • Plan migration away from any Security Manager dependency before moving to Java 24 or later.

Standards and references

Related NHI Mgmt Group resources: Java and Spring Security Guide · Secure Deserialisation Guide · XXE Guide · Secure Coding Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 27 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org