A review checklist that finds real holes

Spring Security · lesson 31 of 31 · 6 min read

The handful of settings that account for most production security incidents.

Open this lesson in the learning hub

Key points

  • CSRF disabled on a cookie-authenticated app. Disabling it is correct for a token-authenticated API and dangerous for anything using session cookies. The same line is right in one place and a hole in the other.
  • CORS with credentials and a wildcard origin. The specification forbids it, but a permissive custom filter can reproduce the effect and expose authenticated endpoints to any site.
  • permitAll on the wrong matcher. A pattern such as /api/** intended to open one endpoint opens all of them. Confirm with an actual request, not by reading the pattern.
  • Actuator exposed. /actuator/env and /actuator/configprops can print configuration including credentials whose key name does not match the masking patterns.
  • Stack traces returned to clients. They leak framework versions, file paths and SQL. Boot masks these by default; a custom handler often unmasks them without meaning to.
  • Default or shared credentials anywhere - a seeded admin user, a fixed JWT signing key committed for local development, a service account shared between environments.

Example

# Probe the running application. This finds more than reading configuration.

# 1. Is anything reachable unauthenticated that should not be?
$ curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" \
    localhost:8080/api/orders \
    localhost:8080/api/admin/users \
    localhost:8080/actuator/env \
    localhost:8080/actuator/heapdump
#   Anything other than 401/403/404 on those last two is a finding.

# 2. Are the security headers present?
$ curl -sI localhost:8080/ | grep -iE \
    "strict-transport|x-content-type|x-frame|content-security"

# 3. Does CORS reflect an arbitrary origin back with credentials?
$ curl -sI -H "Origin: https://evil.example" localhost:8080/api/orders \
    | grep -i "access-control-allow"
#   allow-origin: https://evil.example  WITH allow-credentials: true
#   -> any site can make authenticated calls on a user behalf

# 4. Object-level authorization - the one automated scanners miss.
$ curl -s -H "Authorization: Bearer $USER_A_TOKEN" \
       localhost:8080/api/orders/<an order belonging to user B>
#   200 means broken object-level authorization. This is the big one.

---

# Sane defaults to assert in a test rather than trust:
#
#   csrf                 enabled for cookie auth, disabled only for token APIs
#   sessionCreationPolicy STATELESS for APIs
#   cors                 explicit origin list, never a wildcard with credentials
#   actuator             health + info public; everything else authenticated
#                        or on a separate management port
#   error responses      no stack trace, no SQL, no framework version
#   headers              HSTS, X-Content-Type-Options, frame-options, CSP

Probe the running application rather than reading config - and test object-level access with a second user, which is the check most often skipped.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.