T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/soundstudiopro.com/private_html/utils/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/private_html/utils/audio_token.php
<?php
/**
 * Audio Token System - Generates and validates expiring signed tokens for audio streaming
 * This prevents direct URL sharing and unauthorized downloads
 */

// Secret key for signing tokens - should be unique per installation
// This is combined with the track ID and expiration to create a signature
define('AUDIO_TOKEN_SECRET', 'ssp_audio_2024_' . md5(__DIR__));

// Token expiration time in seconds (5 minutes default)
define('AUDIO_TOKEN_EXPIRY', 300);

// Token usage limit - very restrictive to prevent abuse
define('AUDIO_TOKEN_MAX_USES', 1); // Allow only 1 use (no refreshes allowed)

/**
 * Generate a signed token for audio streaming
 * NOW INCLUDES USER/SESSION BINDING - tokens cannot be shared
 * 
 * @param int $trackId The track ID
 * @param int|null $variationIndex Optional variation index
 * @param int|null $expiresIn Seconds until expiration (default: AUDIO_TOKEN_EXPIRY)
 * @param int|null $userId User ID to bind token to (required for security)
 * @param string|null $sessionId Session ID to bind token to (required for security)
 * @return array ['token' => string, 'expires' => int]
 */
function generateAudioToken($trackId, $variationIndex = null, $expiresIn = null, $userId = null, $sessionId = null) {
    $expires = time() + ($expiresIn ?? AUDIO_TOKEN_EXPIRY);
    
    // CRITICAL: Include user and session in token signature
    // This prevents token sharing - tokens only work for the user/session who generated them
    // For guests (no user_id), bind to session_id to prevent sharing
    
    // Ensure session is started
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    
    // Always use current session ID (don't trust passed parameter)
    // This ensures consistency - token is always bound to the current session
    $currentSessionId = session_id();
    
    // Build user context: user_id|session_id
    // For logged-in users: "123|abc123" 
    // For guests: "|abc123" (empty user_id but session_id present)
    $userContext = ($userId ?? '') . '|' . $currentSessionId;
    
    // Create signature from track ID, variation, expiration, user context, and secret
    $data = $trackId . '|' . ($variationIndex ?? '') . '|' . $expires . '|' . $userContext;
    $signature = hash_hmac('sha256', $data, AUDIO_TOKEN_SECRET);
    
    // Return short token (first 16 chars of signature) and expiration
    return [
        'token' => substr($signature, 0, 16),
        'expires' => $expires
    ];
}

/**
 * Check if a token has exceeded max uses
 * 
 * @param string $token The token to check
 * @param int $trackId The track ID
 * @return array ['used' => bool, 'use_count' => int, 'expired' => bool]
 */
function checkTokenUsage($token, $trackId) {
    // Ensure session is started
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    
    // Initialize token usage tracking in session if not exists
    if (!isset($_SESSION['audio_token_usage'])) {
        $_SESSION['audio_token_usage'] = [];
    }
    
    // Create unique key for this token+track combination
    $tokenKey = $token . '|' . $trackId;
    
    // Check if token has been used
    if (isset($_SESSION['audio_token_usage'][$tokenKey])) {
        $useCount = $_SESSION['audio_token_usage'][$tokenKey]['count'];
        
        // Check if exceeded max uses
        if ($useCount >= AUDIO_TOKEN_MAX_USES) {
            return [
                'used' => true,
                'use_count' => $useCount,
                'expired' => true
            ];
        }
        
        // Under max uses - allow but track
        return [
            'used' => false,
            'use_count' => $useCount,
            'expired' => false
        ];
    }
    
    // Token not used yet
    return [
        'used' => false,
        'use_count' => 0,
        'expired' => false
    ];
}

/**
 * Mark a token as used (increment usage counter)
 * 
 * @param string $token The token to mark
 * @param int $trackId The track ID
 */
