Excessive Reliance on Global Variables

Description

Excessive Reliance on Global Variables occurs when code is structured in a way that depends too heavily on using or setting global variables throughout various points in the code, instead of preserving the associated information in a narrower, more local context. Global variables are accessible from anywhere in the program, which makes tracking their state difficult and creates hidden dependencies between different parts of the code. This violates principles of encapsulation and makes the code harder to understand, test, and maintain.

Risk

Excessive use of global variables has security implications. Global state can be modified from anywhere, making it hard to ensure invariants are maintained. Race conditions are more likely when multiple threads access global state. Security-sensitive data in global variables is more exposed. Hidden dependencies between components can cause unexpected security behaviors. Testing for security issues is complicated by global state. Code review cannot easily track all places where global state changes. Debugging security incidents is harder with non-local effects.

Solution

Minimize use of global variables; prefer local variables and parameter passing. Encapsulate state in objects with controlled access methods. Use dependency injection to provide shared state explicitly. If globals are necessary, use proper synchronization for thread safety. Consider using constants instead of variables for immutable global data. Apply the principle of least privilege - limit scope to what's actually needed. Use static analysis tools to detect excessive global variable usage. Refactor legacy code to reduce global dependencies gradually.

Common Consequences

ImpactDetails
OtherScope: Other

Reduce Maintainability - Global state creates hidden dependencies that complicate maintenance.
OtherScope: Other

Increase Analytical Complexity - Hard to track data flow through global variables.
IntegrityScope: Integrity

Unexpected State - Global state can be modified unexpectedly from anywhere.

Example Code

Vulnerable Code

// Vulnerable: Excessive global variables
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Global variables scattered throughout
char g_current_user[100];
int g_permission_level;
bool g_is_authenticated;
char g_session_token[256];
int g_failed_login_attempts;
bool g_account_locked;
char g_last_error[500];
void* g_database_connection;
char g_config_path[256];
int g_log_level;

// Functions modify globals from anywhere
void vulnerable_login(const char* username, const char* password) {
    // Modifies multiple globals
    if (verify_password(username, password)) {
        g_is_authenticated = true;
        strcpy(g_current_user, username);
        g_permission_level = get_user_permission(username);
        generate_token(g_session_token);
        g_failed_login_attempts = 0;  // Reset
    } else {
        g_is_authenticated = false;
        g_failed_login_attempts++;  // Increment global
        if (g_failed_login_attempts >= 5) {
            g_account_locked = true;  // Set another global
        }
    }
}

void vulnerable_process_request(const char* action) {
    // Relies on globals being set correctly somewhere else
    if (!g_is_authenticated) {
        strcpy(g_last_error, "Not authenticated");
        return;
    }

    if (g_permission_level < 2) {
        strcpy(g_last_error, "Insufficient permissions");
        return;
    }

    // Process action...
}

// Problem: Any function can modify any global
// Race conditions when multiple threads use these
void vulnerable_other_function() {
    // Can arbitrarily modify security state!
    g_is_authenticated = true;  // Security bypass!
    g_permission_level = 999;   // Privilege escalation!
}
// Vulnerable: Java with excessive static/global state
public class VulnerableGlobalState {

    // Mutable static fields = global state
    public static User currentUser;
    public static Session activeSession;
    public static int permissionLevel;
    public static boolean isAdmin;
    public static DatabaseConnection dbConnection;
    public static Configuration config;
    public static Logger logger;

    // Thread-unsafe global access
    public static Map<String, Object> cache = new HashMap<>();
    public static List<String> auditLog = new ArrayList<>();
}

public class VulnerableAuthService {

    public void login(String username, String password) {
        if (verifyCredentials(username, password)) {
            // Sets global state
            VulnerableGlobalState.currentUser = loadUser(username);
            VulnerableGlobalState.activeSession = createSession();
            VulnerableGlobalState.permissionLevel = getPermission(username);
            VulnerableGlobalState.isAdmin = checkAdmin(username);
        }
    }

    public void logout() {
        // Clears global state
        VulnerableGlobalState.currentUser = null;
        VulnerableGlobalState.activeSession = null;
        VulnerableGlobalState.permissionLevel = 0;
        VulnerableGlobalState.isAdmin = false;
    }
}

public class VulnerableOrderService {

    public void placeOrder(Order order) {
        // Relies on global state from auth
        if (VulnerableGlobalState.currentUser == null) {
            throw new UnauthorizedException();
        }

        // Problem: No guarantee this state is valid
        // Could have been modified by another thread
        order.setUserId(VulnerableGlobalState.currentUser.getId());

        // Directly uses global DB connection
        VulnerableGlobalState.dbConnection.save(order);

        // Adds to global audit log (thread-unsafe!)
        VulnerableGlobalState.auditLog.add(
            "Order placed by " + VulnerableGlobalState.currentUser.getName()
        );
    }
}
# Vulnerable: Python with excessive global state
# File: globals.py
current_user = None
session_data = {}
is_authenticated = False
permission_level = 0
config = {}
db_connection = None
cache = {}
audit_log = []


