FortiBlox LogoFortiBlox Docs
NexusSecurity

Security Best Practices

Comprehensive security guidelines for FortiBlox Nexus API keys and applications

Security Best Practices

Protecting your FortiBlox Nexus API keys is critical to preventing unauthorized usage, unexpected charges, and security breaches. This guide provides comprehensive security practices with clear do's and don'ts.

Critical: Never expose API keys in client-side code, public repositories, or browser applications. Compromised keys can lead to service disruption and unexpected charges.

Environment Variables: The Foundation

Don't Do This

// ❌ NEVER hardcode API keys
const apiKey = "fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p";
const connection = new Connection(
  `https://nexus.fortiblox.com/rpc?api-key=${apiKey}`
);
# ❌ NEVER hardcode API keys
API_KEY = "fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p"
client = Client(f"https://nexus.fortiblox.com/rpc?api-key={API_KEY}")
// ❌ NEVER hardcode API keys
let api_key = "fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p";
let url = format!("https://nexus.fortiblox.com/rpc?api-key={}", api_key);

Do This Instead

// ✅ CORRECT - Use environment variables
require('dotenv').config();

const connection = new Connection(
  `https://nexus.fortiblox.com/rpc?api-key=${process.env.FORTIBLOX_API_KEY}`,
  'confirmed'
);

// Validate the key exists
if (!process.env.FORTIBLOX_API_KEY) {
  throw new Error('FORTIBLOX_API_KEY environment variable is required');
}

Create a .env file:

# .env
FORTIBLOX_API_KEY=fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p
NODE_ENV=production

Add to .gitignore:

# .gitignore
.env
.env.local
.env.*.local
*.env
# ✅ CORRECT - Use environment variables
import os
from dotenv import load_dotenv
from solana.rpc.api import Client

load_dotenv()

# Validate the key exists
api_key = os.getenv('FORTIBLOX_API_KEY')
if not api_key:
    raise ValueError('FORTIBLOX_API_KEY environment variable is required')

client = Client(f"https://nexus.fortiblox.com/rpc?api-key={api_key}")

Create a .env file:

# .env
FORTIBLOX_API_KEY=fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p
ENVIRONMENT=production

Add to .gitignore:

# .gitignore
.env
*.env
__pycache__/
// ✅ CORRECT - Use environment variables
use solana_client::rpc_client::RpcClient;
use std::env;
use dotenv::dotenv;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv().ok();

    // Validate the key exists
    let api_key = env::var("FORTIBLOX_API_KEY")
        .expect("FORTIBLOX_API_KEY environment variable is required");

    let url = format!("https://nexus.fortiblox.com/rpc?api-key={}", api_key);
    let client = RpcClient::new(url);

    Ok(())
}

Create a .env file:

# .env
FORTIBLOX_API_KEY=fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p
RUST_ENV=production

Add to .gitignore:

# .gitignore
.env
target/
Cargo.lock

Separate Keys Per Environment

Don't Do This

// ❌ NEVER use the same key across environments
const apiKey = process.env.FORTIBLOX_API_KEY; // One key for everything

Do This Instead

// ✅ CORRECT - Environment-specific keys
function getFortiBloxApiKey() {
  const env = process.env.NODE_ENV || 'development';

  switch (env) {
    case 'production':
      return process.env.FORTIBLOX_API_KEY_PROD;
    case 'staging':
      return process.env.FORTIBLOX_API_KEY_STAGING;
    case 'development':
    default:
      return process.env.FORTIBLOX_API_KEY_DEV;
  }
}

const apiKey = getFortiBloxApiKey();
if (!apiKey) {
  throw new Error(`FORTIBLOX_API_KEY for ${process.env.NODE_ENV} is not set`);
}

const connection = new Connection(
  `https://nexus.fortiblox.com/rpc?api-key=${apiKey}`,
  'confirmed'
);

Benefits:

  • Isolate usage and billing per environment
  • Revoke compromised keys without affecting other environments
  • Apply different access controls per environment
  • Better monitoring and cost tracking

Never Use API Keys in Browser Applications

The Problem

