Skip to content
LogoLogo

Content Security Policy (CSP)

A Content Security Policy (CSP) defines rules that tell browsers which content sources are valid. CSP mitigates XSS, clickjacking, and cross-site leak vulnerabilities in web client environments.

Integrating Openfort's embedded wallet SDK requires adjusting your CSP headers to allow Openfort's services to function correctly.

Required allowlisted domains

Allowlist the following domains in your CSP configuration for Openfort's embedded wallet to work correctly.

For iframes (frame-src, child-src)

https://embed.openfort.io

For API connections (connect-src)

https://api.openfort.io
https://embed.openfort.io
https://shield.openfort.io

For frame-ancestors

If you're embedding Openfort's iframe in your application, the iframe needs your domain in its frame-ancestors directive. Openfort automatically validates and configures this based on your project's allowed origins.

Additional security headers

Implement these security headers alongside CSP for comprehensive protection:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()
HeaderPurpose
Strict-Transport-SecurityEnforces HTTPS connections
X-Content-Type-OptionsPrevents MIME type sniffing
X-Frame-OptionsControls iframe embedding (use DENY unless embedding is needed)
X-XSS-ProtectionEnables browser XSS filtering
Referrer-PolicyControls referrer information sent with requests
Permissions-PolicyRestricts browser features

Base CSP configuration

Use this base CSP configuration for applications using Openfort:

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: blob: https://blog-cms.openfort.io;
  font-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';
  frame-src https://embed.openfort.io;
  child-src https://embed.openfort.io;
  connect-src 'self' https://api.openfort.io https://embed.openfort.io https://shield.openfort.io;
  worker-src 'self';
  manifest-src 'self';
  upgrade-insecure-requests;

Use nonces for stronger security

Nonce-based CSP provides stronger security than 'unsafe-inline'. A nonce is a random value generated per request:

Content-Security-Policy:
  script-src 'self' 'nonce-{{RANDOM_VALUE}}';
  style-src 'self' 'nonce-{{RANDOM_VALUE}}';

Add the nonce to your script and style tags:

<script nonce="{{RANDOM_VALUE}}">...</script>
<style nonce="{{RANDOM_VALUE}}">...</style>

Generate a new cryptographically random nonce for each request on the server side.

Framework-specific implementation

Next.js

Add CSP and security headers in next.config.js:

const cspHeader = `
  default-src 'self';
  script-src 'self';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: blob: https://blog-cms.openfort.io;
  font-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';
  frame-src https://embed.openfort.io;
  child-src https://embed.openfort.io;
  connect-src 'self' https://api.openfort.io https://embed.openfort.io https://shield.openfort.io;
  worker-src 'self';
  manifest-src 'self';
  upgrade-insecure-requests;
`.replace(/\n/g, '');
 
const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: cspHeader,
  },
  {
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000; includeSubDomains; preload',
  },
  {
    key: 'X-Content-Type-Options',
    value: 'nosniff',
  },
  {
    key: 'X-Frame-Options',
    value: 'DENY',
  },
  {
    key: 'X-XSS-Protection',
    value: '1; mode=block',
  },
  {
    key: 'Referrer-Policy',
    value: 'strict-origin',
  },
  {
    key: 'Permissions-Policy',
    value: 'geolocation=(), microphone=(), camera=(), payment=()',
  },
];
 
const nextConfig = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: securityHeaders,
      },
    ];
  },
};
 
module.exports = nextConfig;

Express.js

Configure CSP and security headers with the helmet middleware:

const helmet = require('helmet');
 
app.use(helmet());
 
app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "blob:", "https://blog-cms.openfort.io"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      baseUri: ["'self'"],
      formAction: ["'self'"],
      frameAncestors: ["'none'"],
      frameSrc: [
        "https://embed.openfort.io",
      ],
      childSrc: [
        "https://embed.openfort.io",
      ],
      connectSrc: [
        "'self'",
        "https://api.openfort.io",
        "https://embed.openfort.io",
        "https://shield.openfort.io",
      ],
      workerSrc: ["'self'"],
      manifestSrc: ["'self'"],
      upgradeInsecureRequests: [],
    },
  })
);
 
app.use(
  helmet.hsts({
    maxAge: 63072000,
    includeSubDomains: true,
    preload: true,
  })
);
 
app.use(
  helmet.referrerPolicy({
    policy: 'strict-origin',
  })
);
 
app.use(
  helmet.permittedCrossDomainPolicies({
    permittedPolicies: 'none',
  })
);

Ruby on Rails

Configure CSP and security headers in your application.

# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do |policy|
  policy.default_src :self
  policy.script_src  :self
  policy.style_src   :self, :unsafe_inline
  policy.img_src     :self, :data, :blob, "https://blog-cms.openfort.io"
  policy.font_src    :self
  policy.object_src  :none
  policy.base_uri    :self
  policy.form_action :self
  policy.frame_ancestors :none
  policy.frame_src   "https://embed.openfort.io",
  policy.child_src   "https://embed.openfort.io",
  policy.connect_src :self,
                     "https://api.openfort.io",
                     "https://embed.openfort.io",
                     "https://shield.openfort.io",
  policy.worker_src  :self
  policy.manifest_src :self
  policy.upgrade_insecure_requests true
end

# config/application.rb or config/environments/production.rb
config.action_dispatch.default_headers = {
  'X-Frame-Options' => 'DENY',
  'X-XSS-Protection' => '1; mode=block',
  'X-Content-Type-Options' => 'nosniff',
  'Referrer-Policy' => 'strict-origin',
  'Permissions-Policy' => 'geolocation=(), microphone=(), camera=(), payment=()'
}

# Enable HSTS
config.force_ssl = true
config.ssl_options = { hsts: { subdomains: true, preload: true, expires: 2.years } }

NGINX

Configure CSP and security headers in your NGINX configuration.

server {
    listen 443 ssl http2;
    server_name example.com;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;

    # CSP header
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://blog-cms.openfort.io; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; frame-src https://embed.openfort.io; child-src https://embed.openfort.io; connect-src 'self' https://api.openfort.io https://embed.openfort.io https://shield.openfort.io; worker-src 'self'; manifest-src 'self'; upgrade-insecure-requests" always;

    location / {
        # Your application configuration
    }
}

For nonce-based CSP with NGINX, use the $request_id variable:

# Generate nonce from request ID
set $csp_nonce $request_id;

add_header Content-Security-Policy "script-src 'self' 'nonce-$csp_nonce'; style-src 'self' 'nonce-$csp_nonce';" always;

# Use sub_filter to inject nonce into HTML
sub_filter_once off;
sub_filter_types text/html;
sub_filter 'CSP_NONCE_PLACEHOLDER' '$csp_nonce';

CSP violation reporting

Configure a reporting endpoint to monitor CSP violations in production.

Content-Security-Policy: [your policy]; report-uri /csp-report;

Or use the newer report-to directive:

Content-Security-Policy: [your policy]; report-to csp-endpoint;
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"/csp-report"}]}

You can send reports to analytics services like PostHog, Sentry, or your own endpoint.

Optional additions

Telegram login

For Telegram authentication, add:

  • frame-src: https://oauth.telegram.org
  • script-src: https://telegram.org

Solana support

For Solana applications, add these RPC endpoints to connect-src:

https://api.mainnet-beta.solana.com
https://api.devnet.solana.com

Sentry error tracking

For Sentry error tracking, add the ingest endpoint:

connect-src 'self' https://*.ingest.us.sentry.io https://*.ingest.sentry.io;

Custom RPC endpoints

For custom RPC endpoints, add them to the connect-src directive:

connect-src 'self' https://api.openfort.io https://your-rpc-endpoint.com;

Best practices

  • Start restrictive. Begin with a strict policy and loosen only as necessary. Use default-src 'none' as your baseline for maximum security.

  • Use nonces over unsafe-inline. Use nonce-based CSP instead of 'unsafe-inline' for scripts and styles.

  • Test in staging. Test CSP changes in a staging environment before deploying to production.

  • Use Report-Only mode. Before enforcing a new CSP, use Content-Security-Policy-Report-Only to identify issues without breaking functionality:

Content-Security-Policy-Report-Only: [your policy]; report-uri /csp-violation-report;
  • Monitor violations. Set up a reporting endpoint to track CSP violations and identify potential issues or attacks.

  • Avoid unsafe-eval. Never add 'unsafe-eval' to your script-src directive as it weakens your security posture.

  • Use upgrade-insecure-requests. Include upgrade-insecure-requests to automatically upgrade HTTP requests to HTTPS.

  • Layer your defenses. Combine CSP with other security headers (HSTS, X-Content-Type-Options, X-Frame-Options, and others) for comprehensive protection.

Troubleshooting

When encountering issues after implementing CSP:

  • Check browser console. CSP violations are logged to the browser's developer console with details about what was blocked.

  • Review network requests. Use the Network tab in developer tools to identify which domains your application reaches.

  • Incrementally add sources. When something is blocked, add only the specific source needed rather than using broad wildcards.

  • Test authentication flows. Verify all OAuth providers and authentication methods work correctly after CSP implementation.