Security Guide¶
Security considerations and best practices for Morphium applications in MongoDB Community Edition environments.
Security Model Overview¶
Morphium is built primarily for MongoDB Community Edition, which has specific security limitations compared to MongoDB Enterprise. Understanding these constraints is essential for proper security planning.
Community Edition Limitations¶
No SSL/TLS Support: - MongoDB Community Edition does not support SSL/TLS encryption - Morphium's wire protocol driver does not implement SSL/TLS - All network communication is unencrypted
Basic Authentication Only:
- Only username/password authentication supported
- No support for:
- LDAP authentication
- Kerberos authentication
- x.509 certificate authentication
- SCRAM-SHA-256 (depends on MongoDB version)
Limited Authorization: - Basic role-based access control (RBAC) - No advanced security features like: - Auditing - Field-level security - Encryption at rest (without additional tools)
Network Security¶
Trusted Network Requirement¶
Since SSL/TLS is not available, Morphium must be deployed in trusted network environments:
// Configure for trusted network deployment
MorphiumConfig cfg = new MorphiumConfig();
// Internal network hosts only
cfg.clusterSettings().addHostToSeed("internal-mongo1.company.local", 27017);
cfg.clusterSettings().addHostToSeed("internal-mongo2.company.local", 27017);
cfg.clusterSettings().addHostToSeed("internal-mongo3.company.local", 27017);
// Use internal DNS names, not public IPs
Network Security Best Practices¶
1. Network Isolation:
# MongoDB should only be accessible from application networks
# Use firewall rules to restrict access
# Allow only application servers
iptables -A INPUT -p tcp --dport 27017 -s 10.0.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 27017 -j DROP
# Or use cloud security groups
# AWS: Restrict to application security groups only
# Azure: Use NSG rules for internal access only
# GCP: Configure VPC firewall rules
2. VPN/Private Networks:
# Deploy MongoDB and applications in:
# - Private VPC/VNet subnets
# - Corporate VPN networks
# - Dedicated private networks
# - Container overlay networks (Docker, Kubernetes)
3. Network Monitoring:
// Monitor for unusual connection attempts
@Component
public class SecurityMonitor {
@EventListener
public void onConnectionError(ConnectionErrorEvent event) {
// Log failed connection attempts for security analysis
securityLogger.warn("Connection attempt failed from: {} to: {}",
event.getSourceIP(), event.getTargetHost());
}
}
Authentication and Authorization¶
Basic Authentication Configuration¶
Application Authentication:
MorphiumConfig cfg = new MorphiumConfig();
// Use environment variables for credentials
cfg.authSettings().setMongoLogin(System.getenv("MONGO_USERNAME"));
cfg.authSettings().setMongoPassword(System.getenv("MONGO_PASSWORD"));
// For replica set operations (if needed)
cfg.authSettings().setMongoAdminUser(System.getenv("MONGO_ADMIN_USER"));
cfg.authSettings().setMongoAdminPwd(System.getenv("MONGO_ADMIN_PWD"));
Never hardcode credentials:
// ❌ NEVER DO THIS
cfg.authSettings().setMongoLogin("hardcoded_user");
cfg.authSettings().setMongoPassword("hardcoded_password");
// ✅ USE ENVIRONMENT VARIABLES OR SECURE VAULTS
cfg.authSettings().setMongoLogin(System.getenv("MONGO_USERNAME"));
cfg.authSettings().setMongoPassword(getFromSecureVault("mongo.password"));
MongoDB User Management¶
Create Application-Specific Users:
// In MongoDB shell - create dedicated application user
use myapp_prod
// Application user with minimal required permissions
db.createUser({
user: "myapp_user",
pwd: "secure_random_password",
roles: [
{ role: "readWrite", db: "myapp_prod" },
{ role: "read", db: "myapp_config" }
]
})
// Admin user for replica set operations (if needed)
use admin
db.createUser({
user: "myapp_admin",
pwd: "admin_secure_password",
roles: [
{ role: "read", db: "local" },
{ role: "clusterMonitor", db: "admin" }
]
})
Role-Based Access Control:
// Custom role for specific application needs
use admin
db.createRole({
role: "myAppRole",
privileges: [
{
resource: { db: "myapp_prod", collection: "" },
actions: ["find", "insert", "update", "remove"]
},
{
resource: { db: "myapp_prod", collection: "sensitive_collection" },
actions: ["find"] // Read-only for sensitive data
}
],
roles: []
})
// Assign custom role to user
use myapp_prod
db.grantRolesToUser("myapp_user", ["myAppRole"])
Data Protection¶
Application-Level Encryption¶
Since MongoDB Community Edition lacks encryption at rest, implement application-level encryption:
// Field-level encryption using Morphium's encryption support
@Entity
public class SensitiveData {
@Id
private MorphiumId id;
@Encrypted // Encrypted before storing to MongoDB
private String socialSecurityNumber;
@Encrypted
private String creditCardNumber;
private String publicData; // Not encrypted
}
// Configure encryption provider
cfg.encryptionSettings().setEncryptionKeyProvider(new AESEncryptionProvider());
cfg.encryptionSettings().setEncryptionKey(getEncryptionKey());
Custom Encryption Implementation:
@Component
public class DataEncryption {
private final AESUtil aes;
public String encryptSensitiveField(String plainText) {
if (plainText == null) return null;
return aes.encrypt(plainText, getFieldEncryptionKey());
}
public String decryptSensitiveField(String encryptedText) {
if (encryptedText == null) return null;
return aes.decrypt(encryptedText, getFieldEncryptionKey());
}
private String getFieldEncryptionKey() {
// Get from secure key management system
return keyManager.getKey("field_encryption_key");
}
}
Sensitive Data Handling¶
PII Data Protection:
@Entity
public class UserProfile {
@Id
private MorphiumId id;
@Encrypted
private String email;
@Encrypted
private String phoneNumber;
// Hash instead of storing directly
private String passwordHash;
// Tokenize sensitive IDs
private String userToken; // Use instead of actual user ID externally
}
// Implement data masking for logs
@Override
public String toString() {
return "UserProfile{" +
"id=" + id +
", email='" + maskEmail(email) + '\'' +
", phoneNumber='" + maskPhone(phoneNumber) + '\'' +
'}';
}
Secure Configuration Management¶
Environment-Based Configuration¶
Development Environment:
// development.properties
database=myapp_dev
hosts=localhost:27017
mongo_login=dev_user
mongo_password=dev_password
log_level=5
Production Environment:
// production.properties - minimal logging
database=myapp_prod
hosts=prod-mongo1:27017,prod-mongo2:27017,prod-mongo3:27017
mongo_login=${MONGO_USERNAME}
mongo_password=${MONGO_PASSWORD}
log_level=3
Container Security:
# Use non-root user
RUN adduser --disabled-password --gecos '' appuser
USER appuser
# Use secrets for sensitive data
# Pass via environment variables from orchestrator
ENV MONGO_USERNAME=""
ENV MONGO_PASSWORD=""
# Don't include credentials in image
COPY --chown=appuser:appuser app.jar /app/app.jar
Kubernetes Secrets:
apiVersion: v1
kind: Secret
metadata:
name: mongo-credentials
type: Opaque
data:
username: bXlhcHBfdXNlcg== # base64 encoded
password: c2VjdXJlX3Bhc3N3b3Jk # base64 encoded
---
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: myapp
env:
- name: MONGO_USERNAME
valueFrom:
secretKeyRef:
name: mongo-credentials
key: username
- name: MONGO_PASSWORD
valueFrom:
secretKeyRef:
name: mongo-credentials
key: password
Audit and Monitoring¶
Security Event Logging¶
Implement comprehensive security logging:
@Component
public class SecurityAuditLogger {
private final Logger securityLog = LoggerFactory.getLogger("SECURITY");
public void logAuthenticationAttempt(String username, boolean success, String sourceIP) {
securityLog.info("AUTH_ATTEMPT user={} success={} source={}",
username, success, sourceIP);
}
public void logDataAccess(String username, String collection, String operation) {
securityLog.info("DATA_ACCESS user={} collection={} operation={}",
username, collection, operation);
}
public void logSecurityViolation(String violation, String details) {
securityLog.warn("SECURITY_VIOLATION type={} details={}", violation, details);
}
}
Structured Security Logging:
// Use structured logging for security events
@Component
public class StructuredSecurityLogger {
private final ObjectMapper objectMapper = new ObjectMapper();
public void logSecurityEvent(SecurityEvent event) {
try {
String jsonEvent = objectMapper.writeValueAsString(event);
securityLogger.info("SECURITY_EVENT: {}", jsonEvent);
} catch (Exception e) {
securityLogger.error("Failed to log security event", e);
}
}
}
public class SecurityEvent {
private String eventType;
private String username;
private String sourceIP;
private String resource;
private String operation;
private boolean success;
private LocalDateTime timestamp;
// ... getters/setters
}
Monitoring Security Metrics¶
Track security-related metrics:
@Component
public class SecurityMetrics {
private final MeterRegistry meterRegistry;
// Count authentication failures
private final Counter authFailures = Counter.builder("morphium.auth.failures")
.register(meterRegistry);
// Count data access by type
private final Counter dataAccess = Counter.builder("morphium.data.access")
.tag("operation", "read")
.register(meterRegistry);
public void recordAuthFailure(String reason) {
authFailures.increment(Tags.of("reason", reason));
}
public void recordDataAccess(String operation, String collection) {
dataAccess.increment(Tags.of("operation", operation, "collection", collection));
}
}
Input Validation and Injection Prevention¶
Query Injection Prevention¶
Use Morphium's type-safe query API:
// ✅ SAFE - Uses parameterized queries
Query<User> safeQuery = morphium.createQueryFor(User.class)
.f("username").eq(userInput) // Automatically escaped/parameterized
.f("status").eq("active");
// ❌ DANGEROUS - Raw query construction
Query<User> unsafeQuery = morphium.createQueryFor(User.class)
.complexQuery(Doc.of("username", userInput)); // Could allow injection
Input validation:
@Component
public class InputValidator {
public void validateUsername(String username) {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("Username cannot be empty");
}
if (username.length() > 50) {
throw new IllegalArgumentException("Username too long");
}
if (!username.matches("^[a-zA-Z0-9_-]+$")) {
throw new IllegalArgumentException("Username contains invalid characters");
}
}
public void validateEmail(String email) {
if (email == null || !email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$")) {
throw new IllegalArgumentException("Invalid email format");
}
}
}
Secure Development Practices¶
Code Security Review Checklist¶
Configuration Security: - [ ] No hardcoded credentials in source code - [ ] Environment variables used for sensitive data - [ ] Minimal database permissions granted - [ ] Network access properly restricted
Data Protection: - [ ] Sensitive fields encrypted at application level - [ ] PII data properly masked in logs - [ ] Input validation implemented - [ ] Output encoding applied where needed
Authentication/Authorization: - [ ] Strong password policies enforced - [ ] User roles properly defined and assigned - [ ] Session management secure - [ ] Access controls verified
Logging and Monitoring: - [ ] Security events properly logged - [ ] Sensitive data not logged - [ ] Log files properly secured - [ ] Monitoring alerts configured
Security Testing¶
Automated Security Testing:
@Test
public class SecurityTests {
@Test
public void testNoCredentialsInLogs() {
// Verify no passwords appear in log files
String logContent = readLogFile();
assertFalse("Password leaked in logs",
logContent.contains("password"));
}
@Test
public void testInputValidation() {
// Test SQL injection attempts
assertThrows(IllegalArgumentException.class, () -> {
userService.findUser("'; DROP TABLE users; --");
});
}
@Test
public void testEncryption() {
// Verify sensitive data is encrypted
SensitiveData data = new SensitiveData();
data.setSocialSecurityNumber("123-45-6789");
morphium.store(data);
// Verify stored data is encrypted (not plaintext)
Doc stored = morphium.getDatabase()
.getCollection("sensitive_data")
.find(Doc.of("_id", data.getId()))
.first();
assertNotEquals("123-45-6789", stored.getString("socialSecurityNumber"));
}
}
Security Incident Response¶
Incident Response Plan¶
1. Immediate Response: - Identify compromised accounts/systems - Revoke compromised credentials immediately - Block suspicious network traffic - Isolate affected systems
2. Investigation: - Analyze security logs for attack patterns - Identify data that may have been accessed - Determine attack vector and timeline - Document all findings
3. Recovery: - Patch security vulnerabilities - Update all credentials - Restore from clean backups if needed - Implement additional security measures
4. Post-Incident:
- Update security procedures
- Enhance monitoring and alerting
- Conduct security training
- Review and test incident response plan
This security guide provides comprehensive coverage of security considerations for Morphium applications in MongoDB Community Edition environments, focusing on network security, authentication, data protection, and secure development practices.