Skip to content

HTTP security response headers

Every response Myra AI Workspace serves to a browser carries a set of HTTP security headers that harden the web application against common client-side attacks (cross-site scripting, clickjacking, protocol downgrade, MIME-type confusion). They are applied at the edge/application tier and require no configuration by tenants.

Headers at a glance

Header Value (application UI) Protects against
Content-Security-Policy see below cross-site scripting, data exfiltration, unwanted framing
Strict-Transport-Security max-age=63072000; includeSubDomains protocol downgrade / SSL-stripping
X-Frame-Options SAMEORIGIN clickjacking
X-Content-Type-Options nosniff MIME-type confusion attacks
X-Robots-Tag noindex, nofollow — on the public share page (URLs under /shared/…) and the Outlook add-in task pane (/outlook/) a private transcript reachable by an unguessable link, or the add-in pane, being indexed by a search engine

Strict-Transport-Security (HSTS) tells the browser to only ever contact the host over HTTPS for the next two years. X-Content-Type-Options: nosniff stops the browser from second-guessing a response's declared content type. Both are sent on every response — the application UI, the API, and the documentation.

Content Security Policy

The application UI is served with a Content Security Policy that constrains where scripts, styles, images, fonts, connections and frames may come from:

default-src 'self';
script-src  'self' 'unsafe-inline' https://mtm.myrasecurity.com;
style-src   'self' 'unsafe-inline';
img-src     'self' data: blob: https://mtm.myrasecurity.com;
media-src   'self' data: blob:;
font-src    'self' data:;
connect-src 'self' <the environment's API hosts> https://mtm.myrasecurity.com;
worker-src  'self';
frame-src   'self' <the environment's admin API hosts>;
frame-ancestors 'self';
object-src  'none';
base-uri    'self';
form-action 'self'

Why each directive is shaped this way:

  • default-src 'self' — the baseline: by default a page may only load resources from its own origin. Every exception below is deliberate and enumerated.
  • img-src 'self' data: blob: — the UI renders several images as inline data:/blob: URLs rather than separate network requests: AI-generated images from the code interpreter, uploaded attachment thumbnails, the in-app tenant logo, and the pre-auth login logo. The login logo is fetched over connect-src (as JSON) and rendered as a data: URL for exactly this reason — a cross-host <img src> to the admin/auth asset host would be blocked in a split-host deployment. Notably, wildcard https: is excluded — an image referenced by an arbitrary external URL inside model or document output is never auto-loaded (it is shown as inert text instead), which closes a zero-click tracking/exfiltration channel. The single host-specific exception is https://mtm.myrasecurity.com (the Matomo analytics image beacon — see below); no other external host may load an image.
  • media-src 'self' data: blob: — audio/video elements load only from the app's own origin and inline data:/blob: sources. The read-aloud (text-to-speech) feature fetches synthesized audio and plays it from a blob: object URL; sandboxed artifacts may inline small data:/blob: media. As with img-src, https: is excluded, so an external <audio>/<video> URL in model or document output is never auto-loaded — the same zero-click-exfiltration close. (Without this directive, media falls back to default-src 'self', which does not cover blob:, and read-aloud is blocked outright.)
  • connect-src — the UI may only open network connections (data fetches, chat streaming, error reporting) to its own origin and the API hosts for its environment, plus https://mtm.myrasecurity.com for the Matomo analytics beacon (see below). Connections to any other host are blocked — including the FX rate source: the daily USD→EUR exchange rate for cost display is fetched by the gateway server-side and served from the API, so no third-party FX host appears in the policy.
  • frame-src — the UI embeds two kinds of frames: interactive artifacts produced by the assistant (rendered in a locked-down, script-only sandbox) and inline previews of documents such as PDFs served by the admin API. Both are permitted; nothing else.
  • object-src 'none', base-uri 'self', form-action 'self', frame-ancestors 'self' — hardening: no legacy plugin embeds, no <base> hijacking, forms may only submit to the application itself, and the application may not be embedded in a frame by another site (an additional clickjacking control alongside X-Frame-Options).

Why script-src allows inline scripts

script-src permits 'unsafe-inline' rather than pinning specific script hashes. This is a deliberate, necessary trade-off for the assistant artifact feature: interactive artifacts (for example an assistant-authored React component or an HTML/SVG preview) run untrusted, model-authored code inside a sandboxed frame with an opaque origin and no access to cookies, storage, or the parent page. Such a frame inherits the parent page's Content Security Policy, so a hash- or nonce-based script-src would block the artifact's own inline scripts and the feature could not run at all. The untrusted artifact code is instead contained by the sandbox itself and by the frame's own stricter policy (it is permitted no network access — connect-src 'none'), which is where the real isolation boundary lives. That per-frame policy applies to every artifact kind — the interactive React preview as well as the HTML and SVG previews. script-src still forbids eval and forbids loading scripts from any external origin except the single enumerated Matomo analytics host (see below).

