Skip to content

feat: Missing Security Measures and Access Control #149

@KevinMB0220

Description

@KevinMB0220

Issue: Missing Security Measures and Access Control

Description:

The codebase lacks comprehensive security measures and proper access control mechanisms. Many functions don't implement proper authorization checks, and there are potential security vulnerabilities that could be exploited by malicious users.

Problems Identified:

  1. Insufficient Access Control: Many functions lack proper authorization checks
  2. Missing Input Sanitization: No validation of user inputs for malicious content
  3. Incomplete Admin Controls: Admin functions don't have proper validation
  4. Missing Rate Limiting: No protection against spam or abuse
  5. Insufficient Session Security: Session management lacks proper security measures

Code Issues Found:

// In CoreActions - Line 82: Basic admin check but missing validation
assert(caller == contract.admin, 'Only admin can spawn items');
// Missing: Admin role validation, contract state validation

// In PlayerActions - Line 743: Returns zero address - security risk
fn get_erc1155_address(self: @ContractState) -> ContractAddress {
    starknet::contract_address_const::<0x0>() // Returns zero address!
}

// In SessionActions - Line 36: No rate limiting for session creation
fn create_session_key(ref self: ContractState, session_duration: u64, max_transactions: u32) -> felt252 {
    // No rate limiting - users can spam session creation
}

Specific Problems:

  1. Admin Role Validation: Admin functions don't properly validate admin status
  2. Session Hijacking: Session management lacks proper security measures
  3. Input Validation: No sanitization of user inputs
  4. Rate Limiting: No protection against spam or abuse
  5. Access Control: Missing proper authorization for sensitive operations

Impact:

  • High Priority: Security vulnerabilities that could be exploited
  • Potential for unauthorized access to admin functions
  • Risk of session hijacking or abuse
  • Possible spam attacks on the system
  • Loss of user funds or items

