Skip to main content

Security Guidelines

Authentication & Authorization

Firebase Authentication

ChainAlign uses Firebase Authentication for user management and JWT-based authentication.

Session Management

  • User session tokens: Expire after 1 hour
  • Refresh tokens: Automatically rotated for extended sessions
  • Frontend behavior: Automatic logout on token expiration
  • Session invalidation: All sessions invalidated on password change or account disable

For Development & Testing

Generating Test Tokens:

# Generate a test token for a specific user
node scripts/generateTestToken.mjs <uid> <email> <displayName>

# Example:
node scripts/generateTestToken.mjs test-user-123 test@example.com "Test User"

Important Notes:

  • Test tokens expire quickly (typically within 1 hour)
  • NEVER commit hardcoded tokens to Git
  • Use environment variables for any long-lived tokens
  • Always remove test tokens from code before committing

Backend API Token (Service-to-Service)

For internal backend service communication (e.g., AIManager calling internal APIs), use the BACKEND_API_TOKEN environment variable:

  1. Generate a token using the script above for a service account user
  2. Add to .env (NOT .env.example):
    BACKEND_API_TOKEN=your_generated_token_here
  3. Use in code:
    const backendApiToken = process.env.BACKEND_API_TOKEN;
    if (!backendApiToken) {
    console.error('BACKEND_API_TOKEN environment variable is not set');
    return null;
    }

Token Rotation

When to rotate tokens:

  • After any suspected security breach
  • If a token is accidentally committed to version control
  • On a regular schedule (recommended: every 90 days for service accounts)

How to rotate:

  1. Generate a new token:

    node scripts/generateTestToken.mjs <service-account-uid> <email> <name>
  2. Update the token in all environments:

    • Development: Update .env locally
    • Staging: Update environment variables in staging deployment
    • Production: Update environment variables in production deployment
  3. If a token was exposed in Git history:

    # Revoke the exposed token immediately via Firebase Console
    # - Go to Firebase Console > Authentication > Users
    # - Find the user/service account
    # - Disable the account or delete and recreate it

    # Then generate and deploy a new token
    node scripts/generateTestToken.mjs <new-uid> <email> <name>
  4. Verify the new token works in all environments before removing the old one

Multi-Tenancy Security

ChainAlign is a multi-tenant platform where data isolation is critical. Every tenant's data must be completely isolated from other tenants.

Tenant Isolation Requirements

  1. Database Queries: ALWAYS filter by tenant_id

    // CORRECT - Tenant-scoped query
    const scenarios = await db('scenarios')
    .where({ tenant_id: req.user.tenant_id })
    .select();

    // WRONG - Missing tenant_id filter (security vulnerability!)
    const scenarios = await db('scenarios').select();
  2. API Request Validation:

    // Validate that requested resource belongs to user's tenant
    const scenario = await scenariosRepository.findById(scenarioId);
    if (scenario.tenant_id !== req.user.tenant_id) {
    return res.status(403).json({ error: 'Access denied' });
    }
  3. Repository Pattern: Extend BaseRepository which enforces tenant scoping

    // BaseRepository automatically adds tenant_id to queries
    const result = await repository.findByTenantId(tenantId, filters);

IDOR Prevention

Insecure Direct Object Reference (IDOR) vulnerabilities occur when users can access resources by guessing IDs. Prevent this by:

  • Using UUIDs instead of sequential integers for primary keys
  • Always validating tenant_id matches the authenticated user
  • Never trusting client-provided IDs without validation

Testing Tenant Isolation

# Test with different tenant users
npm test -- tenant-isolation.test.js

# Manual testing checklist:
# 1. Create resource as User A (tenant 1)
# 2. Attempt to access resource as User B (tenant 2)
# 3. Verify 403 Forbidden response

Git Pre-Commit Hook

A pre-commit hook is installed at .git/hooks/pre-commit to automatically detect hardcoded secrets before commits.