Matomo analytics (https://mtm.myrasecurity.com)

The public web surfaces load Matomo Tag Manager for product analytics. This adds one host-specific external origin, https://mtm.myrasecurity.com (a Myra-operated domain), to three directives — script-src (the container and tracker JavaScript), img-src (the image-beacon fallback), and connect-src (the matomo.php tracking beacon). All three target that one host; no other external origin is permitted, so the policy stays otherwise closed.

The web app loads the tracker only on the production host (ai.myra.eu); the documentation site loads its own container on ai-docs.myra.eu / ai-docs-beta.myra.eu. In the web app it is also skipped when a session starts on a shared-conversation link (/shared/*, the token is part of the path), and the loader never injects the container script twice. A hostname gate means it never loads on beta, internal, development, CI, or preview builds, so those are permitted by the CSP but never actually inject it. Matomo is operated by Myra; its cookie and consent configuration is documented in the privacy policy.

Because that per-frame policy also blocks stylesheets, web fonts, remote images and API calls, an artifact has to be self-contained to render at all. The assistant's instructions state this constraint explicitly, so a generated artifact inlines its CSS and JavaScript and embeds images and fonts as data: URIs rather than fetching them.

The same containment applies when an artifact is opened in its own browser tab: that tab is an application page which hosts the artifact in the identical sandboxed frame and labels it as AI-generated content. The artifact document itself is never the tab's own page.

The API and documentation

The inference and admin APIs return JSON and streamed data that browsers do not apply a page CSP to, so they carry HSTS and nosniff plus targeted framing controls rather than the full application policy. The admin API deliberately permits the application UI to frame its document downloads (so inline PDF preview works) while still blocking framing by any third-party site. The documentation site carries HSTS, nosniff and X-Frame-Options: SAMEORIGIN.

The Office add-in task pane (/outlook/)

The Office.js Outlook add-in task pane is served on the application origin under /outlook/ but carries its own Content Security Policy (OUTLOOK_CSP), because the application policy's frame-ancestors 'self' would prevent Microsoft Outlook from embedding the pane in its webview. This one location deliberately differs from the application policy:

  • frame-ancestors lists the Microsoft-owned Office-web host origins (https://*.cloud.microsoft, https://*.officeapps.live.com, https://*.office.com, https://*.office365.com) — never a broad *.microsoft.com wildcard — so only Microsoft's Office surfaces may frame it. *.cloud.microsoft is required because Office-on-the-web is migrating to the unified cloud.microsoft domain (completing ~mid-2025); without it the pane would be refused framing (a blank pane) as that rollout lands (AGF-3083). X-Frame-Options is omitted for this location (it conflicts with cross-origin framing; frame-ancestors is the modern control).
  • script-src 'self' https://appsforoffice.microsoft.com allows the Office.js library from Microsoft's CDN and forbids 'unsafe-inline' (tighter than the application policy — the pane has no artifact-sandbox requirement). The pane's own logic ships as a built bundle.
  • connect-src reaches only the direct action lane (https://ai-api*.myra.eu).
  • The pane renders all model and mailbox output with textContent (never innerHTML), so an untrusted model response cannot inject script into this same-origin surface.

OUTLOOK_CSP is a sanctioned CSP value kept byte-identical across the SPA confs and enforced by scripts/lint_security_headers.sh alongside the application, admin and monitor policies.

The Office add-in task pane (/excel/)

The Office.js Excel add-in task pane (AGF-2823) is served under /excel/ and carries its own Content Security Policy (EXCEL_CSP), for the same reason as the Outlook pane — the application policy's frame-ancestors 'self' would prevent Excel from embedding it. EXCEL_CSP is now byte-identical to OUTLOOK_CSP (and WORD_CSP):

  • frame-ancestors is the modern full Office-web set — https://*.cloud.microsoft, https://*.officeapps.live.com, https://*.office.com, https://*.office365.com (AGF-3083; previously least-privilege to only *.officeapps.live.com, which would refuse framing as Excel-web migrates to cloud.microsoft). Desktop Excel (WebView2) does not enforce frame-ancestors. X-Frame-Options is omitted for this location (same rationale as Outlook).
  • script-src, connect-src, and the other directives are the same as OUTLOOK_CSP.
  • The pane renders all model output and its cell preview with textContent / createElement (never innerHTML); untrusted model output written into cells is additionally neutralised against formula injection (apostrophe + text numberFormat; a formula-write is screened) — see Direct document-AI actions.

EXCEL_CSP is a sanctioned CSP value kept byte-identical across the SPA confs and enforced by scripts/lint_security_headers.sh (Check 1 sanction + the /excel/ arm of the inheritance check).

The Office add-in task pane (/word/)

The Office.js Word add-in task pane (AGF-2687) is served under /word/ and carries WORD_CSP, which is byte-identical to OUTLOOK_CSP/EXCEL_CSP — the same modern Office-web frame-ancestors set (*.cloud.microsoft + *.officeapps.live.com + *.office.com + *.office365.com), Office.js CDN in script-src, and connect-src to the direct action lane. The pane renders model output via textContent and inserts into the document only through safe Word APIs (insertText/insertParagraph — never a live hyperlink/image/HTML sink); see Direct document-AI actions. WORD_CSP is sanctioned + lint-guarded (Check 1 + the /word/ arm) exactly like the other two.

The three Office add-in CSPs are currently identical; they are kept as three sanctioned values so the scripts/lint_security_headers.sh inheritance check can assert each /location/ independently.

The /privacy.html, /tos.html and /impressum.html redirect location (301 to the route, relative Location) declares no headers of its own and therefore inherits the server-level set above unchanged.