Recommended Solutions:

  1. Implement Comprehensive Access Control:

    fn validate_admin_access(self: @ContractState) {
        let caller = get_caller_address();
        let world = self.world_default();
        let contract: Contract = world.read_model(COA_CONTRACTS);
        
        // Validate caller is not zero address
        assert(!caller.is_zero(), 'ZERO_ADDRESS');
        
        // Validate contract state
        assert(!contract.admin.is_zero(), 'INVALID_CONTRACT_STATE');
        
        // Validate admin role
        assert(caller == contract.admin, 'INSUFFICIENT_PERMISSIONS');
        
        // Check if contract is paused
        assert(!contract.paused, 'CONTRACT_PAUSED');
    }
    
    fn validate_player_access(self: @ContractState, player_id: ContractAddress) {
        let caller = get_caller_address();
        
        // Validate caller is not zero address
        assert(!caller.is_zero(), 'ZERO_ADDRESS');
        
        // Validate player ID matches caller
        assert(caller == player_id, 'UNAUTHORIZED_PLAYER');
        
        // Validate player exists and is active
        let world = self.world_default();
        let player: Player = world.read_model(player_id);
        assert(player.max_hp > 0, 'PLAYER_NOT_FOUND');
    }
  2. Add Rate Limiting Protection:

    // Rate limiting for session creation
    fn check_rate_limit(self: @ContractState, user: ContractAddress, operation: felt252) -> bool {
        let world = self.world_default();
        let current_time = get_block_timestamp();
        let rate_limit_key = (user, operation, current_time / 3600); // 1 hour windows
        
        let mut rate_limit: RateLimit = world.read_model(rate_limit_key);
        
        match operation {
            'CREATE_SESSION' => {
                if rate_limit.count >= 5 { // Max 5 sessions per hour
                    return false;
                }
            },
            'SPAWN_ITEMS' => {
                if rate_limit.count >= 10 { // Max 10 spawns per hour
                    return false;
                }
            },
            _ => {}
        }
        
        rate_limit.count += 1;
        world.write_model(@rate_limit);
        true
    }
  3. Implement Input Sanitization:

    fn sanitize_input(self: @ContractState, input: felt252) -> felt252 {
        // Basic input sanitization
        // In a real implementation, you might want to check for malicious patterns
        input
    }
    
    fn validate_faction(self: @ContractState, faction: felt252) -> bool {
        // Validate faction is one of the allowed values
        faction == 'CHAOS_MERCENARIES' || 
        faction == 'SUPREME_LAW' || 
        faction == 'REBEL_TECHNOMANCERS'
    }
    
    fn validate_session_duration(self: @ContractState, duration: u64) -> bool {
        // Validate session duration is within reasonable limits
        duration >= 3600 && duration <= 86400 // 1 hour to 24 hours
    }
  4. Add Session Security Measures:

    fn create_secure_session(
        ref self: ContractState, 
        session_duration: u64, 
        max_transactions: u32
    ) -> felt252 {
        let caller = get_caller_address();
        
        // Validate inputs
        assert(self.validate_session_duration(session_duration), 'INVALID_DURATION');
        assert(max_transactions > 0 && max_transactions <= 1000, 'INVALID_TRANSACTIONS');
        
        // Check rate limiting
        assert(self.check_rate_limit(caller, 'CREATE_SESSION'), 'RATE_LIMIT_EXCEEDED');
        
        // Generate secure session ID
        let session_id = self.generate_secure_session_id(caller);
        
        // Create session with security measures
        let session_key = SessionKey {
            session_id,
            player_address: caller,
            session_key_address: caller,
            created_at: get_block_timestamp(),
            expires_at: get_block_timestamp() + session_duration,
            last_used: get_block_timestamp(),
            status: 0, // Active
            max_transactions,
            used_transactions: 0,
            is_valid: true,
        };
        
        let mut world = self.world_default();
        world.write_model(@session_key);
        
        session_id
    }
  5. Implement Secure Random Number Generation:

    fn generate_secure_session_id(self: @ContractState, player: ContractAddress) -> felt252 {
        // Use multiple sources of entropy for security
        let tx_hash: felt252 = starknet::get_tx_info().unbox().transaction_hash;
        let block_timestamp: felt252 = get_block_timestamp().into();
        let player_address: felt252 = player.into();
        
        // Combine entropy sources
        let mut hash_data = array![tx_hash, block_timestamp, player_address];
        poseidon_hash_span(hash_data.span())
    }
  6. Add Contract Pause Functionality:

    fn pause_contract(ref self: ContractState) {
        self.validate_admin_access();
        
        let mut world = self.world_default();
        let mut contract: Contract = world.read_model(COA_CONTRACTS);
        
        contract.paused = true;
        world.write_model(@contract);
        
        world.emit_event(@ContractPaused { paused_by: get_caller_address() });
    }
    
    fn unpause_contract(ref self: ContractState) {
        self.validate_admin_access();
        
        let mut world = self.world_default();
        let mut contract: Contract = world.read_model(COA_CONTRACTS);
        
        contract.paused = false;
        world.write_model(@contract);
        
        world.emit_event(@ContractUnpaused { unpaused_by: get_caller_address() });
    }
  7. Implement Emergency Functions:

    fn emergency_withdraw(ref self: ContractState, token_address: ContractAddress, amount: u256) {
        self.validate_admin_access();
        
        // Emergency function to withdraw tokens in case of emergency
        let client = IERC20Dispatcher { contract_address: token_address };
        let contract_address = get_contract_address();
        
        assert(client.balance_of(contract_address) >= amount, 'INSUFFICIENT_BALANCE');
        client.transfer(get_caller_address(), amount);
        
        world.emit_event(@EmergencyWithdraw { 
            token: token_address, 
            amount, 
            withdrawn_by: get_caller_address() 
        });
    }
  8. Add Security Events:

    #[derive(Drop, Copy, Serde)]
    #[dojo::event]
    pub struct SecurityEvent {
        #[key]
        pub event_type: felt252, // 'UNAUTHORIZED_ACCESS', 'RATE_LIMIT_EXCEEDED', etc.
        pub user: ContractAddress,
        pub timestamp: u64,
        pub details: felt252,
    }
    
    #[derive(Drop, Copy, Serde)]
    #[dojo::event]
    pub struct ContractPaused {
        pub paused_by: ContractAddress,
    }
    
    #[derive(Drop, Copy, Serde)]
    #[dojo::event]
    pub struct ContractUnpaused {
        pub unpaused_by: ContractAddress,
    }

Files Affected:

  • src/systems/core.cairo - Add admin access control
  • src/systems/player.cairo - Add player access control
  • src/systems/session.cairo - Add session security
  • src/models/core.cairo - Add security models
  • Missing: Security helper functions, rate limiting models

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions