Security
Overview
Security and data privacy are top priorities for SupaScan. This document outlines our security measures, data handling practices, and privacy protection policies. SupaScan is designed with privacy-first principles - we do not collect or store user personal data, only essential payment information and API keys with automatic deletion policies.
Data Privacy & Protection
What We DON'T Store
SupaScan follows strict privacy principles and does not collect or store:
- Personal Information: Names, email addresses, phone numbers, or any personal identifiers
- User Behavior Data: Browsing history, search queries, or usage patterns
- Location Data: IP addresses are anonymized and not stored
- Social Media Data: We don't collect or store social media profiles or posts
- Wallet Addresses: We don't store or track your wallet addresses for privacy
- Trading Data: Individual trading decisions or portfolio information
What We DO Store (Minimally)
We only store essential data required for service operation:
- API Keys: Encrypted API keys for service access
- Payment Information: Encrypted payment tokens for billing (stripe/paypal)
- Usage Metrics: Aggregated, anonymized usage statistics
- Service Logs: System logs without personal identifiers
Automatic Data Deletion
All stored data has automatic deletion policies:
// API Key Management
export const deleteExpiredApiKeys = async () => {
// Delete API keys that haven't been used for 90 days
await clickhouse.query(`
DELETE FROM api_keys
WHERE last_used < now() - INTERVAL 90 DAY
`);
};
// Payment Data Management
export const deleteExpiredPaymentData = async () => {
// Delete payment tokens after successful subscription completion
await paymentService.deleteExpiredTokens();
};
// Usage Data Retention
export const cleanupUsageData = async () => {
// Keep only 30 days of anonymized usage data
await clickhouse.query(`
DELETE FROM usage_metrics
WHERE timestamp < now() - INTERVAL 30 DAY
`);
};
Security Architecture
graph TB
subgraph "Privacy-First Security"
A[No Personal Data Collection] --> B[API Key Encryption]
B --> C[Payment Data Protection]
C --> D[Automatic Data Deletion]
D --> E[Anonymized Logging]
end
subgraph "Threat Protection"
F[SQL Injection] --> G[Parameterized Queries]
H[XSS Attacks] --> I[Input Sanitization]
J[DDoS] --> K[Rate Limiting]
L[Data Breach] --> M[Zero Personal Data]
end
Authentication & Authorization
API Key Management
SupaScan uses secure API key authentication with automatic expiration:
// API Key Generation
export const generateApiKey = async (userId: string) => {
const keyId = crypto.randomUUID();
const secretKey = crypto.randomBytes(32).toString('hex');
// Store encrypted key
const encryptedKey = await encrypt(secretKey);
await clickhouse.query(`
INSERT INTO api_keys (key_id, user_id, encrypted_key, created_at, expires_at)
VALUES (?, ?, ?, now(), now() + INTERVAL 1 YEAR)
`, [keyId, userId, encryptedKey]);
return `sk_${keyId}_${secretKey}`;
};
// API Key Validation
export const validateApiKey = async (apiKey: string) => {
const [prefix, keyId, secretKey] = apiKey.split('_');
if (prefix !== 'sk' || !keyId || !secretKey) {
throw new Error('Invalid API key format');
}
const storedKey = await clickhouse.query(`
SELECT encrypted_key, expires_at
FROM api_keys
WHERE key_id = ? AND expires_at > now()
`, [keyId]);
if (!storedKey) {
throw new Error('Invalid or expired API key');
}
return { keyId, valid: true };
};
Payment Data Security
Payment information is handled securely with minimal storage:
// Payment Token Management
export const processPayment = async (paymentData: PaymentData) => {
// Store only payment token, not card details
const paymentToken = await stripe.paymentMethods.create({
type: 'card',
card: paymentData.card
});
// Store only token reference
await clickhouse.query(`
INSERT INTO payment_tokens (user_id, token_id, status, created_at)
VALUES (?, ?, 'active', now())
`, [paymentData.userId, paymentToken.id]);
// Immediately delete card details from memory
delete paymentData.card;
};
// Automatic Payment Token Cleanup
export const cleanupPaymentTokens = async () => {
// Delete payment tokens after 7 days of inactivity
await clickhouse.query(`
DELETE FROM payment_tokens
WHERE last_used < now() - INTERVAL 7 DAY
`);
};
Data Isolation
No user data is stored - all queries are stateless:
// No user data storage - all queries are anonymous
export const getTokenData = async (tokenMint: string) => {
// Public blockchain data only - no user tracking
return clickhouse.query(`
SELECT token_mint, name, symbol, supply, metadata
FROM tokens
WHERE token_mint = ?
`, [tokenMint]);
};
Input Validation & Sanitization
Solana Address Validation
All Solana addresses are validated before processing:
import { PublicKey } from '@solana/web3.js';
export const validateSolanaAddress = (address: string): string => {
try {
const publicKey = new PublicKey(address);
return publicKey.toBase58();
} catch (error) {
throw new Error('Invalid Solana address format');
}
};
// Token Mint Validation
export const validateTokenMint = (mint: string): string => {
const validMint = validateSolanaAddress(mint);
// Additional validation for token mints
if (validMint.length !== 44) {
throw new Error('Invalid token mint format');
}
return validMint;
};
API Parameter Validation
export const validateApiParams = (params: any): ValidationResult => {
// Check for malicious input
const dangerousPatterns = [
/<script/i, // Script tags
/javascript:/i, // JavaScript protocol
/data:/i, // Data URLs
/vbscript:/i, // VBScript
/onload=/i, // Event handlers
/onerror=/i, // Event handlers
/sql.*injection/i, // SQL injection attempts
/union.*select/i // SQL union attacks
];
const paramString = JSON.stringify(params);
for (const pattern of dangerousPatterns) {
if (pattern.test(paramString)) {
logger.warn('Malicious input detected', { pattern, params });
return { valid: false, error: 'Invalid input detected' };
}
}
// Check parameter length
if (paramString.length > MAX_PARAM_LENGTH) {
return { valid: false, error: 'Parameter too long' };
}
return { valid: true };
};
Webhook Payload Validation
export const validateWebhookPayload = (payload: any): boolean => {
// Validate webhook URL format
if (!payload.webhook_url || !isValidUrl(payload.webhook_url)) {
throw new Error('Invalid webhook URL');
}
// Validate filters structure
if (payload.filters) {
validateFilters(payload.filters);
}
// Sanitize payload to prevent injection
return sanitizePayload(payload);
};
const isValidUrl = (url: string): boolean => {
try {
const parsedUrl = new URL(url);
return ['http:', 'https:'].includes(parsedUrl.protocol);
} catch {
return false;
}
};
SQL Injection Prevention
All ClickHouse queries use parameterized statements with additional security measures:
// ✅ Safe: Parameterized ClickHouse query
const getTokenData = async (tokenMint: string) => {
return clickhouse.query(`
SELECT token_mint, name, symbol, supply, metadata
FROM tokens
WHERE token_mint = ?
`, [tokenMint]);
};
// ✅ Safe: Complex parameterized query
const getWalletTransactions = async (walletAddress: string, limit: number) => {
return clickhouse.query(`
SELECT signature, slot, block_time, fee, success
FROM transactions
WHERE signer = ?
ORDER BY block_time DESC
LIMIT ?
`, [walletAddress, limit]);
};
// ❌ Unsafe: String concatenation (NEVER DO THIS)
// clickhouse.query(`SELECT * FROM tokens WHERE token_mint = '${mint}'`);
// ✅ Additional security: Query sanitization
export const sanitizeQuery = (query: string): string => {
// Remove dangerous SQL keywords
const dangerousKeywords = ['DROP', 'DELETE', 'UPDATE', 'INSERT', 'ALTER'];
for (const keyword of dangerousKeywords) {
if (query.toUpperCase().includes(keyword)) {
throw new Error(`Dangerous SQL keyword detected: ${keyword}`);
}
}
return query;
};
Rate Limiting & API Protection
API Key Rate Limiting
SupaScan implements tiered rate limiting based on subscription level:
// Rate limiting by API key tier
export const rateLimitMiddleware = async (req: Request, res: Response, next: NextFunction) => {
const apiKey = req.headers.authorization?.replace('Bearer ', '');
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
const keyInfo = await getApiKeyInfo(apiKey);
const rateLimit = getRateLimitForTier(keyInfo.tier);
const currentUsage = await getCurrentUsage(keyInfo.keyId);
if (currentUsage >= rateLimit.limit) {
return res.status(429).json({
error: 'Rate limit exceeded',
limit: rateLimit.limit,
resetTime: rateLimit.resetTime
});
}
// Increment usage counter
await incrementUsage(keyInfo.keyId);
next();
};
// Tier-based rate limits
const getRateLimitForTier = (tier: string) => {
const limits = {
'free': { requests: 1000, window: 'day', burst: 10 },
'pro': { requests: 100000, window: 'day', burst: 1000 },
'enterprise': { requests: -1, window: 'day', burst: -1 }
};
return limits[tier] || limits.free;
};
Solana RPC Rate Limiting
// RPC rate limiting with fallback nodes
const rpcLimiter = new RateLimiter({
tokensPerInterval: 1000,
interval: 'minute'
});
export const callSolanaRPC = async (method: string, params: any[]) => {
if (!rpcLimiter.tryRemoveTokens(1)) {
// Fallback to secondary RPC nodes
return callSecondaryRPC(method, params);
}
try {
return await primaryRPCClient.request({ method, params });
} catch (error) {
// Automatic failover to backup RPC nodes
return callBackupRPC(method, params);
}
};
// Multiple RPC endpoints for redundancy
const rpcEndpoints = [
'https://api.mainnet-beta.solana.com',
'https://solana-api.projectserum.com',
'https://rpc.ankr.com/solana'
];
Webhook Rate Limiting
// Webhook delivery rate limiting
export const webhookRateLimiter = async (webhookId: string) => {
const webhookLimit = await getWebhookRateLimit(webhookId);
const currentDeliveries = await getWebhookDeliveryCount(webhookId);
if (currentDeliveries >= webhookLimit.maxDeliveriesPerHour) {
logger.warn('Webhook rate limit exceeded', { webhookId, currentDeliveries });
return false;
}
return true;
};
Data Protection & Encryption
Zero Personal Data Storage
SupaScan is designed with privacy-first principles - we don't store personal data:
// No personal data logging - only system metrics
logger.info('API request processed', {
endpoint: req.path,
method: req.method,
timestamp: new Date().toISOString(),
// No user identification or personal data
});
// Only log aggregated, anonymized metrics
logger.info('Usage statistics', {
totalRequests: 1250,
averageResponseTime: '45ms',
errorRate: '0.1%'
// No individual user data
});
API Key Encryption
All stored API keys are encrypted with industry-standard encryption:
import { createCipher, createDecipher } from 'crypto';
// Encrypt API keys before storage
export const encryptApiKey = (apiKey: string): string => {
const cipher = createCipher('aes-256-gcm', process.env.ENCRYPTION_KEY);
let encrypted = cipher.update(apiKey, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
};
// Decrypt API keys for validation
export const decryptApiKey = (encryptedKey: string): string => {
const decipher = createDecipher('aes-256-gcm', process.env.ENCRYPTION_KEY);
let decrypted = decipher.update(encryptedKey, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
};
Payment Data Security
Payment information is handled with minimal storage and automatic cleanup:
// Store only payment tokens, never card details
export const storePaymentToken = async (userId: string, paymentToken: string) => {
const encryptedToken = encryptApiKey(paymentToken);
await clickhouse.query(`
INSERT INTO payment_tokens (user_id, encrypted_token, created_at, expires_at)
VALUES (?, ?, now(), now() + INTERVAL 30 DAY)
`, [userId, encryptedToken]);
// Schedule automatic deletion
scheduleTokenDeletion(userId, 30);
};
// Automatic payment token cleanup
export const cleanupExpiredPaymentTokens = async () => {
await clickhouse.query(`
DELETE FROM payment_tokens
WHERE expires_at < now()
`);
logger.info('Expired payment tokens cleaned up');
};
ClickHouse Security
# ClickHouse configuration security
# /etc/clickhouse-server/config.xml
<clickhouse>
<users>
<supascan>
<password_sha256_hex>encrypted_password</password_sha256_hex>
<networks>
<ip>::/0</ip>
</networks>
<access_management>1</access_management>
</supascan>
</users>
<profiles>
<default>
<max_memory_usage>10000000000</max_memory_usage>
<max_query_size>268435456</max_query_size>
<readonly>1</readonly> <!-- Read-only for security -->
</default>
</profiles>
</clickhouse>
Environment Security
# Secure environment file permissions
chmod 600 .env
chmod 600 config/secrets.env
# Never commit secrets to git
echo ".env" >> .gitignore
echo "config/secrets.env" >> .gitignore
echo "*.key" >> .gitignore
echo "*.pem" >> .gitignore
# Use secure secret management
export SUPASCAN_ENCRYPTION_KEY=$(aws secretsmanager get-secret-value --secret-id supascan/encryption-key --query SecretString --output text)
export CLICKHOUSE_PASSWORD=$(aws secretsmanager get-secret-value --secret-id supascan/clickhouse --query SecretString --output text)
Error Handling & Logging
Safe Error Messages
SupaScan implements secure error handling that doesn't expose internal details:
// API error handling
export const handleApiError = async (error: Error, req: Request, res: Response) => {
const errorId = generateErrorId();
// Log full error internally (without personal data)
logger.error('API Error', {
errorId,
error: error.message,
stack: error.stack,
endpoint: req.path,
method: req.method,
timestamp: new Date().toISOString()
// No user data or sensitive information
});
// Return generic error message to client
res.status(500).json({
error: 'Internal server error',
errorId,
timestamp: new Date().toISOString()
});
};
// ClickHouse query error handling
export const handleQueryError = async (error: Error, query: string) => {
const errorId = generateErrorId();
logger.error('Query Error', {
errorId,
error: error.message,
queryType: 'SELECT', // Don't log actual query
timestamp: new Date().toISOString()
});
throw new Error(`Query failed. Reference: ${errorId}`);
};
Anonymized Logging
All logs are anonymized and don't contain personal information:
// Secure logging middleware
export const secureLogger = (req: Request, res: Response, next: NextFunction) => {
const startTime = Date.now();
res.on('finish', () => {
const duration = Date.now() - startTime;
logger.info('API Request', {
method: req.method,
endpoint: req.path,
statusCode: res.statusCode,
duration: `${duration}ms`,
userAgent: req.get('User-Agent'),
timestamp: new Date().toISOString()
// No IP addresses, API keys, or user identifiers
});
});
next();
};
Error ID Generation
import { randomBytes } from 'crypto';
// Generate unique error IDs for tracking
export const generateErrorId = (): string => {
return randomBytes(8).toString('hex').toUpperCase();
};
// Error tracking without personal data
export const trackError = (errorId: string, error: Error, context: any) => {
logger.error('Error Tracked', {
errorId,
error: error.message,
context: sanitizeContext(context),
timestamp: new Date().toISOString()
});
};
// Sanitize context to remove personal data
const sanitizeContext = (context: any): any => {
const sanitized = { ...context };
// Remove potentially sensitive fields
delete sanitized.apiKey;
delete sanitized.userId;
delete sanitized.email;
delete sanitized.phone;
delete sanitized.ip;
return sanitized;
};
Audit Logging & Monitoring
System Audit Logging
SupaScan maintains comprehensive audit logs without storing personal data:
// System event logging
export const logSystemEvent = (
eventType: string,
details: any
) => {
logger.info('System Event', {
eventType,
details: sanitizeDetails(details),
timestamp: new Date().toISOString(),
serverId: process.env.SERVER_ID
// No user identification
});
};
// API usage logging (anonymized)
export const logApiUsage = (
endpoint: string,
method: string,
responseTime: number,
statusCode: number
) => {
logger.info('API Usage', {
endpoint,
method,
responseTime: `${responseTime}ms`,
statusCode,
timestamp: new Date().toISOString()
// No user data or API keys
});
};
Security Event Monitoring
// Security event logging
export const logSecurityEvent = (
eventType: string,
severity: 'low' | 'medium' | 'high' | 'critical',
details: any
) => {
logger.warn('Security Event', {
eventType,
severity,
details: sanitizeDetails(details),
timestamp: new Date().toISOString(),
serverId: process.env.SERVER_ID
});
// Alert security team for critical events
if (severity === 'critical') {
alertSecurityTeam(eventType, details);
}
};
// Rate limit monitoring
export const logRateLimitExceeded = (
endpoint: string,
limit: number,
current: number
) => {
logSecurityEvent('rate_limit_exceeded', 'medium', {
endpoint,
limit,
current
});
};
// Failed authentication attempts
export const logFailedAuth = (
endpoint: string,
reason: string
) => {
logSecurityEvent('failed_authentication', 'high', {
endpoint,
reason
});
};
Data Access Logging
// Log data access without personal information
export const logDataAccess = (
dataType: string,
operation: string,
success: boolean
) => {
logger.info('Data Access', {
dataType,
operation,
success,
timestamp: new Date().toISOString()
// No user identification or sensitive data
});
};
// ClickHouse query logging
export const logQueryExecution = (
queryType: string,
duration: number,
success: boolean,
rowsReturned?: number
) => {
logger.info('Query Execution', {
queryType,
duration: `${duration}ms`,
success,
rowsReturned,
timestamp: new Date().toISOString()
});
};
Deployment Security
Process Security
# Run SupaScan as non-root user
useradd -r -s /bin/false supascan
chown -R supascan:supascan /opt/supascan
# Systemd security options
[Service]
Type=simple
User=supascan
Group=supascan
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/supascan/logs
Restart=always
RestartSec=5
Network Security
# SupaScan network security configuration
# Only allow necessary outbound connections
# HTTPS for API calls
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT
# HTTP for health checks
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
# Solana RPC endpoints
iptables -A OUTPUT -p tcp --dport 8899 -j ACCEPT
# ClickHouse connection
iptables -A OUTPUT -p tcp --dport 9000 -j ACCEPT
# Redis connection
iptables -A OUTPUT -p tcp --dport 6379 -j ACCEPT
# Block all other outbound traffic
iptables -A OUTPUT -j DROP
# No inbound ports needed for SupaScan
iptables -A INPUT -j DROP
Secret Management
# Use AWS Secrets Manager for production
export SUPASCAN_ENCRYPTION_KEY=$(aws secretsmanager get-secret-value --secret-id supascan/encryption-key --query SecretString --output text)
export CLICKHOUSE_PASSWORD=$(aws secretsmanager get-secret-value --secret-id supascan/clickhouse --query SecretString --output text)
export STRIPE_SECRET_KEY=$(aws secretsmanager get-secret-value --secret-id supascan/stripe --query SecretString --output text)
# Or use environment variables for development
export SUPASCAN_ENCRYPTION_KEY="your-encryption-key"
export CLICKHOUSE_PASSWORD="your-clickhouse-password"
export STRIPE_SECRET_KEY="your-stripe-secret"
SSL/TLS Configuration
# Nginx SSL configuration for SupaScan API
server {
listen 443 ssl http2;
server_name api.supascan.com;
ssl_certificate /etc/ssl/certs/supascan.crt;
ssl_certificate_key /etc/ssl/private/supascan.key;
# Strong SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Privacy & Security Compliance
Data Privacy Compliance
SupaScan is designed to meet the highest privacy standards:
- GDPR Compliance: No personal data collection or processing
- CCPA Compliance: No sale or sharing of personal information
- SOC 2 Type II: Comprehensive security controls and monitoring
- Zero-Knowledge Architecture: We don't know who our users are
Data Retention Policies
// Automatic data cleanup policies
export const dataRetentionPolicies = {
apiKeys: {
inactive: '90 days',
expired: 'immediate',
deleted: 'permanent'
},
paymentTokens: {
unused: '7 days',
expired: 'immediate',
deleted: 'permanent'
},
usageMetrics: {
retention: '30 days',
anonymized: true,
aggregated: true
},
logs: {
retention: '90 days',
personalData: 'none',
anonymized: true
}
};
Security Checklist
Pre-Deployment
- All environment variables set correctly
-
.envfile has correct permissions (600) - ClickHouse configured with proper security
- No secrets in code or logs
- SSL/TLS certificates configured
- Rate limiting enabled
- Encryption keys properly managed
Post-Deployment
- Monitor logs for security events
- Regular security updates
- Backup encryption keys securely
- Review API key usage patterns
- Check for unusual traffic patterns
- Verify data deletion policies
Regular Audits
- Review code for vulnerabilities
- Update dependencies
- Check for exposed secrets
- Test rate limiting
- Verify zero personal data storage
- Audit data deletion compliance
- Review encryption key rotation
Incident Response
Security Incident Procedure
- Detect: Monitor logs for anomalies and security events
- Contain: Isolate affected systems if necessary
- Investigate: Analyze logs and system state (without personal data)
- Remediate: Fix vulnerability and update security measures
- Document: Record incident details and lessons learned
- Prevent: Update security measures and monitoring
Data Breach Response
In the unlikely event of a data breach:
- Assessment: Determine scope (no personal data to be compromised)
- Notification: Inform users of any API key exposure
- Mitigation: Immediately revoke and regenerate affected API keys
- Analysis: Review logs to understand breach vector
- Prevention: Update security measures to prevent recurrence
Emergency Contacts
- SupaScan Security Team: security@supascan.com
- System Administrator: admin@supascan.com
- Hosting Provider: Configure in deployment
- Law Enforcement: If criminal activity suspected
Best Practices
Privacy-First Design
- Zero Personal Data: Never collect or store personal information
- Minimal Data Collection: Only store essential operational data
- Automatic Deletion: All data has expiration dates
- Anonymized Logging: No user identification in logs
Security Principles
- Defense in Depth: Multiple security layers
- Fail Secure: Default to denying access
- Least Privilege: Minimum necessary permissions
- Regular Updates: Keep all dependencies current
- Continuous Monitoring: 24/7 security monitoring
- Encryption Everywhere: All data encrypted in transit and at rest
Operational Security
- Secure Development: Security-first coding practices
- Regular Audits: Quarterly security assessments
- Staff Training: Security awareness for all team members
- Incident Drills: Regular security incident simulations
- Backup Security: Encrypted backups with secure storage
- Access Control: Strict access management and monitoring
SupaScan Security - Privacy-first, security-focused blockchain analytics platform with zero personal data storage and comprehensive protection measures.