Security | RedwoodSDK
RedwoodSDK

Security

Learn about security headers and how to configure them for your app.


šŸ” Security Headers

Security headers are an important part of protecting your application from common attacks like cross-site scripting (XSS), clickjacking, and data injection. RedwoodSDK makes it easy to add these headers to your responses using middleware.

Here is an example of a middleware that adds a set of common security headers:

src/app/headers.ts
import type { RouteMiddleware } from "rwsdk/worker";

export const setCommonHeaders =
  (): RouteMiddleware =>
  ({ response, rw: { nonce } }) => {
    const headers = response.headers;
    headers.set("X-Frame-Options", "DENY");
    headers.set("X-Content-Type-Options", "nosniff");
    headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
    headers.set(
      "Content-Security-Policy",
      `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline'; object-src 'none';`
    );
    headers.set(
      "Permissions-Policy",
      "geolocation=(), microphone=(), camera=()"
    );
  };

You can then apply this middleware in your src/worker.tsx:

src/worker.tsx
import { rwsdk } from "rwsdk/worker";
import { setCommonHeaders } from "./app/headers.js";
import { routes } from "./app/pages/routes.js";

export default {
  async fetch(request, env, ctx) {
    return rwsdk(request, env, ctx, {
      routes,
      middleware: [setCommonHeaders()],
    });
  },
};

Changing CSP (Content Security Policy) headers

Sometimes you need to allow additional resources or modify the Content Security Policy (CSP) to accommodate third-party scripts, styles, or other assets. The CSP headers control what resources can be loaded and executed by your application.

Adding trusted domains

Your CSP should reflect your application's needs. Some apps require more permissive policies to function properly, while security-critical applications might need stricter controls. The key is understanding the tradeoffs and making informed decisions. For more information, see the MDN Content Security Policy documentation.

// In app/headers.ts
headers.set(
  "Content-Security-Policy",
- `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline'; object-src 'none';`,
+ `default-src 'self'; script-src 'self' 'nonce-${nonce}' https://trusted-scripts.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' https://images.example.com; object-src 'none';`,
);

Allowing images from multiple sources

When working with images in your RedwoodSDK application, you may need to load images from different sources such as remote URLs or data URIs. The default CSP configuration doesn't include an img-src directive, which means images from external sources will be blocked.

To allow images from remote URLs and data URIs, add the img-src directive to your CSP:

// In app/headers.ts
headers.set(
  "Content-Security-Policy",
- `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline'; object-src 'none';`,
+ `default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline'; img-src 'self' https://trusted-images.example.com data:; object-src 'none';`,
);

This configuration allows:

  • 'self' - Images from your own domain
  • https://trusted-images.example.com - Images from specific trusted domains
  • data: - Data URIs (base64 encoded images)

Be cautious when using data: URIs as they can significantly increase the size of your HTML. Only allow specific remote domains that you trust, and consider using a more restrictive CSP in production environments.

Using nonce for inline scripts

Sometimes you need to include inline scripts in your application, but Content Security Policy (CSP) blocks them by default for security reasons. RedwoodSDK automatically generates a fresh, cryptographically secure nonce value for each request You can access this nonce in document or page components rendered by the router, using rw.nonce.

Only use the nonce attribute for trusted inline scripts that you have full control over. Adding nonces to third-party or user-generated scripts defeats the purpose of CSP protection and could expose your application to cross-site scripting (XSS) attacks.

export const Document = ({ rw, children }) => (
  <html lang="en">
    <head><!-- ... --></head>
    <body>
      {children}

      <!-- Set the nonce the inline script -->
      <script nonce={rw.nonce}>/* ... */</script>
    </body>
  </html>
);

Lifting device permission restrictions

Sometimes you need to allow your web application to access device features like the camera, microphone, or geolocation. These permissions are controlled by the Permissions-Policy header.

To enable device access, you'll need to modify the Permissions-Policy header in your app/headers.ts file:

// In app/headers.ts
headers.set(
  "Permissions-Policy",
-  "geolocation=(), microphone=(), camera=()",
+  "geolocation=self, microphone=self, camera=self"
);

The self keyword allows the feature to be used only by your own domain.

For a complete reference, see the MDN Permissions Policy documentation.