External Influence of Sphere Definition
Description
External Influence of Sphere Definition occurs when an external actor can modify the boundaries or definitions of protection domains, trust zones, or security spheres. This includes allowing users to control which resources belong to which security context, modify access control boundaries, redefine trust relationships, or influence how privilege separation is applied. Attackers can exploit this to expand their access or include unauthorized resources in their control sphere.
Risk
Attackers expand their security sphere to include protected resources. Trust boundaries can be redefined to bypass access controls. Security domains can be merged to eliminate isolation. Privilege boundaries can be modified to gain escalated access. Multi-tenant isolation can be broken. Protection mechanisms can be circumvented by changing their scope.
Solution
Enforce sphere definitions server-side. Don't allow external input to modify security boundaries. Validate all trust relationship changes. Implement immutable security domain definitions. Use cryptographic binding for sphere membership. Audit all boundary modifications. Apply principle of least privilege to sphere management.
Common Consequences
| Impact | Details |
|---|---|
| Authorization | Scope: Access Control Bypass Expanding sphere includes protected resources. |
| Confidentiality | Scope: Data Exposure Modifying boundaries exposes protected data. |
| Integrity | Scope: Privilege Escalation Changing trust relationships gains elevated access. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: External influence of security spheres
@RestController
public class VulnerableSecurityController {
// VULNERABLE: User controls their own roles
@PostMapping("/api/user/roles")
public void updateRoles(@RequestBody RoleUpdateRequest request) {
User user = getCurrentUser();
// VULNERABLE: User defines their own security sphere
user.setRoles(request.getRoles()); // Can add 'admin' role!
userRepository.save(user);
}
// VULNERABLE: User controls resource access definitions
@PostMapping("/api/access-rules")
public void defineAccessRules(@RequestBody AccessRuleRequest request) {
// VULNERABLE: User defines what they can access
AccessRule rule = new AccessRule();
rule.setUserId(getCurrentUser().getId());
rule.setResourcePattern(request.getPattern()); // Can set "*"
rule.setPermissions(request.getPermissions()); // Can set "ALL"
accessRuleRepository.save(rule);
}
// VULNERABLE: User controls group membership
@PostMapping("/api/groups/{groupId}/join")
public void joinGroup(@PathVariable Long groupId) {
User user = getCurrentUser();
Group group = groupRepository.findById(groupId);
// VULNERABLE: No validation of group membership rules
group.addMember(user); // Can join admin groups
groupRepository.save(group);
}
// VULNERABLE: User controls tenant boundaries
@PostMapping("/api/tenant/switch")
public void switchTenant(@RequestParam Long tenantId) {
User user = getCurrentUser();
// VULNERABLE: User can switch to any tenant
user.setCurrentTenant(tenantId); // Cross-tenant access
sessionService.updateTenant(user, tenantId);
}
}
// VULNERABLE: User-controlled trust domain
public class VulnerableTrustManager {
// VULNERABLE: External input defines trusted sources
public void addTrustedSource(String userProvidedDomain) {
// User controls what domains are trusted
trustedDomains.add(userProvidedDomain);
}
// VULNERABLE: External input defines allowed origins
public void setCORSOrigins(List<String> userProvidedOrigins) {
// User controls CORS policy
this.allowedOrigins = userProvidedOrigins;
}
}
# VULNERABLE: Python external sphere influence
from flask import Flask, request, session
app = Flask(__name__)
# VULNERABLE: User controls their permissions
@app.route('/api/user/permissions', methods=['POST'])
def update_permissions():
user_id = session['user_id']
permissions = request.json.get('permissions', [])
# VULNERABLE: User defines their own permission sphere
db.execute(
"UPDATE users SET permissions = %s WHERE id = %s",
(json.dumps(permissions), user_id)
)
return {'status': 'ok'}
# VULNERABLE: User controls namespace/scope
@app.route('/api/set-scope', methods=['POST'])
def set_scope():
scope = request.json.get('scope')
# VULNERABLE: User controls their access scope
session['scope'] = scope # Can set 'admin' or 'global'
return {'status': 'ok'}
# VULNERABLE: User controls data visibility boundaries
class VulnerableDataService:
def get_data(self, user_filter):
# VULNERABLE: User defines data boundary
filter_clause = user_filter # User can set to "1=1"
return db.query(f"SELECT * FROM data WHERE {filter_clause}")
# VULNERABLE: User controls API access scope
@app.route('/api/token', methods=['POST'])
def create_token():
requested_scopes = request.json.get('scopes', [])
# VULNERABLE: User chooses their own scopes
token = create_jwt_token(
user_id=session['user_id'],
scopes=requested_scopes # Can request 'admin:*'
)
return {'token': token}
# VULNERABLE: User controls file access boundaries
@app.route('/api/set-root', methods=['POST'])
def set_file_root():
root_dir = request.json.get('root')
# VULNERABLE: User defines their file access sphere
session['file_root'] = root_dir # Can set '/'
return {'status': 'ok'}
// VULNERABLE: Node.js external sphere influence
const express = require('express');
const app = express();
// VULNERABLE: User controls their role assignment
app.post('/api/user/role', (req, res) => {
const { role } = req.body;
const user = req.user;
// VULNERABLE: User defines their security sphere
user.role = role; // Can set 'superadmin'
user.save();
res.json({ success: true });
});
// VULNERABLE: User controls tenant isolation
app.post('/api/tenant/data-access', (req, res) => {
const { tenantIds } = req.body;
// VULNERABLE: User defines which tenants they can access
req.session.accessibleTenants = tenantIds; // Can include all tenants
res.json({ success: true });
});
// VULNERABLE: User controls OAuth scopes
app.post('/oauth/authorize', (req, res) => {
const { scope } = req.body;
// VULNERABLE: User controls granted scopes
const token = generateToken({
userId: req.user.id,
scope: scope // User-controlled scope string
});
res.json({ access_token: token });
});
// VULNERABLE: User controls security policy
app.post('/api/security-policy', (req, res) => {
const { allowedIPs, allowedOrigins, bypassAuth } = req.body;
// VULNERABLE: User can modify security policy
securityConfig.allowedIPs = allowedIPs;
securityConfig.allowedOrigins = allowedOrigins;
securityConfig.bypassAuth = bypassAuth; // User can disable auth!
res.json({ success: true });
});
// VULNERABLE: User controls trust relationships
app.post('/api/federation/trust', (req, res) => {
const { trustedDomain, trustLevel } = req.body;
// VULNERABLE: User defines trust relationships
federationConfig.addTrusted(trustedDomain, trustLevel);
res.json({ success: true });
});
Fixed Code
// SAFE: Server-controlled security spheres
@RestController
public class SecureSecurityController {
@Autowired
private RoleService roleService;
@Autowired
private AuditService auditService;
// SAFE: Roles assigned by admins only
@PostMapping("/api/admin/user/{userId}/roles")
@PreAuthorize("hasRole('USER_ADMIN')")
public void updateRoles(
@PathVariable Long userId,
@RequestBody RoleUpdateRequest request) {
// SAFE: Validate requested roles are assignable
Set<String> requestedRoles = request.getRoles();
Set<String> assignableRoles = roleService.getAssignableRoles(getCurrentUser());
if (!assignableRoles.containsAll(requestedRoles)) {
throw new ForbiddenException("Cannot assign these roles");
}
User targetUser = userRepository.findById(userId)
.orElseThrow(() -> new NotFoundException("User not found"));
auditService.log("ROLE_CHANGE", getCurrentUser(), targetUser, requestedRoles);
targetUser.setRoles(requestedRoles);
userRepository.save(targetUser);
}
// SAFE: Access rules defined by authorized admins
@PostMapping("/api/admin/access-rules")
@PreAuthorize("hasRole('ACCESS_ADMIN')")
public void defineAccessRules(@RequestBody AccessRuleRequest request) {
// SAFE: Validate rule doesn't exceed admin's authority
if (!accessRuleValidator.canCreate(getCurrentUser(), request)) {
throw new ForbiddenException("Rule exceeds authority");
}
AccessRule rule = new AccessRule();
rule.setResourcePattern(sanitizePattern(request.getPattern()));
rule.setPermissions(validatePermissions(request.getPermissions()));
auditService.log("ACCESS_RULE_CREATED", getCurrentUser(), rule);
accessRuleRepository.save(rule);
}
// SAFE: Group membership controlled by group policies
@PostMapping("/api/groups/{groupId}/join")
public void joinGroup(@PathVariable Long groupId) {
User user = getCurrentUser();
Group group = groupRepository.findById(groupId)
.orElseThrow(() -> new NotFoundException("Group not found"));
// SAFE: Check if user is allowed to join
if (!groupService.canJoin(user, group)) {
throw new ForbiddenException("Not authorized to join this group");
}
// For restricted groups, require approval
if (group.requiresApproval()) {
groupService.requestMembership(user, group);
} else {
groupService.addMember(user, group);
}
}
// SAFE: Tenant switching based on user's authorized tenants
@PostMapping("/api/tenant/switch")
public void switchTenant(@RequestParam Long tenantId) {
User user = getCurrentUser();
// SAFE: Verify user is authorized for this tenant
if (!user.getAuthorizedTenants().contains(tenantId)) {
throw new ForbiddenException("Not authorized for tenant");
}
auditService.log("TENANT_SWITCH", user, tenantId);
sessionService.updateTenant(user, tenantId);
}
}
// SAFE: Trust domains defined by system, not users
public class SecureTrustManager {
private final Set<String> trustedDomains;
public SecureTrustManager(SecurityConfig config) {
// SAFE: Trust domains from configuration, not user input
this.trustedDomains = Collections.unmodifiableSet(
config.getTrustedDomains()
);
}
// SAFE: Admin-only trust modification with audit
@PreAuthorize("hasRole('SECURITY_ADMIN')")
public void addTrustedSource(String domain, User admin) {
auditService.log("TRUST_ADDED", admin, domain);
// Requires application restart to take effect
configService.addTrustedDomain(domain);
}
}
# SAFE: Python with server-controlled spheres
from flask import Flask, request, session
from functools import wraps
app = Flask(__name__)
def admin_required(f):
@wraps(f)
def decorated(*args, **kwargs):
if not current_user.is_admin:
return {'error': 'Forbidden'}, 403
return f(*args, **kwargs)
return decorated
# SAFE: Permissions assigned by admins only
@app.route('/api/admin/user/<user_id>/permissions', methods=['POST'])
@admin_required
def update_user_permissions(user_id):
permissions = request.json.get('permissions', [])
# SAFE: Validate permissions are valid and assignable
valid_permissions = get_valid_permissions()
if not all(p in valid_permissions for p in permissions):
return {'error': 'Invalid permissions'}, 400
# SAFE: Admin can only assign permissions they have
if not can_assign_permissions(current_user, permissions):
return {'error': 'Cannot assign these permissions'}, 403
audit_log('PERMISSION_CHANGE', current_user, user_id, permissions)
db.execute(
"UPDATE users SET permissions = %s WHERE id = %s",
(json.dumps(permissions), user_id)
)
return {'status': 'ok'}
# SAFE: Scope determined by server, not user
@app.route('/api/data')
def get_data():
# SAFE: Scope from user's assigned permissions
user_scope = get_user_scope(current_user)
return get_scoped_data(user_scope)
# SAFE: Token scopes limited to user's permissions
@app.route('/api/token', methods=['POST'])
def create_token():
requested_scopes = request.json.get('scopes', [])
# SAFE: Filter to only allowed scopes
user_allowed_scopes = get_user_allowed_scopes(current_user)
granted_scopes = [s for s in requested_scopes if s in user_allowed_scopes]
token = create_jwt_token(
user_id=session['user_id'],
scopes=granted_scopes # Only server-validated scopes
)
return {'token': token, 'scopes': granted_scopes}
# SAFE: File access based on assigned root, not user input
@app.route('/api/files')
def list_files():
# SAFE: Root determined by user's assigned directory
user_root = get_user_file_root(current_user) # From database, not session
# Validate user has access to this directory
if not user_root:
return {'error': 'No file access'}, 403
return list_directory(user_root)
# SAFE: Data visibility based on user's assigned scope
class SecureDataService:
def get_data(self, user):
# SAFE: Scope determined by user's permissions
visible_departments = user.get_visible_departments()
if not visible_departments:
return []
placeholders = ','.join(['%s'] * len(visible_departments))
return db.query(
f"SELECT * FROM data WHERE department_id IN ({placeholders})",
visible_departments
)
// SAFE: Node.js with server-controlled spheres
const express = require('express');
const app = express();
// SAFE: Role changes require admin authorization
app.post('/api/admin/user/:userId/role',
requireAuth,
requireRole('USER_ADMIN'),
async (req, res) => {
const { userId } = req.params;
const { role } = req.body;
// SAFE: Validate role exists and is assignable
const validRoles = await getAssignableRoles(req.user);
if (!validRoles.includes(role)) {
return res.status(403).json({ error: 'Cannot assign this role' });
}
const targetUser = await User.findById(userId);
if (!targetUser) {
return res.status(404).json({ error: 'User not found' });
}
await auditLog('ROLE_CHANGE', req.user, targetUser, role);
targetUser.role = role;
await targetUser.save();
res.json({ success: true });
}
);
// SAFE: Tenant access based on user's authorized tenants
app.get('/api/tenant/:tenantId/data',
requireAuth,
async (req, res) => {
const { tenantId } = req.params;
// SAFE: Verify user is authorized for tenant
const userTenants = await getUserAuthorizedTenants(req.user.id);
if (!userTenants.includes(tenantId)) {
return res.status(403).json({ error: 'Not authorized for tenant' });
}
const data = await getTenantData(tenantId);
res.json(data);
}
);
// SAFE: OAuth scopes limited by user's permissions
app.post('/oauth/authorize',
requireAuth,
async (req, res) => {
const requestedScopes = req.body.scope?.split(' ') || [];
// SAFE: Only grant scopes user is allowed
const allowedScopes = await getUserAllowedScopes(req.user.id);
const grantedScopes = requestedScopes.filter(s => allowedScopes.includes(s));
const token = await generateToken({
userId: req.user.id,
scope: grantedScopes.join(' ')
});
await auditLog('TOKEN_ISSUED', req.user, grantedScopes);
res.json({
access_token: token,
scope: grantedScopes.join(' ')
});
}
);
// SAFE: Security policy changes require security admin
app.post('/api/admin/security-policy',
requireAuth,
requireRole('SECURITY_ADMIN'),
async (req, res) => {
const { allowedIPs, allowedOrigins } = req.body;
// SAFE: Validate policy changes
if (!validateIPList(allowedIPs)) {
return res.status(400).json({ error: 'Invalid IP list' });
}
if (!validateOriginList(allowedOrigins)) {
return res.status(400).json({ error: 'Invalid origin list' });
}
// SAFE: bypassAuth cannot be set via API
await auditLog('SECURITY_POLICY_CHANGE', req.user, { allowedIPs, allowedOrigins });
await updateSecurityConfig({ allowedIPs, allowedOrigins });
res.json({ success: true });
}
);
Exploited in the Wild
Self-Promotion Attacks
Users granting themselves elevated privileges.
Multi-Tenant Bypass
Users expanding access to other tenants.
Scope Escalation
OAuth scope expansion beyond authorized limits.
Tools to test/exploit
-
Parameter manipulation tools.
-
Authorization testing frameworks.
-
API security scanners.
CVE Examples
-
Various privilege escalation CVEs.
-
Multi-tenant isolation bypasses.
References
-
MITRE. "CWE-673: External Influence of Sphere Definition." https://cwe.mitre.org/data/definitions/673.html
-
Authorization best practices.