# 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)

```text
https://embed.openfort.io
```

### For API connections (connect-src)

```text
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:

```http
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=()
```

| Header | Purpose |
|--------|---------|
| `Strict-Transport-Security` | Enforces HTTPS connections |
| `X-Content-Type-Options` | Prevents MIME type sniffing |
| `X-Frame-Options` | Controls iframe embedding (use `DENY` unless embedding is needed) |
| `X-XSS-Protection` | Enables browser XSS filtering |
| `Referrer-Policy` | Controls referrer information sent with requests |
| `Permissions-Policy` | Restricts browser features |

## Base CSP configuration

Use this base CSP configuration for applications using Openfort:

```http
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:

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

Add the nonce to your script and style tags:

```html
<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**:

```javascript
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:

```javascript
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.

```ruby
# 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.

```nginx
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:

```nginx
# 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.

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

Or use the newer `report-to` directive:

```http
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`:

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

### Sentry error tracking

For Sentry error tracking, add the ingest endpoint:

```http
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:

```http
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:

```http
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.

:::warning
Avoid using wildcards (for example, `*.openfort.io`) in production unless absolutely necessary, as they reduce the effectiveness of your CSP.
:::