What it detects:

  • JWT tokens (Firebase, Auth0, etc.)
  • Bearer tokens in Authorization headers
  • API keys and secrets
  • AWS access keys
  • Private keys (RSA, OpenSSH, etc.)

Hook output:

Success:

🔍 Scanning for hardcoded secrets...
✅ No hardcoded secrets detected

Blocked commit:

🔍 Scanning for hardcoded secrets...
✗ Hardcoded Bearer token found in: backend/src/services/AIManager.js
Pattern: Authorization: Bearer eyJ...

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
❌ COMMIT BLOCKED: Hardcoded secrets detected!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Please:
1. Remove hardcoded secrets from the files above
2. Use environment variables instead (see .env.example)
3. For test tokens, use scripts/generateTestToken.mjs
git commit --no-verify

Only bypass if:

  • You're committing example/documentation files
  • You're certain there are no real secrets (false positive)

Installing the hook on a new clone:

The hook is located at .git/hooks/pre-commit but is not tracked by Git (.git directory is never committed). To set it up on a new machine:

# Copy the pre-commit hook from the scripts directory
cp scripts/pre-commit-hook .git/hooks/pre-commit

# Make it executable
chmod +x .git/hooks/pre-commit

# Verify installation
.git/hooks/pre-commit

Note: The hook template is stored in scripts/pre-commit-hook and should be maintained in the repository for easy installation.

Environment Variables

Required Variables

See .env.example for a complete list. Key security-related variables:

# Firebase Configuration
GOOGLE_APPLICATION_CREDENTIALS=/path/to/firebase-admin-sdk-key.json

# API Keys (never commit actual values)
GEMINI_API_KEY=your_gemini_api_key_here
TYPESENSE_API_KEY=your_typesense_api_key_here

# Backend Service Token
BACKEND_API_TOKEN=your_backend_api_token_here

# Database Credentials
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_NAME=your_db_name

Best Practices

  1. Never commit .env files - They're in .gitignore for a reason
  2. Use strong, unique values for production
  3. Rotate secrets regularly (every 90 days minimum)
  4. Use different credentials for dev/staging/production
  5. Store production secrets in a secure vault (AWS Secrets Manager, GCP Secret Manager, etc.)

Reporting Security Issues

If you discover a security vulnerability:

  1. DO NOT open a public GitHub issue
  2. Email the security team directly at: [pramod.opspilot@gmail.com]
  3. Include:
    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact
    • Suggested fix (if any)

API Security

Authentication & Request Validation

All API endpoints are protected using the following security layers:

  1. JWT Authentication: Implemented in authMiddleware.js

    // All routes require valid Firebase JWT token
    router.use(authMiddleware.verifyToken);
  2. Input Validation: Using validationMiddleware.js

    // Validate request schema before processing
    router.post('/scenarios',
    validationMiddleware.validate(scenarioSchema),
    scenarioController.create
    );
  3. CORS Configuration: Restricts allowed origins

    // Only allow requests from approved domains
    const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3001'];
  4. Rate Limiting: Implemented on all endpoints

    • 100 requests per 15 minutes per IP for general endpoints
    • 10 requests per 15 minutes for authentication endpoints
    • 20 requests per hour for AI/LLM endpoints (per tenant)

Input Sanitization

SQL Injection Prevention:

  • Always use Knex.js parameterized queries (NEVER raw SQL with string concatenation)
  • All repositories extend BaseRepository with built-in sanitization
// CORRECT - Parameterized query
await db('users').where({ email: userInput });

// WRONG - SQL injection vulnerability!
await db.raw(`SELECT * FROM users WHERE email = '${userInput}'`);

XSS Prevention:

  • React automatically escapes HTML in JSX
  • For dangerouslySetInnerHTML, always use DOMPurify
  • API responses sanitize HTML content
import DOMPurify from 'dompurify';

// Safe HTML rendering
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(userContent)
}} />

File Upload Security (CSV Ingestion):

  • File type validation (must be .csv)
  • File size limits (max 50MB)
  • Virus scanning via data-validation-service
  • Content validation before processing
  • Temporary files deleted after processing
// backend/src/routes/data-onboarding.js
const upload = multer({
limits: { fileSize: 50 * 1024 * 1024 }, // 50MB
fileFilter: (req, file, cb) => {
if (file.mimetype !== 'text/csv') {
return cb(new Error('Only CSV files allowed'));
}
cb(null, true);
}
});

WebSocket Security

Real-time notifications via Socket.io are secured:

// Socket.io connection requires JWT authentication
io.use(async (socket, next) => {
const token = socket.handshake.auth.token;
const decoded = await admin.auth().verifyIdToken(token);
socket.userId = decoded.uid;
socket.tenantId = decoded.tenant_id;
next();
});

// Tenant isolation enforced on all socket events
socket.on('subscribe', (channel) => {
if (channel.startsWith(`tenant:${socket.tenantId}`)) {
socket.join(channel);
} else {
socket.emit('error', 'Access denied');
}
});

WebSocket Rate Limiting:

  • Maximum 100 messages per minute per connection
  • Automatic disconnect on excessive messages
  • Reconnection backoff (exponential)

Database Security

Connection Security

  • All database connections use SSL/TLS in production
  • Connection pooling with maximum limits (10 connections per backend instance)
  • Credentials stored in environment variables (never hardcoded)

Data Protection

  • Tenant isolation: UUID-based filtering on all queries
  • Row-level security: PostgreSQL RLS policies on sensitive tables
  • Encryption at rest: Database volumes encrypted (production only)
  • Encryption in transit: SSL enforced for all connections

Query Security

// Always use Knex query builder (prevents SQL injection)
const result = await db('scenarios')
.where({ tenant_id: req.user.tenant_id })
.whereIn('status', ['active', 'pending'])
.select();

// For complex queries, use parameterized raw queries
const result = await db.raw(
'SELECT * FROM scenarios WHERE tenant_id = ? AND created_at > ?',
[tenantId, startDate]
);

Backup & Recovery

  • Automated daily backups (production)
  • Point-in-time recovery enabled
  • Backup encryption with separate keys
  • Regular backup restoration tests

AI/LLM Security

ChainAlign uses Google Gemini for AI-powered insights. Protect against prompt injection and data leakage:

Prompt Injection Prevention

// Sanitize user input before sending to LLM
function sanitizePrompt(userInput) {
// Remove potential instruction injections
return userInput
.replace(/ignore (previous|above) instructions?/gi, '')
.replace(/system:?/gi, '')
.trim()
.substring(0, 2000); // Limit length
}

// Use structured prompts with clear boundaries
const prompt = `
Context: ${sanitizePrompt(context)}
---
User Question: ${sanitizePrompt(userQuestion)}
---
Instructions: Answer based only on the context provided.
`;

Rate Limiting AI Calls

// Limit AI API calls per tenant (prevents abuse and cost overrun)
const AI_RATE_LIMIT = {
maxCallsPerHour: 100,
maxCallsPerDay: 500,
maxTokensPerCall: 4000
};

// Track usage in Redis or database
await rateLimiter.checkLimit(tenantId, 'ai_calls');

Sensitive Data Protection

Do NOT include in embeddings or LLM prompts:

  • Passwords, API keys, or tokens
  • Social Security Numbers or personal identifiers
  • Credit card numbers or financial credentials
  • Health information (PHI/PII)
// Filter sensitive fields before embedding
const dataForEmbedding = {
name: element.name,
description: element.description,
type: element.type
// NEVER include: api_key, password, ssn, etc.
};

Content Filtering

// Validate LLM responses before returning to users
function validateLLMResponse(response) {
// Check for leaked sensitive patterns
const sensitivePatterns = [
/api[_-]?key/i,
/password/i,
/\b[A-Z0-9]{20,}\b/, // Possible tokens
/-----BEGIN.*KEY-----/
];

for (const pattern of sensitivePatterns) {
if (pattern.test(response)) {
throw new Error('LLM response contains sensitive data');
}
}

return response;
}

