Security Best Practices
Secure your Yellow Apps with proper authentication, robust key management, and proactive monitoring.
Authentication & Authorization
Wallet Integration Security
class WalletSecurity {
constructor() {
this.authorizedSessions = new Map();
this.sessionTimeout = 30 * 60 * 1000; // 30 minutes
}
async authenticateUser(walletAddress) {
// Verify wallet connection
if (!window.ethereum) {
throw new Error('No wallet provider found');
}
// Request account access
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (!accounts.includes(walletAddress)) {
throw new SecurityError('Wallet not authorized');
}
// Create session
const sessionId = this.generateSessionId();
this.authorizedSessions.set(sessionId, {
address: walletAddress,
createdAt: Date.now(),
lastActivity: Date.now()
});
// Auto-expire session
setTimeout(() => {
this.authorizedSessions.delete(sessionId);
}, this.sessionTimeout);
return sessionId;
}
validateSession(sessionId) {
const session = this.authorizedSessions.get(sessionId);
if (!session) {
throw new SecurityError('Invalid or expired session');
}
// Update last activity
session.lastActivity = Date.now();
return session;
}
}
Environment Configuration
class SecureConfig {
constructor() {
this.requiredVars = [
'CLEARNODE_ENDPOINT',
'CUSTODY_ADDRESS',
'ADJUDICATOR_ADDRESS'
];
}
validateEnvironment() {
// Check required environment variables
for (const varName of this.requiredVars) {
if (!process.env[varName]) {
throw new ConfigError(`Missing required environment variable: ${varName}`);
}
}
// Validate addresses
if (!this.isValidAddress(process.env.CUSTODY_ADDRESS)) {
throw new ConfigError('Invalid custody contract address');
}
return {
clearNodeEndpoint: process.env.CLEARNODE_ENDPOINT,
custodyAddress: process.env.CUSTODY_ADDRESS,
adjudicatorAddress: process.env.ADJUDICATOR_ADDRESS,
environment: process.env.NODE_ENV || 'development'
};
}
isValidAddress(address) {
return /^0x[a-fA-F0-9]{40}$/.test(address);
}
}
Input Validation & Sanitization
User Input Security
class InputValidator {
validateChannelParams(params) {
return {
participants: this.validateAddresses(params.participants),
amount: this.validateAmount(params.amount),
sessionData: this.sanitizeSessionData(params.sessionData)
};
}
validateAddresses(addresses) {
if (!Array.isArray(addresses) || addresses.length < 2) {
throw new ValidationError('Invalid participants array');
}
return addresses.map(addr => {
if (!/^0x[a-fA-F0-9]{40}$/.test(addr)) {
throw new ValidationError(`Invalid address: ${addr}`);
}
return addr.toLowerCase();
});
}
validateAmount(amount) {
const bigIntValue = BigInt(amount);
if (bigIntValue <= 0n) {
throw new ValidationError('Amount must be positive');
}
if (bigIntValue > BigInt('1000000000000000000000')) { // 1000 tokens max
throw new ValidationError('Amount exceeds maximum limit');
}
return bigIntValue;
}
sanitizeSessionData(data) {
if (!data) return '';
// Remove potentially malicious content
const sanitized = data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
// Validate size limits
if (sanitized.length > 64 * 1024) { // 64KB limit
throw new ValidationError('Session data too large');
}
return sanitized;
}
}
Error Handling
Secure Error Responses
class SecureErrorHandler {
constructor() {
this.errorLogs = new Map();
}
handleError(error, context) {
// Log detailed error internally
this.logError(error, context);
// Return sanitized error to user
return this.sanitizeError(error);
}
sanitizeError(error) {
// Remove sensitive information from error messages
const safeErrors = {
'ValidationError': 'Invalid input parameters',
'SecurityError': 'Authentication failed',
'NetworkError': 'Connection issue',
'TimeoutError': 'Request timed out'
};
return {
message: safeErrors[error.constructor.name] || 'An error occurred',
code: this.getErrorCode(error),
timestamp: Date.now()
};
}
logError(error, context) {
const errorEntry = {
message: error.message,
stack: error.stack,
context,
timestamp: Date.now(),
severity: this.getErrorSeverity(error)
};
// Store securely (never expose to client)
this.errorLogs.set(this.generateErrorId(), errorEntry);
}
}
Rate Limiting & Protection
DDoS Protection
class RateLimiter {
constructor() {
this.requestCounts = new Map();
this.limits = {
perMinute: 100,
perHour: 1000,
perDay: 10000
};
}
async checkRateLimit(userAddress, action) {
const key = `${userAddress}:${action}`;
const now = Date.now();
// Clean old entries
this.cleanupOldEntries(now);
const userRequests = this.requestCounts.get(key) || [];
// Check minute limit
const minuteCount = userRequests.filter(t => now - t < 60000).length;
if (minuteCount >= this.limits.perMinute) {
throw new SecurityError('Rate limit exceeded');
}
// Record request
userRequests.push(now);
this.requestCounts.set(key, userRequests);
return true;
}
cleanupOldEntries(now) {
const dayAgo = now - 24 * 60 * 60 * 1000;
for (const [key, requests] of this.requestCounts) {
const filtered = requests.filter(t => t > dayAgo);
if (filtered.length === 0) {
this.requestCounts.delete(key);
} else {
this.requestCounts.set(key, filtered);
}
}
}
}
Monitoring & Logging
Application Monitoring
class SecurityMonitor {
constructor() {
this.metrics = new Map();
this.alerts = new Set();
}
trackUserActivity(userAddress, action, details) {
const key = `${userAddress}:${action}`;
const activity = {
timestamp: Date.now(),
action,
details,
userAgent: details.userAgent,
ipAddress: details.ipAddress
};
if (!this.metrics.has(key)) {
this.metrics.set(key, []);
}
this.metrics.get(key).push(activity);
// Check for suspicious patterns
this.detectSuspiciousActivity(userAddress, action);
}
detectSuspiciousActivity(userAddress, action) {
const activities = this.metrics.get(`${userAddress}:${action}`) || [];
const recentActivities = activities.filter(a =>
Date.now() - a.timestamp < 5 * 60 * 1000 // Last 5 minutes
);
// Flag rapid consecutive actions
if (recentActivities.length > 50) {
this.alerts.add({
type: 'RAPID_ACTIVITY',
userAddress,
action,
count: recentActivities.length,
timestamp: Date.now()
});
}
}
getSecurityMetrics() {
return {
totalUsers: this.metrics.size,
activeAlerts: this.alerts.size,
recentActivity: this.getRecentActivityCount(),
timestamp: Date.now()
};
}
}
Security Testing
Application Security Tests
describe('Yellow App Security', () => {
describe('Authentication', () => {
it('should reject unauthorized wallet connections', async () => {
const unauthorizedWallet = '0x1234567890123456789012345678901234567890';
await expect(
app.authenticateUser(unauthorizedWallet)
).rejects.toThrow('Wallet not authorized');
});
it('should expire sessions after timeout', async () => {
const session = await app.createSession(validWallet);
// Fast-forward time
jest.advanceTimersByTime(31 * 60 * 1000); // 31 minutes
expect(app.validateSession(session.id)).toThrow('Invalid or expired session');
});
});
describe('Input Validation', () => {
it('should reject invalid amounts', async () => {
await expect(
app.createPayment(-100n, recipient)
).rejects.toThrow('Amount must be positive');
});
it('should sanitize malicious session data', async () => {
const maliciousData = '<script>alert("xss")</script>normal content';
const sanitized = app.sanitizeSessionData(maliciousData);
expect(sanitized).toBe('normal content');
expect(sanitized).not.toContain('<script>');
});
});
describe('Rate Limiting', () => {
it('should block excessive requests', async () => {
// Make 100 requests quickly
for (let i = 0; i < 100; i++) {
await app.makeRequest(userAddress);
}
// 101st request should be blocked
await expect(
app.makeRequest(userAddress)
).rejects.toThrow('Rate limit exceeded');
});
});
});
Security Checklist
Development Phase
- Input Validation: All user inputs sanitized and validated
- Wallet Authentication: Proper wallet connection and session management
- Rate Limiting: Protection against excessive requests
- Error Handling: Secure error messages (no information leakage)
- Session Management: Proper timeout and cleanup
- Connection Security: Secure WebSocket connections only
Pre-Production
- Security Testing: Application security tests passing
- Dependency Audit: All dependencies scanned for vulnerabilities
- Code Review: Peer review with security focus
- Environment Config: Secure configuration management
- Monitoring Setup: Activity tracking and alerting configured
Production
- Real-time Monitoring: Application activity monitoring active
- Incident Response: Security procedures documented and tested
- Backup Security: Secure backup and recovery procedures
- User Education: Security guidelines provided to users
- Regular Updates: Security update procedures established
Focus on application-level security practices that protect your Yellow App users and maintain trust in your service.