Middleware: Security
Web security is one of those areas where “forgetting just one header” can expose your app to attacks — XSS, clickjacking, data leaks, you name it.
That’s why TriFrost includes first-class Security
middleware, inspired by tools like helmet.js, but tailored for the TriFrost ecosystem: runtime-agnostic, composable, and type-safe.
When you attach Security()
, it automatically sets a range of security-related HTTP headers with safe defaults.
📦 Import and Attach
import {Security} from '@trifrost/core';
...
.use(Security(options))
...
You can apply it:
- Globally on the app
- Per router
- Per route
⚙️ Available Options
contentSecurityPolicy
: TriFrostContentSecurityPolicyOptions|null
Configure allowed content sources (CSP).
default:null
crossOriginEmbedderPolicy
:'unsafe-none' | 'require-corp' | 'credentialless'
| null
Control embedding of cross-origin resources.
default:null
crossOriginOpenerPolicy
:'unsafe-none' | 'same-origin-allow-popups' | 'same-origin'
| null
Control cross-origin opener behavior.
default:'same-origin'
crossOriginResourcePolicy
:'same-site' | 'same-origin' | 'cross-origin'
| null
Restrict cross-origin resource sharing.
default:'same-site'
originAgentCluster
: boolean
Isolate browsing context in an origin agent cluster.
default:true
referrerPolicy
:'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url'
| ReffererPolicy[] | null
Control how much referrer info is sent.
default:'no-referrer'
strictTransportSecurity
: {maxAge:number, includeSubDomains?:boolean, preload?:boolean} | null
Force HTTPS and preload.
default:{maxAge:15552000, includeSubDomains:true}
xContentTypeOptions
:'nosniff'
| null
Prevent MIME type sniffing.
default:'nosniff'
xDnsPrefetchControl
:'on' | 'off'
| null
Control DNS prefetching.
default:'off'
xDownloadOptions
:'noopen'
| null
Prevent file downloads from opening automatically.
default:'noopen'
xFrameOptions
:'DENY' | 'SAMEORIGIN'
| null
Prevent clickjacking (frame/iframe).
default:'SAMEORIGIN'
xXssProtection
:'0' | '1' | 'block'
| string | null
Legacy XSS protection header.
default:'0'
Defaults Summarized
If you attach .use(Security())
without arguments, TriFrost sets these headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-site
Origin-Agent-Cluster: ?1
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Content-Type-Options: nosniff
X-DNS-Prefetch-Control: off
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
Everything else (like Content-Security-Policy, Cross-Origin-Embedder-Policy) is disabled by default and only set if explicitly configured.
Skipping Defaults
By default, TriFrost’s Security()
middleware applies safe built-in defaults, so you only need to override what you care about.
If you want to fully opt out of these defaults and only apply your own config, you can pass a second argument:
app.use(Security({
crossOriginOpenerPolicy: 'unsafe-none',
xFrameOptions: null,
}, {use_defaults: false})); // <- use_defaults = false
This will skip all internal defaults and apply only the values you provide.
For most cases, you should leave this flag as true
(the default), but it’s available for power users who want fine-grained control.
CSP Nonce Injection
When using a Content-Security-Policy
with "'nonce'"
as a placeholder, TriFrost will automatically generate and inject a secure, per-request nonce, simplifying CSP compliance without manual coordination.
Enable via CSP Config
app.use(Security({
contentSecurityPolicy: {
'script-src': ["'self'", "'nonce'"],
'style-src': ["'self'", "'nonce'"],
},
}));
At runtime, this produces:
Content-Security-Policy: script-src 'self' 'nonce-AbC123...'; style-src 'self' 'nonce-AbC123...'
Automatic JSX Integration
TriFrost’s JSX renderer fully supports nonce propagation:
- ✅
ctx.nonce
is automatically assigned and stable per request - ✅ You can access it via
ctx.nonce
ornonce()
from@trifrost/core
- ✅ No need to manually pass it to TriFrost’s
<Script>
and<Style>
components, they automatically apply the correct nonce.
Example (manual nonce use for custom tags):
import {nonce} from '@trifrost/core';
export function AnalyticsBeacon() {
return (
<script nonce={nonce()}>
{`navigator.sendBeacon('/beacon')`}
</script>
);
}
Example (TriFrost <Script>
and <Style>
primitives, no nonce needed):
import {Script, Style} from '@trifrost/core';
export function Layout() {
return (
<html>
<head>
<Style /> {/* Automatically gets nonce */}
</head>
<body>
...
<Script>{() => {
console.log('Safe script execution');
}}</Script> {/* Automatically gets nonce */}
</body>
</html>
);
}
👉 Learn more in JSX Basics
Inspecting the Nonce
You can log the nonce in any handler:
app.get('/debug', ctx => {
return ctx.text('Nonce is: ' + ctx.nonce);
});
Examples
Strict CSP for FrontEnd App
💡 Tip: Use
"'nonce'"
(as a string literal) in yourcontentSecurityPolicy
config, TriFrost will replace it automatically with a secure, per-request value.
app.use(Security({
contentSecurityPolicy: {
'default-src': ["'self'"],
'script-src': ["'self'", "'nonce'", 'https://cdn.example.com'],
'style-src': ["'self'", "'nonce'", 'https://fonts.googleapis.com'],
'img-src': ['data:', '*'],
'connect-src': ["'self'", 'https://api.example.com'],
'font-src': ['https://fonts.gstatic.com'],
},
}));
What’s happening here:
- Protects against inline scripts and unexpected external resources
- Allows known CDNs and APIs
- Script and style source get an automatic per-request nonce
- Still blocks everything else
Allow Framing Only on Trusted Origins
app.use(Security({
xFrameOptions: 'SAMEORIGIN', // default, but explicit here
}));
If you want to embed your app inside specific external sites:
app.use(Security({
contentSecurityPolicy: {
'frame-ancestors': ['https://trusted-partner.com'],
},
xFrameOptions: null, /* disable legacy header in favor of CSP */
}));
What’s happening here:
- CSP
frame-ancestors
gives finer control than X-Frame-Options - Avoids blocking your own integrations
API Server — No Framing, No Cross-Origin
app.use(Security({
xFrameOptions: 'DENY',
crossOriginResourcePolicy: 'same-origin',
crossOriginOpenerPolicy: 'same-origin',
}));
What’s happening here:
- Fully isolates the API
- Ensures no cross-origin embedding or data leaks
- Hardens server-side APIs
Public-Facing Static Site — Open Access, Locked Content Types
app.use(Security({
crossOriginResourcePolicy: 'cross-origin',
crossOriginOpenerPolicy: 'unsafe-none',
xContentTypeOptions: 'nosniff',
strictTransportSecurity: {
maxAge: 63072000, // 2 years
includeSubDomains: true,
preload: true,
},
}));
What’s happening here:
- Allows content to be served across origins (e.g., public CDNs)
- Still enforces MIME sniffing protections and HTTPS preload
Lock it down
import {Security} from '@trifrost/core';
app.use(Security({
contentSecurityPolicy: {
'default-src': ["'self'"],
'script-src': ["'self'", 'https://trusted.cdn.com'],
'style-src': ["'self'", 'https://trusted.cdn.com'],
'img-src': '*',
},
strictTransportSecurity: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
referrerPolicy: ['no-referrer', 'strict-origin-when-cross-origin'],
xFrameOptions: 'DENY',
}));
What’s happening here:
- Sets a strong CSP limiting scripts and styles to trusted sources.
- Enforces HTTPS with long max-age and preload (for HSTS).
- Denies all cross-origin frames.
- Blocks referrer data except on strict same-origin.
CSP Explained
contentSecurityPolicy: {
'default-src': "'self'",
'script-src': ["'self'", 'https://cdn.com'],
'style-src': "'self'",
'img-src': '*',
'connect-src': 'api.example.com',
'frame-ancestors': "'none'",
}
This controls which origins your browser is allowed to load content from — preventing injected scripts, rogue iframes, and more.
Values can be:
- A single string (
'self'
) - An array of strings (
["'self'", 'https://cdn.com']
)
Best Practices
- ✅ Always set Strict-Transport-Security if you serve over HTTPS.
- ✅ Use CSP to block unexpected content, especially scripts.
- ✅ Use
"'nonce'"
literal in your CSP config to trigger injection - ✅ Rely on
<Script>
and<Style>
wherever possible, they’ll handle nonce automatically - ✅ Use X-Frame-Options to prevent clickjacking.
- ⚠️ Avoid X-XSS-Protection unless supporting legacy IE.
- ✅ Review your cross-origin policies carefully.
- ✅ Log and monitor header behaviors in production.