Reasoning Chain Audit

All AI reasoning chains are stored via ReasoningBankService for audit purposes:

// Audit reasoning chains for data leakage
await reasoningBankService.create({
tenant_id: tenantId,
user_id: userId,
prompt: sanitizedPrompt,
response: response,
model: 'gemini-pro',
timestamp: new Date()
});

Python Microservices Security

Service Authentication

Python services should NOT be exposed directly to the internet. All requests go through the Node.js backend:

# python-services//app.py
from functools import wraps

def require_backend_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
api_key = request.headers.get('X-Backend-API-Key')
if api_key != os.getenv('BACKEND_SERVICE_KEY'):
return jsonify({'error': 'Unauthorized'}), 401
return f(*args, **kwargs)
return decorated

@app.route('/ingest', methods=['POST'])
@require_backend_auth
def ingest_data():
# Process data
pass

Docker Network Isolation

# docker-compose.yml
services:
:
networks:
- backend-network # Internal network only
# NO port mapping to host (not exposed externally)

chainalign-backend:
networks:
- backend-network
ports:
- "5001:5001" # Only backend exposed

networks:
backend-network:
driver: bridge
internal: false # Backend can reach internet for AI APIs

File Upload Validation

# Validate uploaded files before processing
def validate_file(file):
# Check file size
if len(file.read()) > 50 * 1024 * 1024: # 50MB
raise ValueError('File too large')
file.seek(0)

# Check file type (magic bytes, not just extension)
file_type = magic.from_buffer(file.read(1024), mime=True)
if file_type not in ['text/csv', 'text/plain']:
raise ValueError('Invalid file type')
file.seek(0)

# Virus scan (using ClamAV or similar)
scan_result = clamd.scan_stream(file)
if scan_result['stream'][0] == 'FOUND':
raise ValueError('File contains malware')

Sandboxed Execution

For code execution or data processing:

# Use restricted execution environments
import subprocess

def run_user_code(code):
# NEVER use eval() or exec() on user input!
# Use subprocess with timeout and resource limits
result = subprocess.run(
['python', '-c', code],
timeout=5,
capture_output=True,
text=True,
# Run as restricted user
user='nobody'
)
return result.stdout

Dependency Security

# Audit npm dependencies regularly
npm audit

# Fix vulnerabilities
npm audit fix

# For breaking changes, review manually
npm audit fix --force

Docker Security

  • Base images are regularly updated
  • No secrets in Dockerfiles or docker-compose.yml
  • Use Docker secrets for production deployments

Security Logging & Monitoring

Comprehensive logging is essential for detecting and responding to security incidents.

What to Log

// Authentication events
logger.info('User login successful', {
user_id: user.uid,
tenant_id: user.tenant_id,
ip_address: req.ip,
timestamp: new Date()
});

logger.warn('Failed login attempt', {
email: req.body.email,
ip_address: req.ip,
reason: 'invalid_credentials',
timestamp: new Date()
});

// Authorization failures (potential IDOR attempts)
logger.warn('Unauthorized access attempt', {
user_id: req.user.uid,
tenant_id: req.user.tenant_id,
requested_resource: req.params.scenarioId,
resource_tenant: scenario.tenant_id,
ip_address: req.ip,
timestamp: new Date()
});

// Sensitive operations
logger.info('Sensitive operation performed', {
operation: 'scenario_deleted',
user_id: req.user.uid,
tenant_id: req.user.tenant_id,
resource_id: scenarioId,
timestamp: new Date()
});

// Rate limit violations
logger.warn('Rate limit exceeded', {
user_id: req.user?.uid,
ip_address: req.ip,
endpoint: req.path,
limit: rateLimit,
timestamp: new Date()
});

What NOT to Log

NEVER log sensitive information:

  • Passwords or password hashes
  • API keys, tokens, or secrets
  • Credit card numbers or PII
  • Full JWT tokens (log only last 4 characters)
  • Session IDs
