Skip to content

SimpleSAMLphp Session Management Issues with Nginx Unit #2484

@vaillen

Description

@vaillen

Overview

We've encountered significant challenges when deploying SimpleSAMLphp behind Nginx Unit, particularly with session persistence and SAML authentication flows. While SimpleSAMLphp functions correctly with Apache, we're consistently seeing session data loss when using Nginx Unit, primarily manifesting as "The POST data we should restore was lost" errors during SAML authentication.

Symptoms

  1. Session and POST Data Loss

    • SAML responses from IdP to SP through the ACS endpoint fail to maintain POST data
    • Session data doesn't persist between authentication requests
    • SAML authentication flow breaks with "The POST data we should restore was lost"
  2. Nginx Unit-Specific Challenges

    • Inability to modify incoming request headers in Nginx Unit
    • No straightforward method to inject X-Forwarded-Proto: https header
    • Routing configuration in Nginx Unit significantly impacts SimpleSAMLphp's behavior

Root Cause Analysis

  1. isHTTPS() Implementation Limitations
    The current implementation in SimpleSAMLphp:

    public function isHTTPS(): bool
    {
        return strpos($this->getSelfURL(), 'https://') === 0;
    }

    This approach is too simplistic as it only checks the URL scheme without considering proxy headers or the specific requirements of SAML ACS endpoints.

  2. Session Cookie Handling

    • Mismatch between session.cookie.secure settings and actual protocol
    • Cross-domain POST request issues with SameSite cookie attribute
    • Improper session.cookie.path configurations leading to session data loss
  3. Lack of POST Data Recovery
    No robust mechanism exists to recover POST data when session failures occur

Implemented Solutions

  1. Enhanced isHTTPS() Method
    We've improved the HTTPS detection to handle various proxy configurations and edge cases:

    public function isHTTPS(): bool
    {
        // Special handling for SAML ACS endpoint
        $currentUrl = $_SERVER['REQUEST_URI'] ?? '';
        if (strpos($currentUrl, '/module.php/saml/sp/saml2-acs.php/') !== false) {
            // Force HTTPS for SAML ACS to maintain session integrity
            Logger::debug('isHTTPS: Treating SAML ACS endpoint as HTTPS for session handling');
            return true;
        }
        
        // Support for common proxy headers
        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) 
            && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
            return true;
        }
        
        // Standard HTTPS check
        if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
            return true;
        }
        
        // Fallback to original URL-based check
        return strpos($this->getSelfURL(), 'https://') === 0;
    }
  2. Persistent POST Data Storage
    Added a fallback mechanism to store POST data in temporary files:

    private function savePOSTData(Session $session, string $destination, array $data): string
    {
        // ... existing code ...
        
        // Store POST data in temp file as fallback
        $tempDir = sys_get_temp_dir() . '/simplesaml';
        if (!is_dir($tempDir)) {
            mkdir($tempDir, 0700, true);
        }
        $tempFile = $tempDir . '/post_' . $id . '.tmp';
        
        file_put_contents($tempFile, json_encode($postData), LOCK_EX);
        Logger::debug("Backed up POST data to temporary file: " . basename($tempFile));
        
        return $id;
    }
  3. Robust POST Data Recovery
    Implemented a recovery mechanism for session data loss scenarios:

    public function postredirect(Request $request): Response
    {
        // ... existing code ...
        
        $postData = $session->getData('core_postdatalink', $postId);
        if ($postData === null) {
            $tempFile = sys_get_temp_dir() . "/simplesaml/post_{$postId}.tmp";
            if (file_exists($tempFile)) {
                $content = file_get_contents($tempFile);
                if ($content !== false) {
                    $postData = json_decode($content, true);
                    unlink($tempFile);
                    
                    if (json_last_error() === JSON_ERROR_NONE) {
                        Logger::info("Recovered POST data from temp file for ID: {$postId}");
                    } else {
                        Logger::error("Failed to decode POST data from temp file: " . json_last_error_msg());
                        throw new \RuntimeException('The POST data we should restore was corrupted.');
                    }
                }
            }
            
            if ($postData === null) {
                Logger::error("Failed to recover POST data for ID: {$postId}");
                throw new \RuntimeException('The POST data we should restore was lost.');
            }
        }
        
        // ... rest of the implementation ...
    }

Recommended Improvements for SimpleSAMLphp

  1. Enhanced isHTTPS() Method

    • Add comprehensive proxy header support (X-Forwarded-Proto, X-Forwarded-SSL)
    • Implement configurable URL patterns that should always be treated as HTTPS
    • Document proxy configuration requirements and best practices
  2. Robust POST Data Handling

    • Implement built-in fallback storage mechanisms for POST data
    • Add session recovery capabilities with configurable storage backends
    • Include detailed logging for troubleshooting session issues
  3. Improved Web Server Compatibility

    • Create dedicated documentation for Nginx Unit configuration
    • Add automated tests for different web server configurations
    • Document common pitfalls and solutions for various deployment scenarios

Conclusion

While our current workarounds address the immediate issues with Nginx Unit, we strongly recommend incorporating these improvements into the main SimpleSAMLphp codebase. This would significantly enhance compatibility across different web servers and deployment scenarios, particularly in environments with complex proxy configurations or mixed HTTP/HTTPS requirements. The proposed changes would make SimpleSAMLphp more robust and easier to maintain in enterprise environments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions