Security headers (CSP)
Run the Cariosan SDKs under a strict Content-Security-Policy — what Cariosan needs, a reference policy, nonce setup, and a safe rollout.
A strict Content-Security-Policy (CSP) is your best defence against cross-site scripting. Chat content is written by your users, so a single escaping mistake anywhere in your app should not be enough to run an attacker's script. @cariosan/client and @cariosan/react are built to run under a strict policy:
- No
evalornew Function, so you never need'unsafe-eval'. - No HTML string sinks: no
innerHTML, nodangerouslySetInnerHTML. Messages are rendered as text. - No inline styles in server-rendered markup. The kit's styles come from its CSS file; the few it sets at runtime go through the CSSOM, which
style-src 'self'allows. - No fonts,
data:images or CSSurl()of its own.
Every release is checked for eval-like code. The kit is tested in a real browser under the reference policy below: connecting, sending and receiving, reactions, typing, attachment upload and download, and long-history virtualisation.
What Cariosan needs from your policy
Add these origins to the directives you already have. Throughout this page, chat.example.com is your Cariosan server and files.example.com is your attachment storage (MinIO / S3; the origin of CARIOS_S3_PUBLIC_URL).
| Directive | Add | Why |
|---|---|---|
connect-src | https://chat.example.com wss://chat.example.com | REST and the WebSocket. List the wss:// origin explicitly: 'self' does not cover another host. |
connect-src | https://files.example.com | Attachment uploads are a PUT straight from the browser to the presigned storage URL. Without it, sending files fails. |
img-src | https://files.example.com blob: | Images shown from signed download URLs; blob: for files you fetch and display yourself. |
media-src | https://files.example.com blob: | Audio and video attachments. |
script-src | nothing | Works with a nonce and 'strict-dynamic'. Never add 'unsafe-eval' for Cariosan. |
style-src | nothing | Serve the kit's styles.css from your origin. If your bundler inlines it into a <style> tag, give that tag your nonce. |
Reference policy
script-src: only scripts carrying this response's nonce run, plus the scripts those load ('strict-dynamic'). Inline event handlers (onclick="…"),evaland injected<script>markup are blocked. Generate a fresh random nonce for every response and put it on your own<script>tags.'strict-dynamic'trusts your scripts' own additions. A script you trust can still add other scripts, so keep third-party scripts to the minimum. Inline handlers,evaland inline style attributes stay blocked regardless.style-src 'self'blocksstyle="…"attributes in HTML. If your framework or another library emits them, you will seestyle-src-attrreports. Moving those styles to classes is better than adding'unsafe-inline', which for styles is a much smaller risk than for scripts.frame-ancestors 'none'stops other sites from framing your chat (clickjacking). If you embed the chat in your own pages, useframe-ancestors 'self'instead.- Keep development and production policies apart. Development servers (Next.js, Vite) often need
'unsafe-eval'for hot reload. Allow it only in development.
Next.js (App Router)
Next.js applies the nonce to its own scripts when middleware sets the policy on the request. A nonce makes every page render dynamically, since each response needs a fresh one.
Rolling it out
- Send the policy as
Content-Security-Policy-Report-Only. Nothing is blocked; the browser only reports. Collect reports with aReporting-Endpointsheader (csp="https://your-app.example.com/csp-reports") or watch the browser console. - Use the app as your users do: sign in, open conversations, send files, react, leave a tab in the background and come back. Fix each report at its source. A report naming
chat.example.comorfiles.example.commeans an origin is missing from the table above. - Switch the header name to
Content-Security-Policy. Keep reporting on, so a regression shows up in your reports rather than as a broken page.
Other headers worth sending
Referrer-Policy: the SDK never puts tokens in URLs (the WebSocket carries its token in a header). Signed attachment URLs do carry a signature, and this policy keeps full URLs from leaking to other sites through theRefererheader.Permissions-Policy: drop a permission from the list if your app uses it, for examplecamera=(self)if users take photos to attach.Strict-Transport-Security: serve your app and Cariosan over HTTPS only. Browsers refusews://from anhttps://page anyway.
Was this page helpful?