OWASP Top 10 — prescriptive rules with Java examples
By oculus
•
November 9, 2025
A01 – Broken Access Control
Rules
- Enforce authorization on the server for every request. Never rely on client claims.
- Deny-by-default routes; expose only through explicit configuration.
- Use method/URL‑level checks and object‑level checks (IDOR prevention).
- Prefer RBAC (roles/permissions) or ABAC (attributes). Centralize policy.
Java (Spring Security) – method level
@PreAuthorize("hasAuthority('INVOICE_READ') and #tenantId == authentication.details.tenantId")
public Invoice getInvoice(UUID tenantId, UUID invoiceId) { ... }
Object‑level (service‑side) enforcement
Invoice inv = repo.findById(id).orElseThrow(NotFound::new);
authorizer.assertCanView(authn, inv.getTenantId());
CWE links: 285, 639, 863, 276
A02 – Cryptographic Failures
Rules
- TLS 1.2+ everywhere; HSTS on internet origins.
- Use AES‑GCM for data at rest, PBKDF2/bcrypt/Argon2 for passwords.
- No ECB. Random IV/nonce per message; store alongside ciphertext.
- Keys in KMS/HSM, not env vars. Rotate.
- For JWT: RS256/ES256, set exp, aud, iss, and validate clock skew.
Java – AES‑GCM utility
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = SecureRandom.getInstanceStrong().generateSeed(12); // 96-bit
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
c.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ct = c.doFinal(plaintext);
Password hashing (Spring)
PasswordEncoder enc = new BCryptPasswordEncoder(12);
String hash = enc.encode(password);
CWE links: 327, 329, 330, 522, 916
A03 – Injection (SQL/NoSQL/OS/LDAP)
Rules
- Do parameterized queries only. Never build SQL via string concat.
- Validate and whitelist identifiers (columns, sort fields) if dynamic.
- For OS commands: avoid shell; use API or ProcessBuilder with fixed args.
Java – JDBC parameterization
try (PreparedStatement ps = con.prepareStatement(
"SELECT id, name FROM users WHERE email = ?")) {
ps.setString(1, email);
try (ResultSet rs = ps.executeQuery()) { /* ... */ }
}
Node (pg)
const {rows} = await db.query('SELECT * FROM x WHERE id = $1', [id]);
CWE links: 89, 78, 90, 943
A04 – Insecure Design
Rules
- Add rate limits for login, password reset, sensitive APIs.
- Enforce business invariants server‑side (e.g., cannot transfer negative amount).
- Use state machines for multi‑step workflows.
CWE links: 840, 841, 284
A05 – Security Misconfiguration
Rules
- Disable directory listing; set X-Content-Type-Options: nosniff.
- Minimal CORS; explicit origins; OPTIONS preflight only; do not wildcard with credentials.
- Remove default creds; pin versions; disable debug/actuator in prod.
Spring Security headers
http.headers(h -> h
.xssProtection(x -> x.block(true))
.contentTypeOptions(HeadersConfigurer.ContentTypeOptionsConfig::and)
.httpStrictTransportSecurity(hsts -> hsts.includeSubDomains(true).preload(true))
);
CWE links: 16, 933, 276, 732
A06 – Vulnerable & Outdated Components
Rules
- Lock to vetted versions (Maven/Gradle BOM). No +.
- SCA in CI; fail on known exploited vulns (CISA KEV/EPSS high).
- Ban unmaintained libs; replace with platform features.
Gradle example
dependencies { implementation platform('org.springframework.boot:spring-boot-dependencies:3.3.4') }
CWE links: 1104, 937
A07 – Identification & Authentication Failures
Rules
- Centralize auth (OIDC/OAuth2). No homegrown passwords.
- Sessions: secure, httpOnly cookies; rotate on privilege change; short JWT TTL.
- Account enumeration: same responses/timing for login/reset.
Spring Security – session
http.sessionManagement(s -> s.sessionFixation().migrateSession());
CWE links: 287, 384, 798
A08 – Software & Data Integrity Failures (incl. insecure deserialization)
Rules
- Verify signatures/hashes of artifacts; enforce protected branches; signed commits.
- Do not use Java native deserialization (ObjectInputStream). Use JSON with strict schemas.
- For Jackson: disable polymorphic typing; whitelist classes.
Java – disallow default typing
ObjectMapper m = new ObjectMapper();
m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// No enableDefaultTyping
CWE links: 502, 494, 353
A09 – Security Logging & Monitoring Failures
Rules
- Log security events (authN/Z, input validation failures, changes) with trace IDs.
- Never log secrets, full tokens, or PII. Mask.
- Ship logs to SIEM; create alerts for authz failures, rate limit trips, unexpected 5xx.
Logback mask example
<turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">...</turboFilter>
CWE links: 778, 532
A10 – Server‑Side Request Forgery (SSRF)
Rules
- Block server egress by default; use allowlist for outbound HTTP.
- Reject private/link‑local/loopback destinations after DNS resolve.
- Prefer back‑end APIs/SDKs over arbitrary URLs from users.
Java – allowlist + IP verification
URI uri = URI.create(inputUrl);
String host = uri.getHost();
if (!ALLOWLIST.contains(host)) throw new IllegalArgumentException("disallowed host");
InetAddress addr = InetAddress.getByName(host);
if (addr.isAnyLocalAddress() || addr.isLoopbackAddress() || addr.isLinkLocalAddress())
throw new SecurityException("private address");
CWE links: 918