// WRONG - Logs sensitive data
logger.info('User authenticated', { token: req.headers.authorization });

// CORRECT - Logs only metadata
logger.info('User authenticated', {
user_id: user.uid,
token_last4: req.headers.authorization?.slice(-4)
});

Monitoring & Alerts

Set up automated alerts for:

  1. Repeated failed login attempts (> 5 within 5 minutes from same IP)
  2. Authorization failures (> 10 within 1 hour from same user)
  3. Rate limit violations (persistent violations may indicate attack)
  4. Unusual API patterns (sudden spike in requests)
  5. Database query errors (may indicate SQL injection attempts)
  6. Service errors (500 errors may indicate exploitation)
// Example: Alert on repeated authorization failures
if (failedAuthCount > 10) {
await notificationService.alertSecurityTeam({
severity: 'HIGH',
type: 'repeated_authorization_failures',
user_id: userId,
ip_address: ipAddress,
count: failedAuthCount,
time_window: '1 hour'
});
}

Log Retention

  • Development: 7 days
  • Staging: 30 days
  • Production: 90 days (or as required by compliance)
  • Security logs: 1 year minimum

Incident Response Plan

If a security incident occurs, follow this procedure:

1. Identification & Containment

  • Identify the breach: Review logs, identify affected systems
  • Contain immediately:
    # Revoke compromised tokens
    # Via Firebase Console or programmatically
    await admin.auth().revokeRefreshTokens(userId);

    # Disable affected user accounts
    await admin.auth().updateUser(userId, { disabled: true });

    # Block malicious IPs in firewall
    # (depends on infrastructure - AWS Security Groups, etc.)

2. Eradication

  • Remove the threat:
    • Patch vulnerabilities
    • Remove backdoors or malicious code
    • Reset all compromised credentials
    • Update security rules

3. Recovery

  • Restore normal operations:
    # Generate new tokens
    node scripts/generateTestToken.mjs <new-uid> <email> <name>

    # Update environment variables
    # (in all environments: dev, staging, production)

    # Re-enable services
    # Verify security before going live

4. Notification

  • Internal: Notify engineering and security teams immediately
  • External: Notify affected tenants if their data was compromised
    • Within 72 hours (GDPR requirement)
    • Include: nature of breach, data affected, mitigation steps
    • Contact: security@chainalign.com (or appropriate channel)

5. Post-Mortem

Conduct a post-mortem within 1 week:

  • Root cause analysis
  • Timeline of events
  • What worked / what didn't
  • Action items to prevent recurrence
  • Update security documentation

Emergency Contacts

  • Security Team: security@chainalign.com
  • On-Call Engineer: [Slack channel or PagerDuty]
  • Infrastructure Team: [Contact info]

Compliance

ChainAlign handles sensitive business data (forecasts, demand plans, financial data). Security and compliance are critical:

Data Protection Regulations

GDPR (General Data Protection Regulation)

  • Right to access: Users can request all data stored about them
  • Right to deletion: Users can request account and data deletion
  • Right to portability: Export user data in machine-readable format
  • Breach notification: Notify users within 72 hours of data breach
  • Data minimization: Only collect necessary data
  • Consent: Explicit consent for data processing

Implementation:

// backend/src/routes/users.js

// Export user data (GDPR Article 20)
router.get('/export', authMiddleware, async (req, res) => {
const userData = await userService.exportUserData(req.user.uid);
res.json(userData);
});

// Delete user account (GDPR Article 17 - Right to be forgotten)
router.delete('/account', authMiddleware, async (req, res) => {
await userService.deleteUserAndData(req.user.uid);
res.json({ message: 'Account deleted successfully' });
});

SOC 2 (Service Organization Control 2)

Key requirements for ChainAlign:

  • Security: Access controls, encryption, firewall protection
  • Availability: System uptime, disaster recovery, incident response
  • Processing Integrity: Data accuracy, completeness, timeliness
  • Confidentiality: Tenant isolation, data encryption
  • Privacy: Data handling policies, user consent