function markTokenUsed($token, $trackId) {
    // Ensure session is started
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    
    // Initialize token usage tracking in session if not exists
    if (!isset($_SESSION['audio_token_usage'])) {
        $_SESSION['audio_token_usage'] = [];
    }
    
    // Create unique key for this token+track combination
    $tokenKey = $token . '|' . $trackId;
    
    $now = time();
    
    if (isset($_SESSION['audio_token_usage'][$tokenKey])) {
        // Increment usage count
        $_SESSION['audio_token_usage'][$tokenKey]['count']++;
        $_SESSION['audio_token_usage'][$tokenKey]['last_use'] = $now;
    } else {
        // First use - record timestamp
        $_SESSION['audio_token_usage'][$tokenKey] = [
            'first_use' => $now,
            'last_use' => $now,
            'count' => 1
        ];
    }
    
    // Clean up old token usage data (older than token expiry)
    $cleanupTime = $now - (AUDIO_TOKEN_EXPIRY + 60);
    foreach ($_SESSION['audio_token_usage'] as $key => $usage) {
        if ($usage['first_use'] < $cleanupTime) {
            unset($_SESSION['audio_token_usage'][$key]);
        }
    }
}

/**
 * Validate an audio token
 * NOW VALIDATES USER/SESSION BINDING AND USAGE LIMIT - tokens cannot be shared or used unlimited times
 * 
 * @param int $trackId The track ID
 * @param int|null $variationIndex Optional variation index
 * @param string $token The token to validate
 * @param int $expires The expiration timestamp
 * @param int|null $userId User ID to validate against (required)
 * @param string|null $sessionId Session ID to validate against (required)
 * @param bool $checkUsage Whether to check usage limit (default: true)
 * @return bool True if valid, false otherwise
 */
function validateAudioToken($trackId, $variationIndex, $token, $expires, $userId = null, $sessionId = null, $checkUsage = true) {
    // Check if expired
    if (time() > $expires) {
        return false;
    }
    
    // Ensure session is started
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    
    // Always use current session ID (must match the session used when generating token)
    $currentSessionId = session_id();
    
    // CRITICAL: Validate user context matches (user_id + session_id)
    // Tokens are bound to specific user/session - cannot be shared
    // Build user context the same way as generation: user_id|session_id
    $userContext = ($userId ?? '') . '|' . $currentSessionId;
    
    // Recreate the signature with user context
    $data = $trackId . '|' . ($variationIndex ?? '') . '|' . $expires . '|' . $userContext;
    $expectedSignature = hash_hmac('sha256', $data, AUDIO_TOKEN_SECRET);
    $expectedToken = substr($expectedSignature, 0, 16);
    
    // Constant-time comparison to prevent timing attacks
    if (!hash_equals($expectedToken, $token)) {
        return false;
    }
    
    // Check usage limit if enabled
    if ($checkUsage) {
        $usage = checkTokenUsage($token, $trackId);
        if ($usage['used'] && $usage['expired']) {
            // Token has exceeded max uses
            return false;
        }
    }
    
    return true;
}

/**
 * Generate a complete signed audio URL
 * 
 * @param int $trackId The track ID
 * @param int|null $variationIndex Optional variation index
 * @param int|null $expiresIn Seconds until expiration (default: AUDIO_TOKEN_EXPIRY)
 * @return string The signed URL
 */
function getSignedAudioUrl($trackId, $variationIndex = null, $expiresIn = null, $userId = null, $sessionId = null) {
    $tokenData = generateAudioToken($trackId, $variationIndex, $expiresIn, $userId, $sessionId);
    
    $url = '/utils/play_audio.php?id=' . $trackId;
    
    if ($variationIndex !== null) {
        $url .= '&variation=' . $variationIndex;
    }
    
    $url .= '&token=' . $tokenData['token'];
    $url .= '&expires=' . $tokenData['expires'];
    
    return $url;
}

/**
 * Generate signed audio URL for JavaScript (returns JSON-encodable data)
 * 
 * @param int $trackId The track ID
 * @param int|null $variationIndex Optional variation index
 * @return array URL components for JavaScript
 */
function getSignedAudioUrlComponents($trackId, $variationIndex = null, $userId = null, $sessionId = null) {
    $tokenData = generateAudioToken($trackId, $variationIndex, null, $userId, $sessionId);
    
    return [
        'baseUrl' => '/utils/play_audio.php',
        'trackId' => $trackId,
        'variationIndex' => $variationIndex,
        'token' => $tokenData['token'],
        'expires' => $tokenData['expires']
    ];
}


CasperSecurity Mini