<!-- ❌ EXTREMELY DANGEROUS - Anyone can steal your key -->
<script>
  const apiKey = 'fbx_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p';

  fetch(`https://nexus.fortiblox.com/rpc?api-key=${apiKey}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'getHealth',
      id: 1
    })
  });
</script>

Why this is dangerous:

  1. Anyone can open DevTools → Network tab and see your API key
  2. Your key appears in browser history and logs
  3. Attackers can copy and abuse your key
  4. No way to prevent unauthorized usage

Create a backend API that proxies requests to FortiBlox:

// ✅ CORRECT - Backend proxy
const express = require('express');
const fetch = require('node-fetch');
require('dotenv').config();

const app = express();
app.use(express.json());

// CORS configuration (restrict to your domain)
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://yourdomain.com');
  res.header('Access-Control-Allow-Methods', 'POST');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

app.post('/api/rpc', async (req, res) => {
  try {
    // API key is stored securely on the server
    const apiKey = process.env.FORTIBLOX_API_KEY;

    // Optional: Add request validation
    if (!req.body.method) {
      return res.status(400).json({ error: 'Missing RPC method' });
    }

    // Optional: Rate limiting per user/IP
    // Implement your rate limiting logic here

    // Forward request to FortiBlox
    const response = await fetch(
      `https://nexus.fortiblox.com/rpc?api-key=${apiKey}`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(req.body)
      }
    );

    const data = await response.json();
    res.json(data);
  } catch (error) {
    console.error('RPC proxy error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

app.listen(3000, () => {
  console.log('RPC proxy running on port 3000');
});

Frontend code:

// Frontend - No API key exposed!
async function getAccountInfo(publicKey) {
  const response = await fetch('/api/rpc', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      jsonrpc: '2.0',
      method: 'getAccountInfo',
      params: [publicKey.toString()],
      id: 1
    })
  });

  return await response.json();
}
# ✅ CORRECT - Backend proxy
from flask import Flask, request, jsonify
from flask_cors import CORS
import requests
import os
from dotenv import load_dotenv

load_dotenv()
app = Flask(__name__)

# CORS configuration (restrict to your domain)
CORS(app, origins=["https://yourdomain.com"])

@app.route('/api/rpc', methods=['POST'])
def rpc_proxy():
    try:
        # API key is stored securely on the server
        api_key = os.getenv('FORTIBLOX_API_KEY')
        if not api_key:
            return jsonify({'error': 'Server configuration error'}), 500

        # Optional: Add request validation
        if 'method' not in request.json:
            return jsonify({'error': 'Missing RPC method'}), 400

        # Forward request to FortiBlox
        response = requests.post(
            f"https://nexus.fortiblox.com/rpc?api-key={api_key}",
            json=request.json,
            headers={'Content-Type': 'application/json'}
        )

        return jsonify(response.json())
    except Exception as e:
        print(f"RPC proxy error: {e}")
        return jsonify({'error': 'Internal server error'}), 500

if __name__ == '__main__':
    app.run(port=5000)
// ✅ CORRECT - Next.js API route
// pages/api/rpc.js
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  try {
    // API key is stored securely on the server
    const apiKey = process.env.FORTIBLOX_API_KEY;

    if (!req.body.method) {
      return res.status(400).json({ error: 'Missing RPC method' });
    }

    // Forward request to FortiBlox
    const response = await fetch(
      `https://nexus.fortiblox.com/rpc?api-key=${apiKey}`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(req.body)
      }
    );

    const data = await response.json();
    res.status(200).json(data);
  } catch (error) {
    console.error('RPC proxy error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
}

Frontend code (React):

// Frontend - No API key exposed!
import { useConnection } from './useConnection';

function MyComponent() {
  async function fetchAccountInfo(publicKey) {
    const response = await fetch('/api/rpc', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        jsonrpc: '2.0',
        method: 'getAccountInfo',
        params: [publicKey.toString()],
        id: 1
      })
    });

    return await response.json();
  }

  // Use the function...
}

Solution 2: FortiBlox RPC Proxy (Coming Soon)

We're developing an open-source RPC proxy tool that you can deploy to Cloudflare Workers, Vercel, or your own infrastructure with one click.

Planned features:

  • One-click deployment to Cloudflare Workers
  • Automatic request filtering and validation
  • Custom rate limiting per user/IP
  • Domain whitelisting built-in
  • Zero cold-start latency
  • WebSocket support
  • Free tier friendly (100,000 requests/day on Cloudflare)

Track the roadmap →

Access Control: Multiple Layers

FortiBlox Nexus provides multiple layers of access control to protect your API keys.

Domain Restrictions (Web Applications)

Restrict which domains can use your API key.

Configure in Developer Portal:

  1. Go to API Keys → Select your key
  2. Enable Domain Restrictions
  3. Add allowed domains:
https://myapp.com
https://*.myapp.com
https://staging.myapp.com
localhost:3000

Wildcard support: Use *.myapp.com to allow all subdomains

How it works:

// ✅ Request from https://myapp.com - ALLOWED
fetch('https://nexus.fortiblox.com/rpc?api-key=fbx_...', {
  method: 'POST',
  // Origin header: https://myapp.com
});

// ❌ Request from https://attacker.com - BLOCKED
// Returns: 403 Forbidden - Domain not allowed

Important: Domain restrictions only work when browsers send Origin or Referer headers. For server-to-server calls, use IP restrictions instead.

IP Address Restrictions (Server Applications)

Restrict which IP addresses or CIDR ranges can use your API key.

Configure in Developer Portal:

  1. Go to API Keys → Select your key
  2. Enable IP Restrictions
  3. Add allowed IPs:
# Allow only requests from this IP
203.0.113.45

Use cases:

  • Single server deployments
  • Home/office development
  • Specific cloud instances
# Allow requests from this IP range
203.0.113.0/24

This allows IPs from 203.0.113.0 to 203.0.113.255 (256 addresses).

Common CIDR ranges:

  • /32 - Single IP (203.0.113.45/32)
  • /24 - 256 IPs (203.0.113.0/24)
  • /16 - 65,536 IPs (203.0.0.0/16)
# Allow multiple specific IPs (one per line)
203.0.113.45
198.51.100.12
192.0.2.100
2001:db8::1  # IPv6 supported

Use cases:

  • Multiple server instances
  • Load-balanced deployments
  • Distributed systems
# AWS EC2 instances in a VPC
172.31.0.0/16

# Google Cloud Platform
10.128.0.0/16

# Azure
10.0.0.0/16

# DigitalOcean
159.203.0.0/16

Find your cloud provider's IP ranges:

Find your IP: Run curl ifconfig.me or visit https://ifconfig.me

Network Restrictions

Restrict which X1 Blockchain networks (mainnet, devnet, testnet) the API key can access.

// API key restricted to devnet only

// ✅ This works - devnet allowed
const devnetConnection = new Connection(
  `https://nexus.fortiblox.com/rpc?api-key=${process.env.FORTIBLOX_API_KEY}&network=devnet`
);

// ❌ This fails - mainnet not allowed
// Returns: 403 Forbidden - Network not allowed
const mainnetConnection = new Connection(
  `https://nexus.fortiblox.com/rpc?api-key=${process.env.FORTIBLOX_API_KEY}&network=mainnet`
);

Benefits:

  • Prevent accidental mainnet usage with dev keys
  • Separate billing for different networks
  • Enhanced security through network isolation

Key Rotation

Regular key rotation minimizes the impact of a compromised key.

Recommended rotation schedule:

  • Development keys: Every 90 days
  • Production keys: Every 30 days
  • After employee departure: Immediately
  • After suspected compromise: Immediately

Rotation Procedure

Step 1: Generate new key

  1. Go to Developer Portal → API Keys
  2. Click "Create New API Key"
  3. Use same permissions as old key
  4. Copy the new key immediately

Step 2: Update environment variables

# Update .env files
echo "FORTIBLOX_API_KEY=fbx_NEW_KEY_HERE" > .env.production

# For cloud deployments
# AWS Secrets Manager
aws secretsmanager update-secret \
  --secret-id fortiblox-api-key \
  --secret-string "fbx_NEW_KEY_HERE"

# GCP Secret Manager
echo -n "fbx_NEW_KEY_HERE" | \
  gcloud secrets versions add fortiblox-api-key --data-file=-

# Azure Key Vault
az keyvault secret set \
  --vault-name MyKeyVault \
  --name fortiblox-api-key \
  --value "fbx_NEW_KEY_HERE"

Step 3: Deploy changes

# Deploy to production
git add .env.production
git commit -m "chore: rotate FortiBlox API key"
git push origin main

# Or use your deployment tool
npm run deploy
# or
docker-compose up -d