# File: auth.py
import globals

def login(username, password):
    if verify_credentials(username, password):
        # Modifies module-level globals
        globals.is_authenticated = True
        globals.current_user = load_user(username)
        globals.permission_level = get_permission(username)
        globals.session_data = {'user': username, 'login_time': time.time()}
    else:
        globals.is_authenticated = False


def logout():
    globals.is_authenticated = False
    globals.current_user = None
    globals.permission_level = 0
    globals.session_data = {}


# File: service.py
import globals

def process_request(data):
    # Relies on global state
    if not globals.is_authenticated:
        raise PermissionError("Not authenticated")

    # Race condition: another thread could modify this
    if globals.permission_level < 2:
        raise PermissionError("Insufficient permissions")

    # Global cache without thread safety
    if data['key'] in globals.cache:
        return globals.cache[data['key']]

    result = expensive_operation(data)
    globals.cache[data['key']] = result

    # Global audit log
    globals.audit_log.append({
        'user': globals.current_user,
        'action': 'process_request',
        'time': time.time()
    })

    return result


# File: malicious_code.py
import globals

# Any code can modify security-critical globals!
def privilege_escalation():
    globals.is_authenticated = True
    globals.permission_level = 9999
    globals.current_user = {'name': 'admin', 'is_admin': True}

Fixed Code

// Fixed: Use structured context instead of globals
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

// Encapsulate session state in a struct
typedef struct {
    char username[100];
    int permission_level;
    bool is_authenticated;
    char session_token[256];
    int failed_attempts;
    bool account_locked;
} SessionContext;

// Configuration as read-only after initialization
typedef struct {
    char config_path[256];
    int log_level;
    int max_login_attempts;
} AppConfig;

// Pass context explicitly instead of using globals
int fixed_login(
    SessionContext* ctx,
    const AppConfig* config,
    const char* username,
    const char* password) {

    if (ctx->account_locked) {
        return -1;
    }

    if (verify_password(username, password)) {
        ctx->is_authenticated = true;
        strncpy(ctx->username, username, sizeof(ctx->username) - 1);
        ctx->permission_level = get_user_permission(username);
        generate_token(ctx->session_token);
        ctx->failed_attempts = 0;
        return 0;
    } else {
        ctx->is_authenticated = false;
        ctx->failed_attempts++;
        if (ctx->failed_attempts >= config->max_login_attempts) {
            ctx->account_locked = true;
        }
        return -1;
    }
}

int fixed_process_request(
    const SessionContext* ctx,
    const char* action,
    char* error_buffer,
    size_t error_size) {

    if (!ctx->is_authenticated) {
        strncpy(error_buffer, "Not authenticated", error_size);
        return -1;
    }

    if (ctx->permission_level < 2) {
        strncpy(error_buffer, "Insufficient permissions", error_size);
        return -1;
    }

    // Process action with explicit context
    return 0;
}

// Each request gets its own context - no global state
void fixed_handle_request(const AppConfig* config) {
    SessionContext ctx = {0};  // Local context

    fixed_login(&ctx, config, "user", "password");
    fixed_process_request(&ctx, "action", NULL, 0);

    // Context is automatically cleaned up when function returns
}
// Fixed: Dependency injection and encapsulated state
public class SessionContext {
    private final String username;
    private final int permissionLevel;
    private final boolean admin;
    private final String sessionToken;

    // Immutable once created
    public SessionContext(String username, int permissionLevel,
                          boolean admin, String sessionToken) {
        this.username = username;
        this.permissionLevel = permissionLevel;
        this.admin = admin;
        this.sessionToken = sessionToken;
    }

    // Getters only, no setters
    public String getUsername() { return username; }
    public int getPermissionLevel() { return permissionLevel; }
    public boolean isAdmin() { return admin; }
    public String getSessionToken() { return sessionToken; }
}

// Fixed: Services receive dependencies through constructor
public class FixedAuthService {

    private final UserRepository userRepository;
    private final SessionStore sessionStore;
    private final PasswordEncoder passwordEncoder;

    // Dependencies injected
    public FixedAuthService(
            UserRepository userRepository,
            SessionStore sessionStore,
            PasswordEncoder passwordEncoder) {
        this.userRepository = userRepository;
        this.sessionStore = sessionStore;
        this.passwordEncoder = passwordEncoder;
    }

