Data Access from Outside Expected Data Manager Component
Description
Data Access from Outside Expected Data Manager Component occurs when a product performs data access operations without using its intended data manager component (such as a repository layer, ORM, or data access layer). When the architecture expects all database operations to flow through a designated data management component, but some code bypasses this structure and accesses data directly, it creates architectural violations that undermine maintainability, security controls, and data integrity.
Risk
Bypassing the designated data access layer has significant security implications. Security controls implemented in the data layer (audit logging, access control, input validation) are bypassed. Sensitive queries may not use parameterization, enabling SQL injection. Caching and optimization strategies are circumvented. Data validation and business rules enforcement is skipped. Audit trails have gaps where direct access occurred. Connection management and pooling may be bypassed, causing resource exhaustion. The inconsistent data access patterns make security auditing more difficult.
Solution
Enforce architectural boundaries through code review and static analysis. Use dependency injection to provide data access components. Make database connection details private to the data layer. Apply the Repository pattern to centralize data access. Use linting rules to detect direct database imports outside allowed modules. Implement architecture fitness functions to verify layer boundaries. Use access modifiers and module boundaries to prevent direct database access. Consider database access policies that restrict which components can connect. Document the expected architecture and train developers on patterns.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Reduce Maintainability - Scattered data access makes the system harder to understand and modify. |
| Integrity | Scope: Integrity Bypass Protection Mechanism - Security controls in the data layer are circumvented. |
| Other | Scope: Other Reduce Reliability - Inconsistent data access can cause data integrity issues. |
Example Code
Vulnerable Code
// Vulnerable: Direct database access bypassing the repository layer
// Expected architecture:
// Controller -> Service -> Repository -> Database
//
// Vulnerable code accesses database directly from service or controller
// The proper repository (should be used)
@Repository
public class UserRepository {
@PersistenceContext
private EntityManager em;
public User findById(Long id) {
// Proper: Uses parameterized query
// Proper: Audit logging here
// Proper: Access control checks here
auditLog.log("User lookup: " + id);
return em.find(User.class, id);
}
public List<User> search(String criteria) {
// Proper: Input validation and sanitization
validateSearchCriteria(criteria);
return em.createQuery("SELECT u FROM User u WHERE u.name LIKE :criteria")
.setParameter("criteria", "%" + criteria + "%")
.getResultList();
}
}
// Vulnerable: Service bypasses repository
@Service
public class VulnerableUserService {
@Autowired
private UserRepository userRepository; // Proper dependency
// Vulnerable: Direct JDBC access bypassing repository!
@Autowired
private DataSource dataSource;
public User getUser(Long id) {
// Proper: Uses repository
return userRepository.findById(id);
}
// Vulnerable: Direct database access
public List<User> searchUsers(String criteria) {
// Bypasses repository - no validation, no audit logging!
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement()) {
// Vulnerable: SQL injection AND bypasses security controls!
String sql = "SELECT * FROM users WHERE name LIKE '%" + criteria + "%'";
ResultSet rs = stmt.executeQuery(sql);
// No audit logging
// No input validation
// No access control checks
return mapResults(rs);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
// Vulnerable: Controller with direct database access
@RestController
public class VulnerableUserController {
@Autowired
private JdbcTemplate jdbcTemplate; // Should not have direct access!
@GetMapping("/api/users/{id}")
public User getUser(@PathVariable Long id) {
// Vulnerable: Bypasses service and repository layers entirely!
String sql = "SELECT * FROM users WHERE id = " + id;
return jdbcTemplate.queryForObject(sql, new UserRowMapper());
}
}
# Vulnerable: Direct database access in Python application
# Expected architecture uses repository pattern
class UserRepository:
"""Proper data access layer with security controls."""
def __init__(self, db_session):
self._session = db_session
self._audit_logger = AuditLogger()
def find_by_id(self, user_id: int) -> Optional[User]:
self._audit_logger.log(f"User lookup: {user_id}")
return self._session.query(User).filter(User.id == user_id).first()
def search(self, criteria: str) -> List[User]:
# Validate input
criteria = self._sanitize(criteria)
self._audit_logger.log(f"User search: {criteria}")
return self._session.query(User).filter(
User.name.ilike(f"%{criteria}%")
).all()
# Vulnerable: Service bypasses repository
class VulnerableUserService:
def __init__(self, repository: UserRepository, engine):
self.repository = repository
self._engine = engine # Should not have direct access!
def get_user(self, user_id: int) -> User:
# Proper: Uses repository
return self.repository.find_by_id(user_id)
def search_users(self, criteria: str) -> List[User]:
# Vulnerable: Bypasses repository!
import sqlite3
# Direct connection - bypasses all security controls
with self._engine.connect() as conn:
# SQL injection vulnerability!
result = conn.execute(
f"SELECT * FROM users WHERE name LIKE '%{criteria}%'"
)
return [self._map_row(row) for row in result]
# Vulnerable: View/Controller with direct access
from flask import Flask, request
import psycopg2
app = Flask(__name__)
# Vulnerable: Global database connection in view layer!
db_connection = psycopg2.connect("dbname=myapp user=admin password=secret")
@app.route('/api/users/<int:user_id>')
def get_user(user_id):
# Vulnerable: Direct database access from controller!
cursor = db_connection.cursor()
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}") # SQL injection!
row = cursor.fetchone()
# No audit logging
# No access control
# No connection pooling
return jsonify(row)
Fixed Code
// Fixed: Enforced data access through repository layer
// Fixed: Repository is the ONLY component with database access
@Repository
public class FixedUserRepository {
@PersistenceContext
private EntityManager em;
@Autowired
private AuditLogger auditLogger;
@Autowired
private AccessControlService accessControl;
public User findById(Long id) {
// Fixed: All security controls in one place
accessControl.checkPermission("user:read");
auditLogger.log("User lookup", Map.of("userId", id));
return em.find(User.class, id);
}
public List<User> search(String criteria, User requestingUser) {
// Fixed: Validate and sanitize
String sanitized = InputValidator.sanitizeSearchTerm(criteria);
// Fixed: Access control
accessControl.checkPermission(requestingUser, "user:search");
// Fixed: Audit logging
auditLogger.log("User search", Map.of(
"criteria", sanitized,
"requestedBy", requestingUser.getId()
));
// Fixed: Parameterized query
return em.createQuery(
"SELECT u FROM User u WHERE u.name LIKE :criteria", User.class)
.setParameter("criteria", "%" + sanitized + "%")
.getResultList();
}
}
// Fixed: Service uses repository only - no direct database access
@Service
public class FixedUserService {
private final UserRepository userRepository;
// Fixed: Only repository injected, no DataSource
public FixedUserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User getUser(Long id) {
return userRepository.findById(id);
}
public List<User> searchUsers(String criteria, User requestingUser) {
// Fixed: Delegates to repository - security controls applied
return userRepository.search(criteria, requestingUser);
}
}
// Fixed: Controller has no database dependencies
@RestController
public class FixedUserController {
private final UserService userService;
// Fixed: Only service injected
public FixedUserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/api/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUser(id);
return user != null
? ResponseEntity.ok(user)
: ResponseEntity.notFound().build();
}
}
// Fixed: Architecture enforcement via module boundaries
// In module-info.java or via package-private access
// data-access module exports only interfaces
module dataaccess {
exports com.example.repository; // Only repository interfaces
// Does NOT export: com.example.repository.impl
// Does NOT export: javax.sql
}
// service module depends on repository interfaces only
module service {
requires dataaccess;
// Cannot access database directly!
}
# Fixed: Enforced data access architecture in Python
from abc import ABC, abstractmethod
from typing import List, Optional
from dataclasses import dataclass
# Fixed: Repository interface
class UserRepositoryInterface(ABC):
@abstractmethod
def find_by_id(self, user_id: int) -> Optional['User']:
pass
@abstractmethod
def search(self, criteria: str, requesting_user: 'User') -> List['User']:
pass
# Fixed: Repository implementation (in separate module)
class UserRepository(UserRepositoryInterface):
"""Repository with all security controls."""
def __init__(self, session, audit_logger, access_control):
self._session = session
self._audit = audit_logger
self._access = access_control
def find_by_id(self, user_id: int) -> Optional[User]:
self._access.check_permission("user:read")
self._audit.log("user_lookup", user_id=user_id)
return self._session.query(User).filter(User.id == user_id).first()
def search(self, criteria: str, requesting_user: User) -> List[User]:
# Validate
sanitized = InputValidator.sanitize(criteria)
# Access control
self._access.check_permission(requesting_user, "user:search")
# Audit
self._audit.log("user_search",
criteria=sanitized,
requested_by=requesting_user.id)
# Safe query
return self._session.query(User).filter(
User.name.ilike(f"%{sanitized}%")
).all()
# Fixed: Service depends only on repository interface
class UserService:
"""Service layer - no database access."""
def __init__(self, user_repository: UserRepositoryInterface):
# Fixed: Only repository interface, no database connection
self._repo = user_repository
def get_user(self, user_id: int) -> Optional[User]:
return self._repo.find_by_id(user_id)
def search_users(self, criteria: str, requesting_user: User) -> List[User]:
return self._repo.search(criteria, requesting_user)
# Fixed: Flask app with proper dependency injection
from flask import Flask, g
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
"""Dependency injection container."""
config = providers.Configuration()
# Database session - only available to repository
db_session = providers.Singleton(
create_session,
connection_string=config.database_url
)
# Repository with all dependencies
user_repository = providers.Factory(
UserRepository,
session=db_session,
audit_logger=providers.Factory(AuditLogger),
access_control=providers.Factory(AccessControlService)
)
# Service depends only on repository
user_service = providers.Factory(
UserService,
user_repository=user_repository
)
app = Flask(__name__)
container = Container()
@app.route('/api/users/<int:user_id>')
def get_user(user_id: int):
# Fixed: Uses service, no direct database access
service = container.user_service()
user = service.get_user(user_id)
if user is None:
return {'error': 'User not found'}, 404
return user.to_dict()
# Fixed: Linting rule to detect direct database imports
# In .flake8 or setup.cfg:
# [flake8]
# per-file-ignores =
# # Only repository module can import database
# app/repository/*.py: DBAccess
# app/**/*.py: DBAccess:error
CVE Examples
This CWE is marked as PROHIBITED for direct CVE mapping as it represents an architectural/quality concern rather than a direct security vulnerability.
Related CWEs
- CWE-1061: Insufficient Encapsulation (parent)
- CWE-1227: Encapsulation Issues (category member)
- CWE-89: SQL Injection (commonly enabled by bypassing data layer)
References
- MITRE Corporation. "CWE-1083: Data Access from Outside Expected Data Manager Component." https://cwe.mitre.org/data/definitions/1083.html
- Fowler, Martin. "Patterns of Enterprise Application Architecture." - Repository Pattern
- Evans, Eric. "Domain-Driven Design." - Layered Architecture