Step 4: Verify functionality

# Test the new key
curl https://nexus.fortiblox.com/rpc?api-key=$NEW_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"getHealth","id":1}'

# Expected response: {"jsonrpc":"2.0","result":"ok","id":1}

Step 5: Monitor and revoke old key

  1. Monitor production for 24-48 hours
  2. Check error rates and logs
  3. Once confirmed working, revoke old key in Developer Portal
  4. Document the rotation in your changelog

Automated Rotation Script

#!/bin/bash
# rotate-api-key.sh

set -e

echo "FortiBlox API Key Rotation Script"
echo "=================================="

# Step 1: Prompt for new key (manual generation for now)
echo "1. Generate a new API key in the Developer Portal"
echo "2. Copy the new key"
read -sp "Enter new API key: " NEW_KEY
echo

# Step 2: Backup old key
OLD_KEY=$(grep FORTIBLOX_API_KEY .env.production | cut -d '=' -f2)
echo "Old key (first 10 chars): ${OLD_KEY:0:10}..."

# Step 3: Update .env file
sed -i.bak "s/FORTIBLOX_API_KEY=.*/FORTIBLOX_API_KEY=$NEW_KEY/" .env.production
echo "✓ Updated .env.production"

# Step 4: Test new key
echo "Testing new key..."
RESPONSE=$(curl -s https://nexus.fortiblox.com/rpc?api-key=$NEW_KEY \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"getHealth","id":1}')

if echo "$RESPONSE" | grep -q '"result":"ok"'; then
  echo "✓ New key is valid"
else
  echo "✗ New key validation failed"
  echo "Response: $RESPONSE"
  # Restore old key
  mv .env.production.bak .env.production
  exit 1
fi

# Step 5: Commit changes (optional)
echo "API key rotated successfully!"
echo "Remember to:"
echo "1. Deploy the changes"
echo "2. Monitor production for 24-48 hours"
echo "3. Revoke the old key in the Developer Portal"

Monitoring API Key Usage

Regular monitoring helps detect suspicious activity early.

What to Monitor

1. Unusual request patterns

  • Sudden spike in requests (10x normal)
  • Requests at odd hours (3 AM on weekends)
  • Requests from unexpected geographic locations

2. Unexpected endpoints

  • RPC methods you don't use in your app
  • High-cost operations (e.g., getProgramAccounts)
  • Failed authentication attempts

3. Error rates

  • Sudden increase in 403 (Forbidden) errors
  • Rate limit exceeded errors
  • Authentication failures

Set Up Alerts

In the Developer Portal:

  1. Go to SettingsNotifications
  2. Enable alerts for:
    • Daily credit usage > 80% of monthly limit
    • Rate limit exceeded > 10 times/hour
    • Unusual usage patterns detected
    • Failed authentication > 100 attempts/day
    • API key approaching expiration

Email notification example:

Subject: [FortiBlox Alert] Unusual API key usage detected

Your API key (fbx_...5o6p) has unusual activity:

- Request spike: 50,000 requests in the last hour (normal: 5,000)
- New geographic location: Requests from Russia (first time)
- New RPC methods: getProgramAccounts (never used before)

Action recommended: Review your application logs and consider rotating the key.

View details: https://nexus.fortiblox.com/api-keys/12345/analytics

Least Privilege Principle

Only grant the minimum necessary permissions to each API key.

Don't Do This

// ❌ One key with all permissions
const masterKey = process.env.FORTIBLOX_MASTER_KEY;

// Used everywhere
const rpcConnection = new Connection(`https://nexus.fortiblox.com/rpc?api-key=${masterKey}`);
const geyserStream = connectGeyser(masterKey);
const enhancedApi = new EnhancedAPI(masterKey);

Do This Instead

// ✅ Separate keys for different services
const config = {
  rpc: process.env.FORTIBLOX_RPC_KEY,           // RPC-only access
  geyser: process.env.FORTIBLOX_GEYSER_KEY,     // Geyser-only access
  enhanced: process.env.FORTIBLOX_ENHANCED_KEY  // Enhanced API only
};

// Each service uses its specific key
const rpcConnection = new Connection(
  `https://nexus.fortiblox.com/rpc?api-key=${config.rpc}`
);

const geyserStream = connectGeyser(config.geyser);

const enhancedApi = new EnhancedAPI(config.enhanced);

Benefits:

  • Limit blast radius of compromised keys
  • Better cost tracking per service
  • Easier to debug issues
  • Granular access control
  • Revoke specific service access without affecting others

Git Repository Safety

Don't Do This

# ❌ NEVER commit .env files
git add .env
git commit -m "Add configuration"
git push origin main

# Now your API key is in git history forever

Do This Instead

# ✅ CORRECT - Always .gitignore sensitive files

# 1. Create .gitignore before first commit
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".env.*.local" >> .gitignore
echo "*.env" >> .gitignore

# 2. Commit .gitignore
git add .gitignore
git commit -m "Add .gitignore"

# 3. Create .env.example as a template (without real keys)
cat > .env.example << 'EOF'
# FortiBlox API Configuration
FORTIBLOX_API_KEY=fbx_your_api_key_here
NODE_ENV=development
EOF

# 4. Commit the template
git add .env.example
git commit -m "Add environment template"

If You Already Committed Secrets

If you accidentally committed API keys to git, you must:

1. Revoke the exposed key immediately

  • Go to Developer Portal → API Keys
  • Find the exposed key and click "Revoke"

2. Remove from git history

# Use BFG Repo-Cleaner (easier than git filter-branch)
# Download from: https://rtyley.github.io/bfg-repo-cleaner/

# Remove .env from history
bfg --delete-files .env

# Clean up
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# Force push (WARNING: coordinate with team first!)
git push origin --force --all

3. Generate a new key

  • Create a new API key in Developer Portal
  • Update your application with the new key

Important: Even after removing from git history, assume the key was compromised. Always revoke and regenerate.

Security Checklist

Use this checklist to ensure your API keys are secure:

Development

  • API keys stored in environment variables (not hardcoded)
  • .env files added to .gitignore
  • .env.example template provided (without real keys)
  • No API keys in source code or comments
  • No API keys in configuration files committed to git

Deployment

  • Separate keys for dev, staging, and production
  • Keys stored in secure secret management (AWS Secrets Manager, etc.)
  • Environment-specific key selection logic implemented
  • Keys validated on application startup

Access Control

  • Domain restrictions enabled (for web apps)
  • IP restrictions enabled (for server apps)
  • Network restrictions configured (mainnet/devnet)
  • Least privilege principle applied (separate keys per service)

Monitoring & Rotation

  • Usage monitoring enabled in Developer Portal
  • Alert notifications configured
  • Key rotation schedule documented
  • Keys rotated at least every 90 days
  • Old/unused keys revoked

Client-Side Safety

  • Backend proxy used for browser applications
  • No API keys in frontend JavaScript
  • No API keys in mobile app code
  • CORS configured on backend proxy

Team Management

  • Team members use their own keys (no sharing)
  • Keys revoked when team members leave
  • Key management process documented
  • Security training provided to team

What to Do If Your Key Is Compromised

If you suspect your API key has been compromised, act immediately:

1. Revoke the key (5 seconds)

  • Go to Developer Portal → API Keys
  • Click the compromised key
  • Click "Revoke Key" → Confirm

2. Generate a new key (30 seconds)

  • Click "Create New API Key"
  • Use a descriptive name (e.g., "Production v2 - Rotated 2025-01-15")
  • Configure same permissions as old key
  • Copy the new key immediately

3. Update applications (5-15 minutes)

# Update environment variables
export FORTIBLOX_API_KEY="new_key_here"

# Or update secrets
aws secretsmanager update-secret --secret-id fortiblox-api-key \
  --secret-string "new_key_here"

# Restart applications
docker-compose restart
# or
kubectl rollout restart deployment/my-app

4. Review usage (10 minutes)

  • Go to Developer Portal → Analytics
  • Check recent requests for suspicious activity
  • Download usage logs if needed

5. Check billing (5 minutes)

  • Review current credit usage
  • Check for unexpected charges
  • Contact support if abuse detected

6. Contact support (if needed)

  • Email: [email protected]
  • Subject: "Compromised API Key - Urgent"
  • Include: Key ID, time of compromise, estimated impact

Act fast! The faster you revoke a compromised key, the less damage an attacker can do. Don't wait to investigate - revoke first, investigate later.

Additional Resources

Support

Need help securing your API keys?