# Open Notebook CORS Middleware Security Considerations for Production Deployments

> Secure your Open Notebook production APIs by understanding CORS middleware security. Learn how to configure trusted origins and prevent cross-origin attacks.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: best-practices
- Published: 2026-06-07

---

**Open Notebook’s FastAPI backend defaults to allowing all origins with credentials enabled, which exposes production APIs to cross-origin attacks unless you explicitly configure the `CORS_ORIGINS` environment variable to whitelist only trusted domains.**

The Open Notebook project relies on Starlette’s `CORSMiddleware` to handle cross-origin resource sharing in its FastAPI-based API. Understanding the CORS middleware security considerations for production deployments is essential because the default configuration prioritizes local development convenience over security, potentially allowing credential leakage and unauthorized cross-origin requests from malicious websites.

## Default Wildcard Origin Risks

### Automatic Fallback to Permissive Settings

In [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), the application parses CORS configuration using a local helper function that defaults to the `"*"` wildcard when the environment variable is unset:

```python
_cors_origins_raw = os.getenv("CORS_ORIGINS")
CORS_ALLOWED_ORIGINS = _parse_cors_origins(_cors_origins_raw or "*")
CORS_IS_DEFAULT_WILDCARD = _cors_origins_raw is None

```

As implemented in `api/main.py#L62-L64`, this logic sets `CORS_ALLOWED_ORIGINS` to accept requests from **any domain** if you do not explicitly define the `CORS_ORIGINS` environment variable.

### Startup Warning Indicators

When `CORS_IS_DEFAULT_WILDCARD` evaluates to `True`, the application emits a warning log at startup (`api/main.py#L163-L168`) alerting administrators that the API accepts cross-origin requests from any origin. This serves as a failsafe to notify operators that the deployment remains in an insecure default state.

## Credential Handling Vulnerabilities

### Hardcoded Credential Permission

The middleware registration in `api/main.py#L191-L194` hardcodes `allow_credentials=True`:

```python
app.add_middleware(
    CORSMiddleware,
    allow_origins=CORS_ALLOWED_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

```

This configuration instructs browsers to include cookies, HTTP authentication headers, and client-side certificates with cross-origin requests. When combined with wildcard origins or overly permissive origin lists, this allows malicious websites to perform authenticated actions against your API using the victim’s existing session credentials.

### Cross-Origin Data Exfiltration

Because the default headers and methods are also set to `"*"`, attackers can read sensitive API responses from any origin that successfully authenticates. The combination of wildcard origins and credential transmission creates a high-risk scenario for data exfiltration and session hijacking.

## Middleware Placement and Error Handling

### Request Processing Order

The CORS middleware is added **last** in the application initialization sequence (`api/main.py#L188-L195`). In FastAPI’s middleware architecture, the last-added middleware processes requests **first**, ensuring that CORS headers are injected before authentication or routing logic executes. This ordering guarantees that even requests rejected by upstream middleware still receive proper CORS headers.

### Exception Handler CORS Injection

The application implements custom exception handlers that manually inject CORS headers into error responses using the `_cors_headers(request)` utility function (`api/main.py#L67-L84` and `api/main.py#L98-L105`). This prevents browsers from blocking error details due to missing CORS headers on 4xx or 5xx responses, ensuring consistent cross-origin behavior even during failure states.

## Production Hardening Configuration

Follow these steps to secure the CORS middleware in production environments:

1. **Explicitly define `CORS_ORIGINS`** – Set this environment variable to a comma-separated list of trusted front-end URLs (e.g., `https://notebook.example.com,https://admin.example.com`) instead of relying on the wildcard default.

2. **Audit credential requirements** – Review whether your front-end actually requires cookie-based authentication or HTTP-auth headers. If using token-based authentication in request headers, consider setting `allow_credentials=False` in `api/main.py#L191`.

3. **Restrict HTTP methods** – Replace `"*"` with explicit methods your API requires (e.g., `["GET", "POST", "PUT", "DELETE"]`) to limit the attack surface for cross-origin state-changing requests.

4. **Validate startup logs** – Confirm that `CORS_IS_DEFAULT_WILDCARD` is `False` and verify the parsed whitelist appears correctly in the startup logs emitted at `api/main.py#L163-L168`.

5. **Configure reverse proxy CORS** – Ensure your reverse proxy (nginx, Traefik, etc.) forwards or adds appropriate CORS headers for errors generated at the proxy layer (such as 413 Payload Too Large), as the FastAPI CORS middleware only handles application-level responses.

### Secure Configuration Example

Environment configuration:

```bash

# .env file

CORS_ORIGINS=https://notebook.example.com,https://admin.example.com

```

Application-level restrictions (modifying [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py)):

```python
app.add_middleware(
    CORSMiddleware,
    allow_origins=CORS_ALLOWED_ORIGINS,  # Parsed from CORS_ORIGINS above

    allow_credentials=True,              # Only if cookies/auth headers are required

    allow_methods=["GET", "POST", "PUT", "DELETE"],  # Explicit method whitelist

    allow_headers=["Authorization", "Content-Type"], # Explicit header whitelist

)

```

Error handling ensures CORS compliance:

```python
@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request: Request, exc: StarletteHTTPException):
    return JSONResponse(
        status_code=exc.status_code,
        content={"detail": exc.detail},
        headers={**(exc.headers or {}), **_cors_headers(request)},
    )

```

## Summary

- Open Notebook defaults to `CORS_ORIGINS="*"` when the environment variable is unset, exposing the API to any domain.
- The hardcoded `allow_credentials=True` in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) creates credential leakage risks when combined with permissive origin settings.
- Middleware placement as the last-added middleware ensures CORS headers are present before authentication checks execute.
- Custom exception handlers in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) manually inject CORS headers to maintain cross-origin visibility during error responses.
- Production security requires explicitly setting `CORS_ORIGINS`, auditing credential needs, and restricting methods/headers to minimum required values.

## Frequently Asked Questions

### What happens if I don't set CORS_ORIGINS in production?

The API falls back to accepting requests from any origin (`"*"`) while still allowing credentials, which allows malicious websites to make authenticated requests using your users' existing session cookies or HTTP-auth headers against the API.

### Why is allow_credentials=True considered a security risk?

When `allow_credentials=True` is combined with wildcard or overly permissive origin lists, browsers will send cookies, authorization headers, and TLS client certificates with cross-origin requests. This enables attackers to perform actions as authenticated users if they can lure victims to a malicious site.

### How does the middleware order affect CORS security in Open Notebook?

The CORS middleware is added last in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py), making it the outermost layer in the processing stack. This ensures CORS headers are added to the response before authentication middleware runs, preventing browsers from blocking error responses due to missing CORS headers on rejected requests.

### Where is the CORS configuration parsed in the Open Notebook source code?

The configuration is parsed in `api/main.py#L62-L64` using `os.getenv("CORS_ORIGINS")` and the `_parse_cors_origins()` helper function, with the resulting `CORS_ALLOWED_ORIGINS` list passed to the `CORSMiddleware` constructor at `api/main.py#L191`.