This document outlines security considerations and compliance requirements for deploying the PBA CSV Script in production payment environments.
β AES-256-GCM
- Industry standard AEAD (Authenticated Encryption with Associated Data)
- Provides confidentiality AND authenticity
- 256-bit keys (32 bytes)
- 128-bit authentication tags
β PBKDF2 with SHA-256
- 100,000 iterations (configurable)
- Random 256-bit salt per encryption
- Resistance to dictionary/brute-force attacks
β Random IV Generation
- 128-bit random IV per encryption operation
- Prevents pattern analysis
-
Increase PBKDF2 iterations for higher security:
pbkdf2Iterations: 300000 // Increase from 100,000
-
Consider Argon2 alternative (more resistant to GPU attacks):
// Future: Implement Argon2 option const argon2 = require('argon2');
-
Key rotation policy:
- Rotate master keys every 90 days
- Archive old keys for decryption of old data
Required in Production:
// Force HTTPS redirect
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' &&
req.header('x-forwarded-proto') !== 'https') {
return res.redirect(301, `https://${req.header('host')}${req.url}`);
}
next();
});Configuration:
- Use TLS 1.2+
- Strong cipher suites only
- HSTS headers enabled (via Helmet.js)
// Example with Let's Encrypt (Certbot)
// sudo certbot certonly --standalone -d your-domain.com
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/your-domain/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/your-domain/fullchain.pem')
};
https.createServer(options, app).listen(443);Implement JWT for production:
const jwt = require('jsonwebtoken');
const authMiddleware = (req, res, next) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Apply to protected routes
app.get('/api/billing/address', authMiddleware, (req, res) => {
// ... endpoint code
});For federated authentication:
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback'
}, (accessToken, refreshToken, profile, done) => {
// Authenticate user
}));Enhance with stricter validation:
const { body, validationResult } = require('express-validator');
app.post('/api/billing/export-encrypted-csv', [
body('password')
.isLength({ min: 8, max: 128 })
.matches(/^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/, {
message: 'Invalid characters in password'
}),
body('fields')
.isArray({ min: 1, max: 10 })
.custom(value => {
const allowed = ['apt_unit', 'address_line_1', 'city', 'state_province', 'postal_code', 'card_number', 'expiry_date'];
return value.every(field => allowed.includes(field));
})
], (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
});// Use parameterized queries ALWAYS
const result = await db.query(
'SELECT * FROM users WHERE id = $1',
[userId] // Parameter binding
);const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('redis');
const redisClient = redis.createClient();
// Strict limit on export endpoint (password attacks)
const exportLimiter = rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'export-limit:'
}),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 requests per window
message: 'Too many export requests, try again later'
});
// Moderate limit on read endpoint
const readLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 30
});
app.post('/api/billing/export-encrypted-csv', exportLimiter, (req, res) => {
// ... endpoint
});
app.get('/api/billing/address', readLimiter, (req, res) => {
// ... endpoint
});const allowedOrigins = [
'http://localhost:3000',
'http://localhost:8080'
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('CORS not allowed'));
}
},
credentials: true,
methods: ['GET', 'POST', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
maxAge: 3600
}));const productionOrigins = [
'https://payment-portal.example.com',
'https://card-issuer.example.com'
];
// Add environment variable for dynamic configuration
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || productionOrigins;β Never Log:
- Passwords
- API keys
- Credit card numbers
- Full PII (names, addresses)
- Decrypted data
β Do Log:
- Export events with field count
- Decryption attempts (failures)
- User IDs/sessions (masked)
- IP addresses
- Timestamps
const logger = require('winston');
logger.info('Export requested', {
userId: hashedUserId,
fieldCount: fields.length,
timestamp: new Date(),
ip: req.ip
});
logger.warn('Decryption failed', {
attempt: 'invalid_password',
timestamp: new Date(),
ip: req.ip
});// Alert on suspicious patterns
const decryptionFailures = {};
app.post('/api/billing/decrypt-csv', (req, res, next) => {
const ip = req.ip;
// Track failures
if (!decryptionFailures[ip]) {
decryptionFailures[ip] = { count: 0, firstSeen: Date.now() };
}
// Alert if 10+ failures in 5 minutes
const failures = decryptionFailures[ip];
if (failures.count > 10 && Date.now() - failures.firstSeen < 5 * 60 * 1000) {
// Alert: Possible brute force attack
console.error(`ALERT: Possible brute force from ${ip}`);
}
next();
});β Safe to Store:
- Hashed passwords (bcrypt, Argon2)
- Audit logs
- Encrypted backups
- User settings
β Never Store Plaintext:
- Card numbers
- CVV codes
- Social security numbers
- Full addresses (unless necessary)
// Use connection string from environment
const connectionString = process.env.DATABASE_URL;
// Enable SSL for database connections
const client = new pg.Client({
connectionString,
ssl: { rejectUnauthorized: false }
});Already implemented via Helmet.js:
const helmet = require('helmet');
app.use(helmet());Headers Set:
- Content-Security-Policy
- X-Frame-Options (DENY)
- X-Content-Type-Options (nosniff)
- Strict-Transport-Security
- X-XSS-Protection
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const { createClient } = require('redis');
const redisClient = createClient();
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: 'strict', // CSRF protection
maxAge: 30 * 60 * 1000 // 30 minutes
}
}));Key Requirements:
- β Install and maintain firewall
- β Don't rely on vendor security
- β Protect stored cardholder data
- β Encrypt transmission
- β Maintain antivirus
- β Secure development
- β Restrict access by business need
- β Identify and authenticate access
- β Restrict physical access
- β Track/monitor access
- β Test security regularly
- β Maintain security policy
PCI-DSS Compliance Checklist:
- Use TLS 1.2+ for all data transmission
- Never store full magnetic stripe data
- Never store CVV in any form
- Implement strong authentication
- Encrypt all cardholder data at rest
- Maintain audit logs (min 1 year)
- Annual security assessments
- Penetration testing
Key Principles:
- Lawfulness: Have legal basis for processing
- Purpose limitation: Use data only as stated
- Data minimization: Collect only necessary data
- Accuracy: Keep data current
- Storage limitation: Don't keep longer than needed
- Integrity: Protect data confidentiality
- Accountability: Document compliance
Implementation:
// Privacy policy endpoint
app.get('/api/privacy-policy', (req, res) => {
res.json({
dataCollected: ['address', 'card_number'],
purpose: 'Payment processing',
retention: '90 days',
rights: ['access', 'rectification', 'erasure']
});
});
// Data export (right to portability)
app.get('/api/user/data-export', authMiddleware, async (req, res) => {
const userData = await getUserData(req.user.id);
res.json(userData);
});
// Data deletion (right to be forgotten)
app.delete('/api/user/data', authMiddleware, async (req, res) => {
await deleteUserData(req.user.id);
res.json({ message: 'Data deleted' });
});- Right to know
- Right to delete
- Right to opt-out
- Right to non-discrimination
# Dependency vulnerability scanning
npm audit
# Static code analysis
npm install -g eslint
eslint .
# Penetration testing (OWASP ZAP)
# Download: https://www.zaproxy.org/- SQL injection tests
- XSS vulnerability scan
- CSRF protection
- Authentication bypass attempts
- Rate limiting effectiveness
- Encryption validation
- SSL/TLS configuration
- CORS misconfiguration
// Monitor for potential breaches
const suspiciousActivity = async (event) => {
if (event.failedAttempts > 50) {
await notifySecurityTeam({
level: 'CRITICAL',
type: 'POSSIBLE_BREACH',
event
});
}
};- Isolate affected systems
- Assess scope of breach
- Notify affected parties
- Remediate vulnerability
- Document for compliance
- Monitor for recurrence
- HTTPS/TLS enabled
- Strong cipher suites configured
- JWT authentication implemented
- Rate limiting enabled
- Input validation comprehensive
- CORS properly configured
- Security headers set (Helmet)
- Logging configured (no sensitive data)
- Database encryption enabled
- Backups encrypted
- Audit logs maintained
- Regular vulnerability scanning
- Penetration testing completed
- Security training completed
- Incident response plan ready
- Monthly vulnerability scans
- Quarterly penetration tests
- Annual security audits
- Compliance reviews
- Dependency updates
- Log monitoring
- Access reviews
Document Version: 1.0.0
Last Updated: December 2025
Review Frequency: Quarterly