Skip to content

Security

Optional per-deployment hardening: response headers, CORS and framing, Content-Security-Policy, rate limiting and the session cookie. Everything on this page is optional — left unset, Boards behaves exactly as it did before these settings existed.

Always-on response headers

These are sent on every response and need no configuration. Only Strict-Transport-Security is tunable (see HSTS).

Header Value Sent by Purpose
X-Content-Type-Options nosniff Both Stops the browser guessing a response's content type
Referrer-Policy strict-origin-when-cross-origin Both Limits how much of the Boards URL is leaked to third parties
Strict-Transport-Security max-age=15724800; includeSubDomains Both Forces HTTPS
Cross-Origin-Resource-Policy cross-origin Frontend Required so Boards can be framed by a portal on another origin
X-Powered-By (removed) API No server technology disclosure

Both = the API (core) and the frontend (webfront); the two are kept deliberately in step so a proxy in front of them never sees a header on one origin and not the other.

CORS & framing

By default the API accepts cross-origin calls from any origin (needed for embedding Boards in unknown parent sites). Set CORS_ALLOWED_ORIGINS to lock Boards down to a known set of origins instead.

Key Description Default
global.env.CORS_ALLOWED_ORIGINS
Optional: Comma-separated list of origins allowed to embed or cross-origin-call Boards. When set, the API reflects only these origins (Boards' own URL is added automatically) and the frontend restricts framing (CSP frame-ancestors) to them. * matches a single sub-domain label, e.g. https://*.example.com.
Blank = open (any origin).
(open)

A single origin can be set inline. For a longer list, use a YAML folded block (>-) with one origin per line, each ending in a comma:

global:
  env:
    # inline
    CORS_ALLOWED_ORIGINS: https://connections.company.com

    # list
    CORS_ALLOWED_ORIGINS: >-
      https://teams.microsoft.com,
      https://*.teams.microsoft.com,
      https://connections.company.com

HSTS

Strict-Transport-Security is always sent (default max-age=15724800; includeSubDomains). Use these settings to override the default:

Key Description Default
global.env.HSTS_MAX_AGE Optional: max-age in seconds. Non-numeric values fall back to the default. 15724800
global.env.HSTS_INCLUDE_SUBDOMAINS Optional: Set false to drop the includeSubDomains directive. true
global.env.HSTS_PRELOAD Optional: Set true to add the preload directive. false

For example, a two-year policy with preload:

global:
  env:
    HSTS_MAX_AGE: 31536000
    HSTS_INCLUDE_SUBDOMAINS: 'true'
    HSTS_PRELOAD: 'true'

Content-Security-Policy

No CSP is enforced by default (aside from frame-ancestors, set via CORS_ALLOWED_ORIGINS above). Validate a policy in report-only mode first, then enforce it.

Key Description Notes
webfront.env.CSP_REPORT_ONLY
Optional: A candidate policy sent as Content-Security-Policy-Report-Only. The browser reports violations without blocking anything — use it to see what a policy would break before enforcing.
webfront.env.CSP
Optional: An enforced Content-Security-Policy (same syntax as CSP_REPORT_ONLY).
Do not set frame-ancestors here — control framing via CORS_ALLOWED_ORIGINS only, or the two policies intersect and can block framers you intended to allow.

Use a YAML folded block (>-) with one directive per line, each ending in a semicolon. Start in report-only mode, then move the same value to CSP to enforce it.

What Boards needs from a policy

In the table below, API host is where API_GATEWAY points, and Connections host is the Connections deployment Boards is configured against. When either shares the Boards domain, 'self' already covers it and the entry can be dropped.

Directive Value Why Boards needs it
default-src 'self' Safe fallback for anything not listed; Boards itself is served from its own origin.
script-src 'unsafe-inline' Connections/Collab banner only — it injects inline scripts. Boards' own code does not need this; drop it on every other deployment.
'unsafe-eval' Connections/Collab banner only (its Dojo toolkit). Boards' own code does not need this; drop it on every other deployment.
Connections host Banner scripts (only when Connections is on a different origin).
style-src 'unsafe-inline' Always required, on every deployment: rich-text formatting (text colour, alignment) is rendered as inline styles. The Connections banner also injects styles. Do not drop this one alongside the script-src entries above.
Connections host Banner stylesheets.
img-src data: blob: Avatars and file thumbnails are fetched via the API and displayed from memory; placeholders and icons are inline data URLs.
https: User content (descriptions, comments) can reference images on any host. Replace with known hosts for a tighter policy — external images then show as broken.
connect-src API host + wss: API host All API calls, file/preview downloads and live updates (WebSocket).
Connections host Banner data requests and notifications.
media-src API host Video/audio file preview.
frame-src API host HTML file preview renders in an iframe served by the API.
form-action 'self' Restricts where forms may submit (prevents form-hijacking). Boards' own forms post to its own origin.
API host Form submits to the API (e.g. sign-in). Drop when the API shares the Boards origin.
Connections host Connections/Collab banner forms (e.g. its search box).
font-src data: Bundled fonts; some are inlined as data URLs.
Connections host Banner icon fonts.
https://static2.sharepointonline.com https://spoprod-a.akamaihd.net Microsoft 365 deployments only: the Microsoft 365 header loads its fonts and icon font from these Microsoft CDNs. Without them the header falls back to system fonts and icons render blank.
worker-src 'self' blob: Offline service worker and the PDF preview worker.
manifest-src 'self' The web-app manifest (install/pin metadata).
object-src 'none' Nothing uses plugins — lock it down.
base-uri 'self' Restricts the page <base> URL. This directive has no default-src fallback, so always set it explicitly.
frame-ancestors do not set Framing is controlled by CORS_ALLOWED_ORIGINS (see above), which emits its own frame-ancestors policy. Browsers enforce every policy — setting it here too means a parent must be allowed by both, which can block framers you meant to allow. It is also ignored in Report-Only mode, so it cannot be validated via CSP_REPORT_ONLY anyway.

Which deployments need 'unsafe-inline'?

Only the HCL Connections / Collab embedded SSO header forces script-src 'unsafe-inline' 'unsafe-eval'. Boards' own bundle runs under script-src 'self'.

Deployment script-src needs
HCL Connections or Collab with the embedded SSO header 'self' 'unsafe-inline' 'unsafe-eval' + the Connections host
HCL Connections without the header (different domain, no clientHeaderSSO) 'self'
Microsoft 365 'self' — the header is bundled with Boards. Its fonts come from Microsoft CDNs, so add those to font-src
HCL Domino 'self' + the Domino host — the navbar loads as a module from the provider, with no inline script
HCL DX 'self' — DX injects nothing into a standalone Boards page
Auth0 or standalone 'self'

style-src 'unsafe-inline' is not in that list

Every deployment needs style-src 'unsafe-inline', banner or not — Boards renders rich-text formatting (colour, alignment) as inline styles. Dropping it makes formatted card descriptions and comments render unstyled.

Same domain as Connections

The usual Component Pack shape — Boards and its API served from the Connections domain (e.g. connections.example.com/boards and connections.example.com/api-boards). Everything is same-origin, so 'self' covers the API and the Connections banner:

webfront:
  env:
    CSP_REPORT_ONLY: >-
      default-src 'self';
      script-src 'self' 'unsafe-inline' 'unsafe-eval';
      style-src 'self' 'unsafe-inline';
      img-src 'self' data: blob: https:;
      connect-src 'self';
      media-src 'self';
      frame-src 'self';
      form-action 'self';
      font-src 'self' data:;
      worker-src 'self' blob:;
      manifest-src 'self';
      object-src 'none';
      base-uri 'self';

Separate host names

Boards on its own host names (e.g. boards.example.com with the API at api-boards.example.com, Connections at connections.example.com). https://*.example.com covers the API and Connections hosts in one entry — replace it with the explicit host names for a tighter policy:

webfront:
  env:
    CSP_REPORT_ONLY: >-
      default-src 'self';
      script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.example.com;
      style-src 'self' 'unsafe-inline' https://*.example.com;
      img-src 'self' data: blob: https:;
      connect-src 'self' https://*.example.com wss://*.example.com;
      media-src 'self' https://api-boards.example.com;
      frame-src 'self' https://api-boards.example.com;
      form-action 'self' https://*.example.com;
      font-src 'self' data: https://*.example.com;
      worker-src 'self' blob:;
      manifest-src 'self';
      object-src 'none';
      base-uri 'self';

Standalone (no Connections banner)

Without the Connections/Collab banner — Microsoft 365, HCL Domino, HCL DX, Auth0 and standalone deployments — script-src needs no 'unsafe-inline' or 'unsafe-eval' at all, and the Connections host entries go away. Add the provider host where the deployment loads something from it (a Domino navbar module, the Microsoft 365 font CDNs). For a deployment at boards.example.com with the API at api-boards.example.com:

webfront:
  env:
    CSP_REPORT_ONLY: >-
      default-src 'self';
      script-src 'self';
      style-src 'self' 'unsafe-inline';
      img-src 'self' data: blob: https:;
      connect-src 'self' https://api-boards.example.com wss://api-boards.example.com;
      media-src 'self' https://api-boards.example.com;
      frame-src 'self' https://api-boards.example.com;
      form-action 'self' https://api-boards.example.com;
      font-src 'self' data:;
      worker-src 'self' blob:;
      manifest-src 'self';
      object-src 'none';
      base-uri 'self';

DX: no extra CSP entries. Embedding Boards inside DX pages is controlled by CORS_ALLOWED_ORIGINS (framing), not by this policy.

Whatever the shape, run report-only first — violations surface anything deployment-specific (integrations, customisations) before a policy blocks it.

Rate limiting

Off by default. Once any limit is set, the API rejects requests over it with 429 Too Many Requests. Counters are held in Redis, so the limit is shared across every core replica rather than applied per pod.

Requests are counted per signed-in user, falling back to the client IP for anonymous callers. Image and task-statistic requests are never counted.

Key Description Default
core.env.RATE_LIMIT_MAX_AUTH Optional: Requests per window allowed for a signed-in user. Unset = unlimited. (unlimited)
core.env.RATE_LIMIT_MAX_PUBLIC Optional: Requests per window allowed for an unauthenticated caller, counted per IP. Unset = unlimited. (unlimited)
core.env.RATE_LIMIT_WINDOW Optional: Length of the rolling window, as a duration string (30s, 10m, 1h). An unparseable value falls back to 15 minutes. 15m
core.env.RATE_LIMIT_MAX_BY_PREFIX Optional: JSON object overriding the limit for a named endpoint group. Currently inviteUser (sending user invitations), which is capped at 30 per window whenever rate limiting is on. {}
core:
  env:
    RATE_LIMIT_WINDOW: 10m
    RATE_LIMIT_MAX_AUTH: 2000
    RATE_LIMIT_MAX_PUBLIC: 800
    RATE_LIMIT_MAX_BY_PREFIX: '{"inviteUser": 10}'

Info

Start generously. A single board view issues many API calls, and an office behind one NAT address shares an IP for the public limit — a low RATE_LIMIT_MAX_PUBLIC can lock out a whole site before anyone signs in.

API requests authenticate with a bearer token. A cookie is used only where the browser cannot send an Authorization header — image and file requests — so it is issued with a narrow path (/file under the API context root) rather than for the whole site.

The defaults suit a deployment served over HTTPS, including when Boards is embedded in a portal on another domain. Change them only if you have a reason to.

Key Description Default
core.env.COOKIE_SECURE Optional: Set false to drop the Secure flag, allowing the cookie over plain HTTP. true
core.env.COOKIE_SAMESITE Optional: SameSite value — none, lax or strict. none is required when Boards is embedded in a page on another domain. none
core.env.COOKIE_HTTPONLY Optional: Set false to make the cookie readable by JavaScript. true
core.env.COOKIE_PATH Optional: Overrides the cookie path. By default the cookie is scoped to the API's /file routes. [API_URI_PATH]/file

SameSite=none requires Secure

Browsers reject a SameSite=none cookie that is not marked Secure. Setting COOKIE_SECURE: 'false' while leaving COOKIE_SAMESITE at none therefore breaks image and file loading. On a plain-HTTP deployment set both — COOKIE_SECURE: 'false' and COOKIE_SAMESITE: 'lax' — and accept that Boards can no longer be embedded cross-domain.