DataFn
Advanced

Security

Security measures built into DataFn.

DataFn implements multiple layers of security to protect data integrity, prevent unauthorized access, and defend against common web application attacks.

Deny-by-Default Authorization

Resources without an explicit permissions policy are forbidden by default. Every resource must declare read and write field policies:

{
  name: "todos",
  version: 1,
  fields: [
    { name: "title", type: "string", required: true },
    { name: "status", type: "string", required: false },
  ],
  permissions: {
    read: { fields: ["title", "status"] },
    write: { fields: ["title", "status"] },
  },
}

If no permissions block is defined, all queries and mutations against the resource return FORBIDDEN. This can be overridden for development with allowUnknownResources: true, but this should never be used in production.

Authorization is enforced on:

  • Query select fields
  • Query filter fields
  • Aggregation fields and groupBy
  • Sort fields
  • Search fields
  • Having clauses
  • Mutation record fields
  • Relation operations

Server-Derived Namespaces

The namespace for row-level data isolation is always derived on the server from the authenticated user's context. The client never supplies a namespace value. This prevents a compromised client from accessing another tenant's data:

// Server-side: namespace is derived from auth, never from request body
const namespace = authContext.tenantId;
await adapter.findMany({
  model: "todos",
  where: [],
  namespace,  // Always server-derived
});

Prototype Pollution Protection

All incoming payloads (queries, mutations, sync messages) are checked for prototype pollution attempts. Keys like __proto__, constructor, and prototype are rejected before processing:

// These payloads are rejected:
{ "__proto__": { "isAdmin": true } }
{ "constructor": { "prototype": { "isAdmin": true } } }

Path Traversal Protection

REST endpoints validate resource names and record IDs to prevent path traversal attacks. Inputs containing .., /, or \ are rejected:

GET /datafn/rest/../../etc/passwd  -> Rejected
GET /datafn/rest/todos/../secrets  -> Rejected

Sensitive Field Redaction

Fields marked with encrypt: true in the schema are redacted from log output. This prevents sensitive data (passwords, API keys, PII) from appearing in server logs:

{
  name: "users",
  version: 1,
  fields: [
    { name: "email", type: "string", required: true },
    { name: "apiKey", type: "string", required: false, encrypt: true },
  ],
}

In logs, encrypted fields appear as [REDACTED] instead of their actual values.

WebSocket Authentication

WebSocket connections require authentication at connection time. The client sends a hello message with its client ID and cursors. The server validates the connection before accepting messages:

// Client -> Server (on connect)
{
  "type": "hello",
  "clientId": "client-abc-123",
  "cursors": { "todos": "42" }
}

Unauthenticated WebSocket connections are rejected.

Error Message Redaction

In production mode (debug: false), error messages are stripped of internal details that could leak schema structure or implementation details:

// debug: true (development)
{ "code": "FORBIDDEN", "message": "No authorization policy found for resource 'secrets'" }

// debug: false (production)
{ "code": "FORBIDDEN", "message": "Authorization denied" }

Content-Type Validation

The server validates Content-Type headers on all incoming requests. Only application/json is accepted for DFQL endpoints. Requests with incorrect content types are rejected before any processing occurs.

Rate Limiting

DataFn includes built-in rate limiting to prevent denial-of-service attacks. Rate limits can be configured per endpoint:

const server = createDatafnServer({
  schema,
  rateLimit: {
    enabled: true,
    windowMs: 60000,     // 1 minute window
    maxRequests: 100,    // Max 100 requests per window
  },
});

When the rate limit is exceeded, the server returns a LIMIT_EXCEEDED error.

Security Checklist

MeasureDescription
Deny-by-default authResources without policies are forbidden
Server-derived namespaceClient cannot specify namespace
Prototype pollution check__proto__, constructor, prototype blocked
Path traversal check.., /, \ in resource/ID rejected
Sensitive field redactionencrypt: true fields hidden in logs
WebSocket authAuthentication required at connection time
Error redactionInternal details hidden in production
Content-Type validationOnly application/json accepted
Rate limitingConfigurable per-endpoint limits
IdempotencyPrevents replay attacks via deduplication