Security en US - rocambille/start-express-react GitHub Wiki

This page describes the security mechanisms implemented in StartER: CSRF protection, XSS strategy, cookie management, authentication policy, and recommended best practices.

The goal is to provide a consistent, minimalist yet robust foundation, suitable for a fully decoupled and stateless React + Express API front-end architecture.

CSRF (Cross-Site Request Forgery)

In the StartER architecture:

  • The API is stateless,
  • The server does not maintain any back-end sessions,
  • StartER does not explicitly configure CORS. By default, no cross-site requests are allowed, because the server does not return any Access-Control-Allow-Origin.
  • The front end is served on the same domain as the back end: this is the only domain authorized to communicate with the API,
  • Cookies are set to SameSite=strict.

This already blocks the majority of classic CSRF attacks, since a third-party site can neither send strict cookies nor interact with the API.

StartER uses additional protection, especially for mutative requests (POST, PUT, PATCH, DELETE): the Client-Side Double-Submit pattern.

Tip

The pattern is particularly well explained in the FAQ of the csrf-csrf package, along with other answers regarding CSRF attacks, the necessity of implementing protections, etc.

The pattern is also mentioned, among other places, in the Symfony documentation.

To go deeper, we recommend reading the OWASP documentation.

Client-side token generation

The front end generates a CSRF token using the csrfToken() utility in src/react/components/utils:

  • The token is stored in a cookie __Host-x-csrf-token (see OWASP documentation about cookie prefixes),
  • It expires after 30 seconds,
  • but each use extends its expiration, replicating the behavior of a client-side session cookie,
  • No server storage is required.

This mechanism allows us to say "the CSRF session is active as long as there is activity," while having an explicit timeout to prevent expired tokens from being stored for too long.

Each mutative request includes a header containing the same value as the one stored in the cookie: this is the "double-submit".

X-CSRF-Token: <token>

Server-side verification

The server:

  1. intercepts POST/PUT/PATCH/DELETE requests,
  2. reads the __Host-x-csrf-token cookie,
  3. compares it with the x-csrf-token header,
  4. responds with a 401 status code if the header is missing or if there is an inconsistency between the header and the cookie.

The server remains entirely stateless: it simply compares two values ​​transmitted by the client, without maintaining a session or storing a token on the back end.

This approach is acceptable as long as:

  • The API is not exposed to third parties (no cross-site access allowed).

  • SameSite=strict eliminates the possibility of a third-party site sending the necessary cookies.

XSS protection

The core of XSS protection lies on the front end:

  • no insecure interpolation is performed in the DOM,
  • React prevents HTML injection by default,
  • StartER does not use dangerouslySetInnerHTML anywhere.

On the backend:

  • JSON responses are served with a Content-Type: application/json, which prevents them from being interpreted as executable HTML or JavaScript,
  • all data stored in the database is treated as opaque content on the frontend.

Additional tips

For projects that want to go further:

Starter doesn't enforce a CSP by default, but the front-end + API structure is well-suited to it.

Cookies, authentication, and integrity

StartER uses two main cookies:

  • __Host-auth: a signed JWT token containing the user's identity
  • __Host-x-csrf-token: an ephemeral CSRF token renewed client-side

Both are:

The __Host-auth cookie is set to HttpOnly, making it inaccessible to client-side JavaScript code. This is not the case for the CSRF token, which is written client-side.

Access tokens (JWT)

The JWT stored in a cookie does not represent a session but a signed attestation. Its lifespan must remain short to limit the window of opportunity for a compromised token.

The JWT is not encrypted, only signed. It contains data that is readable by the client, but protected against modification.

Key points:

  • The server does not store session state,
  • Revocation occurs via expiration or an explicit request to the server to delete the cookie (logout).

Limiting attack surfaces

No cross-site cookies

StartER does not serve any content from a third-party domain or a different subdomain.

This drastically reduces the risk of CSRF attacks and attacks related to requests originating from other websites.

Stateless API

The absence of a server-side session limits the risks of:

  • session fixation,
  • incomplete cleanup,
  • memory overflow when tracking long sessions.

No uncontrolled direct uploads

By default, StartER does not process file uploads.

If added, it must be filtered, stored outside the root document and validated (type, size...).

Recommended HTTP strategies

Deployments should consistently include:

  • Strict-Transport-Security
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Referrer-Policy: strict-origin-when-cross-origin

The production server (Caddy, Nginx, or other) is the appropriate location for these headers.

Error handling and security messages

Errors returned by the backend are intentionally concise:

  • 400 for malformed requests,
  • 401 for authentification issues,
  • 403 for authorization issues,
  • 500 for errors not handled by the server,
  • no detailed message explaining the nature of the failure (missing token, invalid signature, expired cookie, etc.).

The frontend then displays clear messages to the user without revealing sensitive information.

Additional recommendations

Logging

Server-side logging is recommended for 401/403 errors, which include:

  • Authentication failures,
  • Unauthenticated access attempts,
  • Forbidden access.

Mutation monitoring

Mutative requests (PUT, POST, PATCH, DELETE) should be systematically logged with:

  • timestamp,

  • authenticated user,

  • endpoint called.

JWT key rotation

A periodic mechanism (monthly/quarterly) is recommended to prevent:

  • the potential leakage of a key,
  • the persistence of old tokens signed with an obsolete key.
⚠️ **GitHub.com Fallback** ⚠️