Security and production deployment
This guide describes the security-related configuration that operators should review when deploying a TerriaJS application. For the complete set of backend options, see the TerriaJS Server example configuration.
Production checklist
Before making a deployment public:
- Serve the application only over HTTPS, with TLS terminated by a reverse proxy, ingress controller, or load balancer in front of TerriaJS Server.
- Configure
hostName,trustedHosts, and ExpresstrustProxyfor the actual deployment topology. - Decide which sites may frame the application.
- Configure
parentMessageAllowedOriginsonly if a parent page or opener needs to control TerriaJS using cross-window messages. - Review Content Security Policy reports before changing CSP from report-only to enforcement mode.
- Replace Cesium's default ion token and restrict all browser-visible tokens.
- Store credentials that must remain private in server-side configuration, not in TerriaJS client configuration.
- Assign ownership and a rotation procedure to every token used by the deployment.
HTTPS and reverse proxies
TerriaJS Server supports serving HTTPS directly. For production deployments, however, TLS should normally terminate at a reverse proxy, ingress controller, or managed load balancer in front of TerriaJS Server.
Internet
│ HTTPS
▼
Reverse proxy, ingress, or load balancer
│ protected internal connection
▼
TerriaJS Server
The frontend should:
- redirect HTTP requests to HTTPS;
- forward requests to TerriaJS Server over a protected network;
- prevent direct public access to the TerriaJS Server port;
- replace, rather than blindly preserve, client-supplied forwarded headers;
- forward the original host and protocol correctly; and
- manage HSTS at the edge if HSTS is appropriate for the entire domain.
Configure the public hostname and any legitimate aliases in the TerriaJS Server configuration:
{
hostName: "maps.example.gov",
trustedHosts: ["maps.example.gov", "maps-alias.example.gov"],
trustProxy: 1
}
trustedHosts constrains HTTPS redirect targets and is used when validating
browser requests to state-changing TerriaJS Server endpoints.
trustProxy tells Express which reverse proxies and forwarded headers may be
trusted. Its correct value depends on the network topology; do not set it to
true without understanding which clients can connect directly to the
application. See the
Express documentation for running behind proxies,
including its warnings about X-Forwarded-For, X-Forwarded-Host, and
X-Forwarded-Proto.
These are the three forwarded headers currently used by Express/TerriaJS Server request handling:
X-Forwarded-Fordetermines the client IP;X-Forwarded-Hostdetermines the public hostname; andX-Forwarded-Protodetermines whether the original request used HTTPS.
The last trusted reverse proxy should replace these headers rather than accept
arbitrary values supplied by a client. The ordinary Host, Origin, and
Referer headers are also security-relevant and should be forwarded without
allowing clients to bypass validation performed at the edge.
Embedding TerriaJS in an iframe
Displaying TerriaJS in an iframe and controlling it with window.postMessage
are separate capabilities.
| Requirement | Configuration |
|---|---|
| Permit another site to display TerriaJS in an iframe | TerriaJS Server securityHeaders.cspFrameAncestors |
| Permit a cross-origin parent or opener to send start data | TerriaJS parentMessageAllowedOrigins |
| Trust public hostnames for server redirects and browser requests | TerriaJS Server hostName and trustedHosts |
Display-only iframe
If a parent site only displays TerriaJS and does not send it start data, omit
parentMessageAllowedOrigins or leave it empty. Only the framing policy needs
to permit the parent:
{
securityHeaders: {
contentSecurityPolicy: true,
cspFrameAncestors: ["'self'", "https://portal.example.gov"]
}
}
TerriaJS always permits messages from its own origin. A same-origin parent or
opener can therefore send messages without being added to
parentMessageAllowedOrigins.
Parent-controlled iframe or popup
If a cross-origin parent or opener needs to send start data, add its exact origin to the client configuration:
{
parameters: {
parentMessageAllowedOrigins: ["https://portal.example.gov"]
}
}
An interactive cross-origin iframe normally needs its parent origin in both
cspFrameAncestors and parentMessageAllowedOrigins.
Origins include the scheme and port. Do not add untrusted origins, and do not
use the opaque origin "null". TerriaJS validates both the sender's origin and
whether the sender is its parent or opener. The sending application must also
use an exact target origin:
iframe.contentWindow.postMessage(startData, "https://maps.example.gov");
When receiving messages from TerriaJS, the parent should validate both
event.origin and event.source. See
Controlling in an iframe or popup for a
complete example.
Content Security Policy
TerriaJS Server provides security response headers and a Content Security Policy (CSP) suitable as a starting point for TerriaJS and Cesium. The CSP is report-only by default. In report-only mode it logs violations but does not block content or prevent a disallowed site from framing the application.
Use the following rollout:
- Deploy with
cspReportOnly: true. - Exercise the application's maps, catalogs, search, analytics, and embedding workflows.
- Review reports received by
/csp-report. - Add only the sources genuinely required by the deployment.
- Set
cspReportOnly: falseand repeat the tests. - Continue monitoring reports after application and catalog changes.
For example:
{
securityHeaders: {
contentSecurityPolicy: true,
cspReportOnly: false,
cspFrameAncestors: ["'self'", "https://portal.example.gov"],
cspScriptSrc: []
}
}
Avoid using "*" for cspFrameAncestors unless unrestricted third-party
embedding is an explicit requirement.
Cesium ion access tokens
Set the deployment's token using cesiumIonAccessToken:
{
parameters: {
cesiumIonAccessToken: "<public-restricted-token>"
}
}
A token placed in client configuration is visible to users. It must be treated as a public token even when the provider applies URL restrictions.
For production:
- create a separate token for each application and environment;
- do not use Cesium's default or evaluation token;
- enable only the required public scopes, such as
assets:readandgeocode; - never grant browser tokens write or account-management scopes;
- restrict access to the assets used by the application;
- configure the most specific practical Allowed URLs;
- account for scheme, hostname, port, and path when configuring Allowed URLs;
- use a separate token for local development; and
- monitor usage for unexpected activity.
See the Cesium ion access-token guide for current scope, asset, URL restriction, monitoring, and rotation options.
If Cesium ion services are not required, disable their use explicitly:
{
parameters: {
useCesiumIonTerrain: false,
useCesiumIonBingImagery: false,
cesiumIonDisableDefaultToken: true
}
}
Cesium ion user login
If users can sign in to Cesium ion from the Add Data panel:
- configure the OAuth application with the exact redirect URI documented for
cesiumIonOAuth2ApplicationID; - keep
cesiumIonLoginTokenPersistenceset to"page"unless persistence is required; and - keep
cesiumIonAllowSharingAddedAssetsset tofalseunless the deployment intentionally supports sharing access to added assets.
sessionStorage and especially localStorage make a login token available for
longer and to more code executing on the same origin.
Other service tokens
Tokens and keys placed in config.json, initialization files, catalog
definitions, generated JavaScript, or browser requests are visible to users.
This commonly includes Bing Maps, Mapbox, ArcGIS, CARTO, and catalog-specific
Cesium ion tokens.
For every browser-visible token:
- grant the minimum permissions;
- configure URL or origin restrictions where supported;
- use separate tokens for production, staging, and development;
- monitor usage; and
- define a rotation and revocation procedure.
For example, see the Mapbox access-token guide for Mapbox scopes, URL restrictions, and rotation.
If a credential must remain private, do not put it in TerriaJS client configuration. Configure it on the server using TerriaJS Server proxy authentication. Proxy authentication prevents the credential itself from being sent to the browser, but a user who can access the configured proxy may still be able to use the upstream service through that credential. Use narrowly scoped, preferably read-only credentials and a narrow proxy allowlist.
Private upstream credentials with proxyAuth
TerriaJS Server can add a fixed authorization value when it proxies requests to
an upstream host. For example, a protected proxyauth.json file can provide a
Bearer token:
{
"data.example.gov": {
authorization: "Bearer <server-side-token>"
}
}
It can also provide one or more custom authentication headers:
{
"data.example.gov": {
headers: [
{
name: "X-API-Key",
value: "<server-side-token>"
}
]
}
}
Pass the protected file to TerriaJS Server:
terriajs-server \
--config-file serverconfig.json \
--proxy-auth /run/secrets/proxyauth.json
The same object may be supplied as proxyAuth in the main server
configuration. A separate file mounted from the deployment platform's secret
manager is preferable because it can be protected and rotated independently.
When configuring proxy authentication:
- never put
proxyauth.jsonunderwwwroot; - never commit real credentials to source control;
- make the object key match the target URL host, including a non-default port;
- also add the required target host to
allowProxyFor; - use a narrowly scoped, preferably read-only upstream credential; and
- restrict the upstream account to only the resources the application needs.
The authorization value may use schemes such as Basic or Bearer.
headers may be used for API keys or other fixed headers expected by the
upstream service.
Proxy authentication keeps the credential out of client configuration and browser requests to the upstream service. It is not user-specific authorization: any user who can make an allowed request through this TerriaJS proxy can potentially exercise the configured upstream credential. If access must depend on the identity or permissions of the current user, use a separate authenticated application proxy.
To rotate a proxy credential:
- Create a replacement with the same or narrower access.
- Update the protected file or secret.
- Restart or redeploy TerriaJS Server.
- Verify the affected catalog and data requests.
- Revoke the previous credential.
See the
TerriaJS Server documentation
for proxyAuth, appendParamToQueryString, allowProxyFor, and other backend
functionality.
Token ownership and rotation
Maintain an operational record for every token:
- service and account owner;
- application and environment;
- configuration location;
- whether it is browser-visible or private;
- permissions, allowed origins, and allowed assets;
- creation date and planned rotation date;
- usage-monitoring location; and
- revocation procedure.
Use a create-deploy-revoke rotation:
- Create a replacement token with the same or narrower access.
- Deploy the replacement without revoking the old token.
- Verify the production workflows that use it.
- Confirm that usage has moved to the replacement.
- Revoke the old token.
- Confirm that the old token no longer works.
- Update the operational record.
If exposure is suspected, rotate immediately, inspect service usage, and check source history, CI logs, deployment artifacts, and container layers for the exposed value. Rotation does not make a browser token secret; scope, asset, and origin restrictions remain necessary.