This document outlines the security measures implemented in the Swaply platform and best practices for maintaining security.
- Hashing: All passwords are hashed using bcryptjs with salt rounds
- Storage: Plain text passwords are never stored in the database
- Validation: Minimum password length enforced (6 characters)
// Password is hashed before saving
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});- Secure Generation: Using jsonwebtoken library
- Expiration: Tokens expire after configurable time (default: 7 days)
- Verification: All protected routes verify token validity
- Storage: Tokens should be stored securely on client side
Three user roles implemented:
- User: Standard user with basic permissions
- Moderator: Can moderate content
- Admin: Full system access
// Authorization middleware
export const authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
message: `User role '${req.user.role}' is not authorized`
});
}
next();
};
};- All inputs are validated before processing
- Mongoose schema validation for database operations
- Type checking and sanitization
// Example: Item validation
const itemSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
},
estimatedValue: {
type: Number,
min: 0,
}
});- Connection string stored in environment variables
- No SQL injection vulnerability (using Mongoose ORM)
- Proper indexing for performance and security
- User inputs are trimmed and sanitized
- Special characters handled properly
- Email addresses lowercase and validated
// Configurable CORS origins
app.use(cors({
origin: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:3000'],
credentials: true,
}));All important actions are logged:
- User authentication
- Item creation/modification
- Offer status changes
- Admin actions
// Automatic logging middleware
export const logActivity = (action, entityType = null) => {
return async (req, res, next) => {
// Logs successful operations
ActivityLog.create({
user: req.user?._id,
action,
entityType,
details: { method: req.method, path: req.path },
ipAddress: req.ip,
userAgent: req.get('user-agent'),
});
};
};- Sensitive information not exposed in error messages
- Stack traces hidden in production
- Proper HTTP status codes
export const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode).json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack,
});
};-
Environment Variables
# Use strong, random secrets JWT_SECRET=$(openssl rand -base64 32) # Never commit .env files echo ".env" >> .gitignore
-
HTTPS/SSL
- Always use HTTPS in production
- Obtain SSL certificates (Let's Encrypt)
- Redirect HTTP to HTTPS
-
Database
- Use MongoDB authentication
- Restrict network access
- Regular backups
- Use MongoDB Atlas for cloud deployments
-
Rate Limiting (Recommended Addition)
import rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs }); app.use('/api/', limiter);
-
Helmet.js (Recommended Addition)
import helmet from 'helmet'; app.use(helmet()); // Sets various HTTP headers for security
-
Dependency Security
# Audit dependencies regularly npm audit # Fix vulnerabilities npm audit fix
-
Code Review
- Review all code changes
- Check for security vulnerabilities
- Test authentication flows
-
Secrets Management
- Never hardcode secrets
- Use environment variables
- Rotate secrets regularly
- Rate Limiting: Not implemented yet (recommended for production)
- Input Sanitization: Basic validation, could be enhanced with express-validator
- File Uploads: Not implemented (would need additional security measures)
- Email Verification: Not implemented (recommended for production)
- Two-Factor Authentication: Not implemented (recommended for sensitive operations)
import rateLimit from 'express-rate-limit';
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: 'Too many login attempts'
});
app.use('/api/auth/login', authLimiter);import { body, validationResult } from 'express-validator';
router.post('/register',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 6 }),
body('username').trim().escape(),
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process registration
}
);import helmet from 'helmet';
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
}
}));import session from 'express-session';
import MongoStore from 'connect-mongo';
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI
}),
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // 24 hours
}
}));import mongoSanitize from 'express-mongo-sanitize';
import xss from 'xss-clean';
// Prevent NoSQL injection
app.use(mongoSanitize());
// Prevent XSS attacks
app.use(xss());- Change all default passwords
- Set strong JWT_SECRET
- Configure HTTPS/SSL
- Set proper CORS origins
- Enable MongoDB authentication
- Add rate limiting
- Add security headers (Helmet)
- Review and limit admin access
- Set up monitoring and alerts
- Configure backups
- Test authentication flows
- Audit dependencies
- Review activity logs
- Document security procedures
- Update dependencies monthly
- Review security logs weekly
- Rotate secrets quarterly
- Test backup restoration
- Review user permissions
- Monitor for suspicious activity
- Update security documentation
-
Immediate Actions
- Isolate affected systems
- Change all passwords and secrets
- Review activity logs
- Identify breach scope
-
Investigation
- Analyze attack vector
- Identify compromised data
- Document findings
-
Recovery
- Patch vulnerabilities
- Restore from clean backups
- Verify system integrity
-
Post-Incident
- Notify affected users
- Update security measures
- Document lessons learned
- Implement preventive measures
If you discover a security vulnerability:
- DO NOT create a public GitHub issue
- Email security concerns to: security@swaply.com
- Include:
- Description of vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
- User data privacy
- Right to be forgotten
- Data export capabilities
- Consent management
- Data collection transparency
- Opt-out mechanisms
- Data deletion requests
- OWASP Top 10
- Node.js Security Best Practices
- Express Security Best Practices
- MongoDB Security Checklist
Remember: Security is an ongoing process, not a one-time task. Regularly review and update security measures as the platform evolves.