> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/TecharoHq/Anubis/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

Anubis uses cryptographic signing to secure challenge tokens and prevent tampering. This page covers key generation, JWT signing, and cookie security.

## JWT Signing

Challenge solutions are stored in JWT (JSON Web Tokens) signed with either:

* **Ed25519** (recommended) - Elliptic curve signatures
* **HMAC-SHA512** - Symmetric key signing

### Ed25519 (Recommended)

Ed25519 provides fast, secure signatures with small key sizes.

#### Generate Key

```bash theme={null}
# Generate 32-byte (256-bit) random key
openssl rand -hex 32 > /etc/anubis/ed25519.key

# Example output:
# a3f5d8c2e1b4f6a9c7d3e8f1a2b5c6d7e3f4a1b2c5d6e7f8a9b1c2d3e4f5a6b7
```

#### Configure Anubis

```bash theme={null}
# Via command-line flag
anubis --ed25519-private-key-hex-file /etc/anubis/ed25519.key

# Via environment variable
export ED25519_PRIVATE_KEY_HEX_FILE=/etc/anubis/ed25519.key

# Or inline (not recommended)
export ED25519_PRIVATE_KEY_HEX=a3f5d8c2e1b4f6a9c7d3e8f1a2b5c6d7e3f4a1b2c5d6e7f8a9b1c2d3e4f5a6b7
```

### HMAC-SHA512

Symmetric signing using a shared secret.

```bash theme={null}
# Generate secret
openssl rand -base64 64

# Configure
export HS512_SECRET="your-secret-here"
```

**Note**: Ed25519 is preferred. HMAC-SHA512 is legacy.

### Random Key (Development Only)

If no key is configured, Anubis generates a random key on startup:

```
WARN generating random key, Anubis will have strange behavior when multiple 
instances are behind the same load balancer target
```

**Consequences**:

* Challenges invalidated on restart
* Multi-instance deployments won't work
* Users must re-solve challenges after deployment

⚠️ **Never use random keys in production**

## Key Management

### Storage Requirements

Keys must be:

1. **Persistent** - Survives container/pod restarts
2. **Secret** - Not committed to version control
3. **Accessible** - Readable by Anubis process

### File Permissions

```bash theme={null}
# Secure key file
chown anubis:anubis /etc/anubis/ed25519.key
chmod 400 /etc/anubis/ed25519.key

# Verify
ls -l /etc/anubis/ed25519.key
# -r-------- 1 anubis anubis 64 Jan 1 12:00 /etc/anubis/ed25519.key
```

### Docker/Kubernetes

#### Docker Secret

```bash theme={null}
# Create secret
docker secret create anubis_key /path/to/ed25519.key

# Use in service
docker service create \
  --secret anubis_key \
  --env ED25519_PRIVATE_KEY_HEX_FILE=/run/secrets/anubis_key \
  ghcr.io/techarohq/anubis:latest
```

#### Kubernetes Secret

```bash theme={null}
# Create secret from file
kubectl create secret generic anubis-key \
  --from-file=ed25519.key=/path/to/ed25519.key
```

```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: anubis
spec:
  template:
    spec:
      containers:
      - name: anubis
        image: ghcr.io/techarohq/anubis:latest
        env:
        - name: ED25519_PRIVATE_KEY_HEX_FILE
          value: /secrets/ed25519.key
        volumeMounts:
        - name: signing-key
          mountPath: /secrets
          readOnly: true
      volumes:
      - name: signing-key
        secret:
          secretName: anubis-key
          defaultMode: 0400
```

### Key Rotation

1. Generate new key
2. Deploy Anubis with new key
3. Existing JWTs remain valid until expiry
4. New challenges use new key

No downtime required. Users with old cookies will re-solve challenges.

## Cookie Security

Anubis sets multiple security flags on cookies.

### Secure Flag

Requires HTTPS:

```bash theme={null}
# Enable (default)
anubis --cookie-secure=true

# Disable (development only)
anubis --cookie-secure=false
```

If `--cookie-secure=false`, SameSite automatically downgrades from `None` to `Lax`.

### SameSite

Controls cross-site cookie sending:

```bash theme={null}
# Options: None, Lax, Strict, Default
anubis --cookie-same-site None
```

| Value     | Behavior                               | Use Case                 |
| --------- | -------------------------------------- | ------------------------ |
| `None`    | Sent on all requests (requires Secure) | Embedded/third-party use |
| `Lax`     | Sent on top-level navigation           | Most sites (recommended) |
| `Strict`  | Only sent on same-site requests        | High-security apps       |
| `Default` | Browser default (usually Lax)          | Legacy compatibility     |

**Default**: `None` (with Secure flag)

### Partitioned (CHIPS)

Enable Cookies Having Independent Partitioned State:

```bash theme={null}
anubis --cookie-partitioned
```

Required for third-party cookies in Chrome (2024+).

### Cookie Domain

Share cookies across subdomains:

```bash theme={null}
# Static domain
anubis --cookie-domain example.com

# Dynamic (auto-detect from request)
anubis --cookie-dynamic-domain
```

