Skip to main content
Securing your Figranium instance is critical, especially when running on a public server.

1. Authentication

By default, the Figranium UI requires an account. When you create your account during initial setup, Figranium enforces the following requirements:
  • Name: Must be 100 characters or fewer.
  • Email: Must be a valid email address (max 255 characters).
  • Password: Must be between 8 and 128 characters long.
All fields must be plain text strings. Requests with missing fields or values that exceed these limits are rejected with a 400 error. After setup, you should also:
  • API Key: Generate a strong API key in Settings > System. All API keys are limited to 512 characters.
  • Rotate: Regularly regenerate the API key if you suspect it’s compromised.
  • Header: Always use x-api-key or Authorization: Bearer for API calls.

2. IP Allowlist (ALLOWED_IPS)

The most effective way to secure a deployment is to restrict access to trusted IP addresses.
  • Environment Variable: ALLOWED_IPS
  • Example: ALLOWED_IPS=127.0.0.1,192.168.1.5,10.0.0.0/8
  • Config File: Alternatively, edit data/allowed_ips.json.
This blocks unauthorized traffic at the network level before it reaches the application logic.

3. SSRF Protection (ALLOW_PRIVATE_NETWORKS)

Server-Side Request Forgery (SSRF) is a risk when allowing users to trigger scrapes of arbitrary URLs.
  • Default: ALLOW_PRIVATE_NETWORKS=false — private network access is blocked out of the box.
    • This prevents users from scanning your internal network (e.g., http://169.254.169.254/latest/meta-data).
  • Local development: If you need to scrape services on your local network, set ALLOW_PRIVATE_NETWORKS=true.

Blocked destinations

When ALLOW_PRIVATE_NETWORKS is false, the following addresses are blocked:
CategoryBlocked ranges
IPv4 private10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (RFC 1918)
IPv4 special0.0.0.0/8 (current network), 127.0.0.0/8 (loopback), 169.254.0.0/16 (link-local), 100.64.0.0/10 (shared/CGN)
IPv4 reserved192.0.0.0/24 (IETF protocol assignments), 192.0.2.0/24 (TEST-NET-1), 198.18.0.0/15 (benchmarking), 198.51.100.0/24 (TEST-NET-2), 203.0.113.0/24 (TEST-NET-3), 224.0.0.0/4 (multicast), 240.0.0.0/4 (reserved)
IPv6 private::1/128 (loopback), fc00::/7 (unique local), fe80::/10 (link-local), ::/128 (unspecified), ff00::/8 (multicast)
Hostnameslocalhost, *.localhost, host.docker.internal
host.docker.internal is blocked because it resolves to the Docker host machine, which could expose internal services. This includes Ollama connections — both Ollama configuration URLs and API calls are validated against the SSRF blocklist. If you need to reach Ollama on the Docker host, set ALLOW_PRIVATE_NETWORKS=true.
Protocol validation is always enforced regardless of the ALLOW_PRIVATE_NETWORKS setting. Only http: and https: URLs are allowed. Schemes such as file://, ftp://, and javascript: are blocked even when private network access is enabled. SSRF protection also applies to output provider credentials. When you add or update a credential with a baseUrl (for example, a Baserow instance URL), Figranium validates the URL to block requests to private or internal network addresses. If the URL fails validation, the credential is rejected with an INVALID_BASE_URL error.

Proxy server validation

Proxy server addresses are validated against the same SSRF blocklist when you add, import, or update proxies in Settings > Proxies or through the API. If a proxy server URL points to a private or internal address, the request is rejected with an INVALID_URL error. This prevents an attacker (or a misconfiguration) from routing scrape traffic through a proxy that resolves to your internal network. The validation applies to all proxy management endpoints:
  • POST /api/settings/proxies — adding a single proxy
  • POST /api/settings/proxies/import — bulk importing proxies
  • PUT /api/settings/proxies/:id — updating an existing proxy
When bulk importing, each proxy entry is validated individually. If any entry points to a restricted address, the entire import is rejected and the error message identifies which server failed validation. If you need to use a proxy on your local network (for example, a SOCKS5 proxy running on the same machine), set ALLOW_PRIVATE_NETWORKS=true.

Ollama URL validation

Ollama base URLs are validated against the same SSRF blocklist when you save them in Settings > System > API Keys. If an Ollama URL points to a private or internal address (such as localhost or host.docker.internal), the save is rejected with an INVALID_URL error. Figranium also validates Ollama URLs at request time, not just when they are saved. Every outbound call to an Ollama instance — including AI-powered selector generation and extraction script generation — goes through redirect-safe fetching. This means:
  • The Ollama URL is re-validated against the SSRF blocklist before each request.
  • If the URL returns a redirect, every hop is validated before following it.
  • Sensitive headers are stripped on cross-origin redirects.
This defense-in-depth approach protects against cases where an Ollama URL was saved before stricter validation was introduced, or where DNS resolution changes between save time and request time.
Other AI providers (Gemini, OpenAI, Claude) connect to fixed public API endpoints and do not require this additional validation. Only Ollama uses user-supplied URLs, so only Ollama calls go through request-time SSRF checks.

Gemini API key transport

Gemini API keys are sent to Google’s API using the x-goog-api-key HTTP header rather than a URL query parameter. This prevents keys from appearing in server access logs, reverse-proxy logs, or Referer headers — all common sources of credential leakage. No configuration is required; this is the default behavior for all Gemini-powered endpoints (selector generation and script generation). If you run Ollama on the same machine or local network as Figranium, set ALLOW_PRIVATE_NETWORKS=true to permit these connections.

Redirect protection

When ALLOW_PRIVATE_NETWORKS is false, all outbound HTTP requests — including webhook callbacks and output provider requests (such as Baserow) — are protected against redirect-based SSRF attacks. If a URL returns a redirect (HTTP 3xx), Figranium validates the destination URL at every hop before following it. This prevents an attacker from providing a URL that initially resolves to a public address but redirects to a private one (such as 169.254.169.254 or localhost).
  • Up to 5 redirects are followed per request. If this limit is exceeded, the request fails.
  • Each redirect destination is checked against the same private-IP blocklist used for scrape URLs.
  • No configuration is needed — this protection is automatic when ALLOW_PRIVATE_NETWORKS=false.
  • Redirects that change method (HTTP 301, 302, 303) automatically switch to GET and drop the request body, following HTTP standards.

Cross-origin credential stripping

When a redirect crosses origin boundaries (for example, from api.baserow.io to a different domain), Figranium automatically strips sensitive headers from the redirected request. This prevents credentials from leaking to untrusted third-party servers. The following headers are removed on cross-origin redirects:
  • Authorization
  • X-API-Key
  • Token
  • Cookie
  • Proxy-Authorization
This protection applies to all outbound requests that follow redirects, including output provider pushes and webhook callbacks. No configuration is required — it is always active.

Browser navigation protection

When ALLOW_PRIVATE_NETWORKS is false, Figranium also guards against SSRF at the browser level. Every cross-origin navigation in a scrape, headful, or agent browser session is validated before the request leaves the browser. If a page attempts to navigate to a private network address, the navigation is blocked.
  • Same-origin navigations (where the destination shares the same origin as the current page) are allowed without additional checks.
  • Only top-level navigations are validated; sub-resource requests (images, scripts, etc.) are not intercepted.
  • Blocked navigations are logged to the server console with a [SECURITY] prefix.

Sandbox isolation

Extraction scripts and the extraction worker run inside a sandboxed environment that isolates them from the host Node.js runtime. The sandbox enforces the following restrictions on all data objects passed into your script:
  • Read-only access — you cannot set, define, or delete properties on proxied data objects. Attempts to do so are silently ignored.
  • Null prototypesObject.getPrototypeOf() returns null on sandboxed objects, preventing prototype-chain traversal back to host objects.
These restrictions apply to the data objects provided by Figranium (such as the page content passed to extraction workers), not to objects you create within your script. If your extraction script creates its own arrays or objects, those work normally.
// This works — you're creating your own object
const results = [];
results.push({ title: "Example" });
return results;

// This does NOT work — the source object is read-only
sourceData.customField = "value"; // silently ignored
If you have existing extraction scripts that modify proxied data objects, update them to copy values into new objects instead of mutating the originals.

DNS validation caching

To reduce the overhead of repeated DNS lookups during URL validation, Figranium caches hostname resolution results for 30 seconds. Both allowed and blocked hostnames are cached, so subsequent requests to the same host resolve instantly. The cache is cleared automatically after the TTL expires.

4. Session Security

  • Session Lifetime: Dashboard sessions expire after 7 days of inactivity. You will need to log in again after this period.
  • HTTPS: Always run Figranium behind a reverse proxy (Nginx, Caddy) that handles TLS/SSL.
  • Secure Cookies: Set SESSION_COOKIE_SECURE=true in your .env file to ensure cookies are only sent over HTTPS. Session cookies are always set with the httpOnly flag, which prevents client-side JavaScript from accessing the session token and mitigates XSS-based session theft.
  • HSTS: When SESSION_COOKIE_SECURE is enabled, Figranium automatically sets Strict-Transport-Security headers (max-age=31536000; includeSubDomains) on every response. This instructs browsers to only communicate with your instance over HTTPS for one year, preventing protocol-downgrade attacks.
  • Strong Secret: Ensure SESSION_SECRET is a long, random string.

5. Content Security Policy

Figranium sets a Content-Security-Policy (CSP) header on every HTTP response. This header tells browsers which origins are allowed to load scripts, styles, fonts, images, and network connections — providing defense-in-depth against cross-site scripting (XSS) and code injection attacks. The policy is applied automatically with no configuration required. The default directives are:
DirectiveAllowed sources
default-src'self'
script-src'self', inline scripts, eval (required by the editor runtime)
style-src'self', inline styles, Google Fonts
font-src'self', Google Fonts static assets
img-src'self', data:, blob:, Google, jsDelivr CDN, GitHub raw content
connect-src'self', GitHub API, Google Generative Language API, OpenAI API, Anthropic API, Baserow API, Ollama (local)
media-src'self', blob:
frame-src'self'
If you integrate a third-party service whose domain is not in the allow-list above, the browser will block those requests. Check the browser console for CSP violation reports to diagnose connectivity issues with external services. Ollama requests are made server-side, so CSP does not affect connectivity to your Ollama instance.
Figranium also sets the following security headers on every response:
  • X-Content-Type-Options: nosniff — prevents browsers from MIME-sniffing responses.
  • X-Frame-Options: SAMEORIGIN — blocks your instance from being embedded in iframes on other domains.
  • X-XSS-Protection: 1; mode=block — enables legacy XSS filters in older browsers.
  • Referrer-Policy: strict-origin-when-cross-origin — limits referrer information sent to external sites.

6. Reverse proxy and TRUST_PROXY

If you run Figranium behind a reverse proxy (Nginx, Caddy, AWS ALB), you should set TRUST_PROXY=true so the server can read the real client IP from proxy headers for logging and rate limiting.
When TRUST_PROXY is enabled, headers like X-Forwarded-For are trusted for general request metadata such as logging and rate limiting. Your reverse proxy must be configured to overwrite (not append to) the X-Forwarded-For header so that untrusted client-supplied values are stripped.

Loopback bypass hardening

The internal task-execution bypass (used by the x-internal-run header) requires both the proxy-resolved client IP and the direct TCP connection address to be loopback addresses. This dual validation prevents an external attacker from bypassing API key authentication by spoofing the X-Forwarded-For header — even when TRUST_PROXY is enabled, the raw socket address must also resolve to 127.0.0.1 or ::1. In practice this means:
  • Local processes calling the API from the same machine pass both checks automatically.
  • Remote requests that spoof a loopback X-Forwarded-For value are still rejected because the TCP connection originates from an external address.
To stay secure with TRUST_PROXY enabled:
  • Only set TRUST_PROXY=true when Figranium is actually behind a trusted proxy.
  • Configure your proxy to overwrite (not append to) the X-Forwarded-For header so untrusted values from clients are stripped.
  • Combine with ALLOWED_IPS to restrict which addresses can reach the server.

7. Rate Limiting

Figranium includes built-in rate limiting to prevent abuse.
  • Authentication: Max 10 failed login attempts per 15 minutes (AUTH_RATE_LIMIT_MAX).
  • Setup check: The setup status endpoint is rate-limited with the same authentication limiter to prevent attackers from probing whether your instance has been configured.
  • Data API: Max 100 data requests per 15 minutes (DATA_RATE_LIMIT_MAX).
  • Settings endpoints: API key, proxy, and other settings routes are rate-limited to prevent automated abuse.
Adjust these values in .env if legitimate traffic is being blocked.

8. Sanitized error responses

API error responses return generic error codes instead of detailed internal messages. This prevents information disclosure that could help an attacker map your infrastructure or discover implementation details. For example, a failed credential URL validation returns INVALID_BASE_URL with a fixed description rather than the underlying system error. The same applies to authentication endpoints, settings routes, and webhook URL validation. Each error response includes a structured error code you can match on programmatically:
Error codeEndpointMeaning
INVALID_WEBHOOK_URLPOST /api/execute/:idWebhook URL failed SSRF validation
INVALID_BASE_URLPOST /api/credentials, PUT /api/credentials/:idCredential base URL is malformed or points to a restricted address
INVALID_URLPOST /api/settings/ollama-api-keyOllama base URL failed SSRF validation
INVALID_URLPOST /api/settings/proxies, POST /api/settings/proxies/import, PUT /api/settings/proxies/:idProxy server address failed SSRF validation
INVALID_DATABASE_IDGET /api/credentials/:id/proxy/baserow/databases/:dbId/tablesDatabase ID is not a valid numeric identifier
INTERNAL_ERRORVariousAn unexpected server-side error occurred
Use the error code field (not the message) for programmatic error handling in your integrations. The message text may change between releases, but error codes are stable.