    public SessionContext login(String username, String password) {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new AuthenticationException("Invalid credentials"));

        if (!passwordEncoder.matches(password, user.getPasswordHash())) {
            throw new AuthenticationException("Invalid credentials");
        }

        String token = generateToken();
        SessionContext context = new SessionContext(
            user.getUsername(),
            user.getPermissionLevel(),
            user.isAdmin(),
            token
        );

        sessionStore.save(token, context);
        return context;
    }
}

// Fixed: Context passed explicitly, no global state
public class FixedOrderService {

    private final OrderRepository orderRepository;
    private final AuditService auditService;

    public FixedOrderService(
            OrderRepository orderRepository,
            AuditService auditService) {
        this.orderRepository = orderRepository;
        this.auditService = auditService;
    }

    public Order placeOrder(SessionContext context, Order order) {
        // Context is explicit - we know exactly what state we have
        Objects.requireNonNull(context, "Session context required");

        order.setUserId(context.getUsername());
        Order saved = orderRepository.save(order);

        auditService.log(
            context.getUsername(),
            "ORDER_PLACED",
            saved.getId()
        );

        return saved;
    }
}

// Thread-safe singleton for truly global immutable config
public enum AppConfig {
    INSTANCE;

    private volatile Configuration config;

    public void initialize(Configuration config) {
        if (this.config != null) {
            throw new IllegalStateException("Already initialized");
        }
        this.config = config;
    }

    public Configuration get() {
        return config;
    }
}
# Fixed: Python with dependency injection and explicit context
from dataclasses import dataclass
from typing import Optional, Dict, Any
import threading


@dataclass(frozen=True)  # Immutable
class SessionContext:
    """Immutable session context."""
    username: str
    permission_level: int
    is_admin: bool
    session_token: str


@dataclass(frozen=True)
class AppConfig:
    """Immutable application configuration."""
    db_url: str
    max_login_attempts: int
    session_timeout_seconds: int


class AuthService:
    """Authentication service with explicit dependencies."""

    def __init__(self, user_repo, session_store, config: AppConfig):
        self._user_repo = user_repo
        self._session_store = session_store
        self._config = config
        self._lock = threading.Lock()

    def login(self, username: str, password: str) -> SessionContext:
        user = self._user_repo.find_by_username(username)
        if not user or not verify_password(password, user.password_hash):
            raise AuthenticationError("Invalid credentials")

        token = generate_token()
        context = SessionContext(
            username=user.username,
            permission_level=user.permission_level,
            is_admin=user.is_admin,
            session_token=token
        )

        with self._lock:  # Thread-safe session storage
            self._session_store.save(token, context)

        return context


class OrderService:
    """Order service with explicit dependencies."""

    def __init__(self, order_repo, audit_service):
        self._order_repo = order_repo
        self._audit_service = audit_service

    def place_order(self, context: SessionContext, order_data: Dict[str, Any]):
        """Place order with explicit session context."""
        if context.permission_level < 2:
            raise PermissionError("Insufficient permissions")

        order = Order(
            user_id=context.username,
            **order_data
        )
        saved = self._order_repo.save(order)

        self._audit_service.log(
            user=context.username,
            action='ORDER_PLACED',
            entity_id=saved.id
        )

        return saved


# Thread-local storage when request context is needed
class RequestContext:
    """Thread-local request context."""

    _local = threading.local()

    @classmethod
    def set(cls, context: SessionContext) -> None:
        cls._local.context = context

    @classmethod
    def get(cls) -> Optional[SessionContext]:
        return getattr(cls._local, 'context', None)

    @classmethod
    def clear(cls) -> None:
        if hasattr(cls._local, 'context'):
            del cls._local.context


# Usage in request handler
def handle_request(request):
    # Create container with dependencies
    container = create_container()

    # Authenticate and get context
    auth_service = container.get(AuthService)
    context = auth_service.login(request.username, request.password)

    # Set thread-local context if needed
    RequestContext.set(context)

    try:
        # Process with explicit context
        order_service = container.get(OrderService)
        result = order_service.place_order(context, request.order_data)
        return result
    finally:
        RequestContext.clear()

CVE Examples

Excessive global variables have contributed to various security vulnerabilities where global state was improperly modified, leading to authentication bypasses, privilege escalation, and race conditions.


  • CWE-1076: Insufficient Adherence to Expected Conventions (parent)
  • CWE-1006: Bad Coding Practices (category member)
  • CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (related)

References

  1. MITRE Corporation. "CWE-1108: Excessive Reliance on Global Variables." https://cwe.mitre.org/data/definitions/1108.html
  2. Martin, Robert C. "Clean Code" - Functions Should Not Use Global Variables.