Compliance Checklist

  • Encryption at rest: Database volumes encrypted in production
  • Encryption in transit: SSL/TLS for all connections (API, WebSocket, database)
  • Access controls: Role-based access control (RBAC) via Firebase
  • Audit logging: All sensitive operations logged with user/tenant context
  • Tenant isolation: UUID-based filtering on all queries
  • Regular security audits: Quarterly penetration testing (production)
  • Dependency scanning: Automated npm audit in CI/CD pipeline
  • Secrets management: Environment variables, never committed to Git
  • Penetration testing: Annual third-party security assessment (recommended)
  • Security training: Annual training for all engineers (recommended)
  • Bug bounty program: Consider for production launch

Data Classification

CategoryExamplesStorage RequirementsAccess Level
PublicDocumentation, marketing materialsNo encryption requiredEveryone
InternalSystem logs, non-sensitive metricsEncrypted at restAuthenticated users
ConfidentialBusiness forecasts, demand plansEncrypted at rest + in transitTenant-scoped only
RestrictedUser credentials, API keys, PIIEncrypted + access loggingAdmin only

Security Audit Log

Maintain an audit trail for:

  • User authentication events (login, logout, failed attempts)
  • Data access (viewing, downloading, exporting)
  • Data modifications (create, update, delete)
  • Permission changes (role assignments)
  • Configuration changes (system settings)
  • AI/LLM queries and responses
// Example: Audit trail for sensitive operations
await auditLogService.create({
tenant_id: req.user.tenant_id,
user_id: req.user.uid,
action: 'scenario_deleted',
resource_type: 'scenario',
resource_id: scenarioId,
ip_address: req.ip,
user_agent: req.headers['user-agent'],
timestamp: new Date()
});

Regular Security Reviews

Schedule and conduct:

  • Weekly: Review security logs for anomalies
  • Monthly: Update dependencies (npm audit fix)
  • Quarterly: Review access controls and permissions
  • Annually: Full security audit and penetration testing
  • Ongoing: Monitor CVE databases for new vulnerabilities

Security Best Practices Summary

For Developers

  1. Never commit secrets - Use .env files and environment variables
  2. Always filter by tenant_id - Multi-tenancy isolation is critical
  3. Use parameterized queries - Prevent SQL injection
  4. Validate all inputs - Use validationMiddleware.js
  5. Sanitize LLM prompts - Prevent prompt injection
  6. Log security events - Authentication, authorization failures
  7. Test tenant isolation - Write tests for cross-tenant access prevention
  8. Review dependencies - Run npm audit regularly
  9. Use HTTPS everywhere - No plain HTTP in production
  10. Implement rate limiting - Prevent abuse and DoS attacks

For DevOps/Infrastructure

  1. Rotate secrets regularly - Every 90 days minimum
  2. Enable database encryption - At rest and in transit
  3. Use Docker secrets - Not environment variables in compose files
  4. Restrict network access - Python services not exposed externally
  5. Enable audit logging - CloudWatch, Stackdriver, or equivalent
  6. Set up monitoring - Alert on security events
  7. Backup regularly - Test restoration procedures
  8. Use separate credentials - Dev, staging, production environments
  9. Enable firewall rules - Allow only necessary ports
  10. Keep systems updated - OS, Docker images, dependencies

For Product/Business

  1. Privacy policy - Clear, compliant with GDPR/CCPA
  2. Terms of service - Define data handling and security responsibilities
  3. Incident response plan - Document and communicate
  4. User education - Best practices for secure password management
  5. Security roadmap - Continuous improvement
  6. Compliance certifications - SOC 2, ISO 27001 (for enterprise customers)
  7. Third-party audits - Independent security assessments
  8. Bug bounty program - Encourage responsible disclosure
  9. Security SLAs - Commit to response times for security issues
  10. Transparency - Communicate security practices to customers

Additional Resources


Document Version: 2.0 Last Updated: 2025-10-15 Next Review: 2025-12-15