**Static domain**:

* Cookie valid for `*.example.com`
* Set once, works everywhere

**Dynamic domain**:

* Cookie valid for request domain only
* Users on `www.example.com` and `api.example.com` get separate cookies

⚠️ **Cannot use both**: Setting both flags is an error.

### Cookie Expiration

```bash theme={null}
# Default: 24 hours
anubis --cookie-expiration-time 24h

# Custom values
anubis --cookie-expiration-time 1h   # 1 hour
anubis --cookie-expiration-time 7d   # 7 days (not supported, use 168h)
anubis --cookie-expiration-time 168h # 7 days
```

After expiration, users must re-solve challenges.

### Cookie Prefix

Customize cookie names:

```bash theme={null}
# Default: "anubis"
anubis --cookie-prefix myapp

# Results in cookies:
# - myapp-auth
# - myapp-cookie-verification
```

Use different prefixes when running multiple Anubis instances on the same domain.

## JWT Claims

JWT payload contains:

```json theme={null}
{
  "exp": 1735689600,          // Expiration timestamp
  "iat": 1735603200,          // Issued at timestamp
  "nbf": 1735603200,          // Not before timestamp
  "difficulty": 2,            // Optional: if --difficulty-in-jwt
  "X-Real-IP": "1.2.3.4"     // Optional: if --jwt-restriction-header set
}
```

### Difficulty in JWT

Include challenge difficulty in token:

```bash theme={null}
anubis --difficulty-in-jwt
```

Use cases:

* Track which difficulty was solved
* Audit challenge settings
* Debug multi-difficulty policies

### JWT IP Restriction

Bind JWT to specific IP address:

```bash theme={null}
# Default: X-Real-IP
anubis --jwt-restriction-header X-Real-IP

# Custom header
anubis --jwt-restriction-header CF-Connecting-IP
```

If set, JWT is only valid when request comes from the same IP that solved the challenge.

**Limitations**:

* Breaks mobile users (IP changes)
* Issues with carrier-grade NAT
* Only use if your threat model requires it

## Security Best Practices

### Key Generation

✅ **Do**:

* Use cryptographically secure random number generator
* Store keys in secrets management (Vault, AWS Secrets Manager)
* Rotate keys periodically
* Use Ed25519 (not HMAC-SHA512)

🚫 **Don't**:

* Commit keys to version control
* Use the same key across environments
* Share keys between services
* Use predictable/weak keys

### Cookie Settings

✅ **Do**:

* Use `--cookie-secure` in production (HTTPS only)
* Set appropriate `--cookie-same-site` for your use case
* Use short expiration times (balance security vs UX)
* Enable `--cookie-partitioned` for third-party contexts

🚫 **Don't**:

* Disable `--cookie-secure` in production
* Use `SameSite=None` without HTTPS
* Set extremely long expiration times
* Ignore browser warnings about cookie flags

### Deployment

✅ **Do**:

* Always configure signing keys in production
* Use persistent storage backends (bbolt, valkey, s3api)
* Monitor for "generating random key" warnings
* Test key rotation procedure

🚫 **Don't**:

* Rely on random key generation
* Use memory storage backend in production
* Forget to mount secrets in containers
* Share signing keys across environments

## Validation Errors

### Key Validation

```bash theme={null}
# Error: supplied key is not hex-encoded
ED25519_PRIVATE_KEY_HEX=not-hex-data

# Fix: Use hex-encoded key
ED25519_PRIVATE_KEY_HEX=$(openssl rand -hex 32)
```

```bash theme={null}
# Error: supplied key is not 32 bytes long
ED25519_PRIVATE_KEY_HEX=abc123  # Too short

# Fix: Must be exactly 64 hex characters (32 bytes)
ED25519_PRIVATE_KEY_HEX=$(openssl rand -hex 32)
```

### Conflicting Configuration

```bash theme={null}
# Error: do not specify both HS512 and ED25519 secrets
HS512_SECRET=secret1
ED25519_PRIVATE_KEY_HEX=secret2

# Fix: Use only one
unset HS512_SECRET
```

```bash theme={null}
# Error: can't set COOKIE_DOMAIN and COOKIE_DYNAMIC_DOMAIN
anubis --cookie-domain example.com --cookie-dynamic-domain

# Fix: Choose one
anubis --cookie-domain example.com
```

## Persistent Storage Warning

When using persistent storage without a configured key:

```
WARN [misconfiguration] persistent storage backend is configured, but no 
private key is set. Challenges will be invalidated when Anubis restarts. 
Set HS512_SECRET, ED25519_PRIVATE_KEY_HEX, or ED25519_PRIVATE_KEY_HEX_FILE
```

**Impact**:

* All active users must re-solve challenges after restart
* Degraded user experience
* Potential spike in challenge traffic

**Fix**: Configure a signing key (see [Key Generation](#key-generation)).

## Next Steps

* [Storage Backends](/admin/storage-backends) - Configure persistent storage
* [Monitoring](/admin/monitoring) - Track security events
* [Troubleshooting](/admin/troubleshooting) - Debug key/cookie issues
