Violation of Secure Design Principles
Description
Violation of Secure Design Principles occurs when software architecture and design don't follow established security principles. This includes violating least privilege, defense in depth, fail-safe defaults, economy of mechanism, complete mediation, open design, separation of privilege, least common mechanism, and psychological acceptability. Poor design leads to systemic vulnerabilities that are difficult to fix later.
Risk
Architectural vulnerabilities pervade entire systems. Single points of failure compromise all security. Excessive privileges enable larger breaches. Missing defense layers allow easy exploitation. Complex designs hide security flaws. Poor compartmentalization allows lateral movement. Design flaws require costly rewrites to fix.
Solution
Apply security principles from design phase. Implement defense in depth. Follow principle of least privilege. Use fail-safe defaults. Keep security mechanisms simple. Verify authorization on every access. Separate critical functions. Minimize shared resources. Design for usability and security together.
Common Consequences
| Impact | Details |
|---|---|
| Security | Scope: Systemic Vulnerabilities Design flaws affect entire application. |
| Maintenance | Scope: Technical Debt Insecure design requires extensive refactoring. |
| Compliance | Scope: Audit Failures Violations of security standards and regulations. |
Example Code + Solution Code
Vulnerable Code
// VIOLATION: Least Privilege - Everything runs as admin
public class VulnerableService {
// VIOLATION: Service runs with admin credentials for everything
private DataSource adminConnection = createAdminConnection();
public List<Product> getPublicProducts() {
// Uses admin connection for read-only public query
return adminConnection.query("SELECT * FROM products WHERE public = true");
}
public void deleteUser(String userId) {
// Same admin connection
adminConnection.execute("DELETE FROM users WHERE id = ?", userId);
}
}
// VIOLATION: Defense in Depth - Single security layer
public class VulnerableSingleLayerSecurity {
public void accessResource(String userId, String resourceId) {
// VIOLATION: Only one security check
if (isAuthenticated(userId)) {
// No authorization check
// No input validation
// No audit logging
return getResource(resourceId);
}
}
}
// VIOLATION: Fail-Safe Defaults - Default to allow
public class VulnerableDefaultAllow {
public boolean checkPermission(String userId, String action) {
try {
Permission perm = permissionService.get(userId, action);
return perm.isAllowed();
} catch (Exception e) {
// VIOLATION: Defaults to allow on error
return true;
}
}
}
// VIOLATION: Economy of Mechanism - Overcomplicated security
public class VulnerableComplexSecurity {
public boolean authenticate(Request req) {
// VIOLATION: Complex, hard to audit
return checkHeader(req) &&
checkCookie(req) &&
checkSession(req) &&
checkToken(req) &&
checkCertificate(req) &&
checkIPRange(req) &&
checkTimeOfDay(req) &&
checkMoonPhase(req) && // Joke, but illustrates complexity
validateAllTheThings(req);
}
}
# VIOLATION: Complete Mediation - Not checking every access
class VulnerableIncompleteMediation:
def __init__(self):
self.verified_users = set()
def access_resource(self, user_id, resource_id):
# VIOLATION: Only checks first time
if user_id not in self.verified_users:
if self.check_authorization(user_id, resource_id):
self.verified_users.add(user_id) # Cached forever
else:
raise PermissionError()
# Subsequent accesses skip authorization
return self.get_resource(resource_id)
# VIOLATION: Separation of Privilege - Single key for everything
class VulnerableSinglePrivilege:
def __init__(self, master_key):
# VIOLATION: One key controls everything
self.master_key = master_key
def authenticate(self, key):
# Master key grants all access
return key == self.master_key
def delete_data(self, key):
if self.authenticate(key):
self.db.delete_all()
def read_secrets(self, key):
if self.authenticate(key):
return self.get_all_secrets()
# VIOLATION: Least Common Mechanism - Shared resources
class VulnerableSharedResources:
# VIOLATION: All tenants share same connection pool
shared_pool = ConnectionPool()
# VIOLATION: All tenants share same cache
shared_cache = Cache()
# VIOLATION: All tenants share same file storage
shared_storage = FileStorage('/data')
def get_tenant_data(self, tenant_id, query):
# Relies on query to isolate data - not enforced at resource level
return self.shared_pool.execute(
f"SELECT * FROM data WHERE tenant_id = '{tenant_id}'"
)
// VIOLATION: Multiple principles in web application
class VulnerableWebApp {
constructor() {
// VIOLATION: Least Privilege - Global admin context
this.db = new DatabaseConnection({ user: 'admin' });
}
// VIOLATION: Defense in Depth - Single auth check
async handleRequest(req) {
if (req.cookies.session) {
// No token validation
// No CSRF protection
// No rate limiting
return this.processRequest(req);
}
return { error: 'Unauthorized' };
}
// VIOLATION: Fail-Safe Defaults
async checkAccess(userId, resource) {
try {
const allowed = await this.permissionService.check(userId, resource);
return allowed;
} catch (error) {
// VIOLATION: Error = allow
console.error(error);
return true;
}
}
// VIOLATION: Complete Mediation - Caching auth decisions
async getResource(userId, resourceId) {
const cacheKey = `auth:${userId}`;
// VIOLATION: Cached auth decision used without refresh
if (this.cache.has(cacheKey)) {
return this.loadResource(resourceId);
}
if (await this.checkAccess(userId, resourceId)) {
// Cache forever - permissions changes not reflected
this.cache.set(cacheKey, true);
return this.loadResource(resourceId);
}
throw new Error('Forbidden');
}
}
// VIOLATION: Open Design - Security depends on secrecy
const SECRET_ADMIN_PATH = '/xK9mN2pL/admin'; // "Hidden" admin
app.get(SECRET_ADMIN_PATH, (req, res) => {
// No authentication - URL secrecy is the "security"
res.json(getAllAdminData());
});
Fixed Code
// CORRECT: Following secure design principles
public class SecureService {
// PRINCIPLE: Least Privilege - Separate connections per role
private DataSource readOnlyConnection;
private DataSource writeConnection;
private DataSource adminConnection;
// PRINCIPLE: Economy of Mechanism - Simple, auditable
public List<Product> getPublicProducts() {
// Uses minimal privilege for the operation
return readOnlyConnection.query(
"SELECT * FROM products WHERE public = true"
);
}
@RequiresRole("ADMIN")
@AuditLogged
public void deleteUser(String adminUserId, String targetUserId) {
// Uses admin connection only for admin operations
adminConnection.execute("DELETE FROM users WHERE id = ?", targetUserId);
}
}
// CORRECT: Defense in Depth - Multiple security layers
public class SecureMultiLayerSecurity {
public Object accessResource(Request request, String resourceId) {
// Layer 1: Input validation
if (!validator.isValidResourceId(resourceId)) {
throw new InvalidInputException();
}
// Layer 2: Authentication
User user = authService.authenticate(request);
if (user == null) {
throw new AuthenticationException();
}
// Layer 3: Authorization
if (!authzService.canAccess(user, resourceId)) {
throw new AuthorizationException();
}
// Layer 4: Rate limiting
if (!rateLimiter.allowRequest(user.getId())) {
throw new RateLimitException();
}
// Layer 5: Get resource
Object resource = resourceService.get(resourceId);
// Layer 6: Audit logging
auditLog.logAccess(user, resourceId);
return resource;
}
}
// CORRECT: Fail-Safe Defaults - Default to deny
public class SecureDefaultDeny {
public boolean checkPermission(String userId, String action) {
// Default is deny
boolean allowed = false;
try {
Permission perm = permissionService.get(userId, action);
if (perm != null) {
allowed = perm.isAllowed();
}
} catch (Exception e) {
// PRINCIPLE: Fail-safe - deny on error
log.error("Permission check failed, denying access", e);
allowed = false;
}
return allowed;
}
}
// CORRECT: Separation of Privilege - Multiple factors required
public class SecureSeparatedPrivilege {
public boolean authorizeHighRiskAction(
String userId,
String password,
String totpCode,
String approverUserId
) {
// PRINCIPLE: Multiple independent factors
// Factor 1: User authenticated
if (!authService.verifyPassword(userId, password)) {
return false;
}
// Factor 2: MFA verified
if (!totpService.verify(userId, totpCode)) {
return false;
}
// Factor 3: Second person approval (separation of duties)
if (!approvalService.isApproved(approverUserId, userId)) {
return false;
}
return true;
}
}
# CORRECT: Complete Mediation - Check every access
class SecureCompleteMediation:
def access_resource(self, user_id, resource_id):
# PRINCIPLE: Always verify on every request
# Never rely on cached authorization decisions
# Verify authentication
user = self.auth_service.verify_session(user_id)
if not user:
raise AuthenticationError()
# Verify authorization (fresh check, not cached)
if not self.authz_service.can_access(user, resource_id):
raise AuthorizationError()
# Get resource
resource = self.resource_service.get(resource_id)
# Audit
self.audit_log.log_access(user, resource_id)
return resource
# CORRECT: Least Common Mechanism - Isolated resources per tenant
class SecureIsolatedResources:
def __init__(self):
self.tenant_pools = {}
self.tenant_caches = {}
self.tenant_storage = {}
def get_tenant_pool(self, tenant_id):
# PRINCIPLE: Each tenant gets isolated resources
if tenant_id not in self.tenant_pools:
self.tenant_pools[tenant_id] = ConnectionPool(
database=f'tenant_{tenant_id}_db',
user=f'tenant_{tenant_id}_user'
)
return self.tenant_pools[tenant_id]
def get_tenant_data(self, tenant_id, query):
# Uses tenant-specific connection
# Database-level isolation, not just query filtering
pool = self.get_tenant_pool(tenant_id)
return pool.execute(query)
# CORRECT: Defense in Depth implementation
class SecureDefenseInDepth:
def process_request(self, request):
# Layer 1: TLS/Transport security (handled at infrastructure)
# Layer 2: Input validation
validated_input = self.validate_input(request)
# Layer 3: Authentication
user = self.authenticate(request)
# Layer 4: Authorization
self.authorize(user, validated_input.action)
# Layer 5: Business logic with validation
result = self.execute_action(user, validated_input)
# Layer 6: Output encoding
safe_output = self.encode_output(result)
# Layer 7: Audit logging
self.audit_log(user, validated_input.action, result.success)
return safe_output
// CORRECT: Following all secure design principles
class SecureWebApp {
constructor() {
// PRINCIPLE: Least Privilege - Different connections for different needs
this.readOnlyDb = new DatabaseConnection({ user: 'reader', readOnly: true });
this.writeDb = new DatabaseConnection({ user: 'writer' });
this.adminDb = new DatabaseConnection({ user: 'admin' });
}
// PRINCIPLE: Defense in Depth - Multiple security layers
async handleRequest(req) {
// Layer 1: Input validation
const validatedInput = this.validateInput(req.body);
// Layer 2: Rate limiting
if (!await this.rateLimiter.check(req.ip)) {
throw new RateLimitError();
}
// Layer 3: Authentication
const user = await this.authenticate(req);
// Layer 4: CSRF validation
if (!this.validateCsrfToken(req, user)) {
throw new CsrfError();
}
// Layer 5: Authorization
if (!await this.authorize(user, validatedInput.action)) {
throw new AuthorizationError();
}
// Layer 6: Process request
const result = await this.processRequest(user, validatedInput);
// Layer 7: Audit logging
await this.auditLog(user, validatedInput.action, result);
return result;
}
// PRINCIPLE: Fail-Safe Defaults - Deny on error
async checkAccess(userId, resource) {
try {
const allowed = await this.permissionService.check(userId, resource);
return allowed === true; // Explicit true check
} catch (error) {
// Log and deny
this.logger.error('Access check failed', { userId, resource, error });
return false; // Fail-safe: deny
}
}
// PRINCIPLE: Complete Mediation - Fresh authorization check every time
async getResource(userId, resourceId) {
// Always verify authorization (no caching of auth decisions)
if (!await this.authorize(userId, resourceId)) {
throw new ForbiddenError();
}
const resource = await this.loadResource(resourceId);
// Log access
await this.auditLog({ userId, action: 'read', resourceId });
return resource;
}
// PRINCIPLE: Separation of Privilege for high-risk operations
async deleteAllUserData(userId, password, totpCode, adminApproval) {
// Multiple factors required
const passwordValid = await this.verifyPassword(userId, password);
const totpValid = await this.verifyTotp(userId, totpCode);
const approved = await this.verifyAdminApproval(adminApproval);
if (!passwordValid || !totpValid || !approved) {
throw new InsufficientPrivilegeError();
}
return this.performDeletion(userId);
}
}
// PRINCIPLE: Open Design - Security doesn't depend on secrecy
// Admin routes are documented but require authentication
app.get('/api/admin/users',
authenticate,
requireRole('admin'),
auditLog('admin_users_list'),
adminController.listUsers
);
Exploited in the Wild
Privilege Escalation
Lack of least privilege led to massive breaches.
Single Point Failures
Missing defense in depth allowed complete compromise.
Cached Auth Bypass
Authorization caching enabled access after revocation.
Tools to test/exploit
-
Architecture review tools.
-
Threat modeling frameworks.
-
Security design checklists.
CVE Examples
-
Architectural flaws in major applications.
-
Design-level vulnerabilities.
References
-
MITRE. "CWE-657: Violation of Secure Design Principles." https://cwe.mitre.org/data/definitions/657.html
-
Saltzer and Schroeder. "The Protection of Information in Computer Systems."