Starship Rewards API

Security Considerations

B2B security best practices for API key management and safe storage

Security Considerations for B2B Integration

Essential security patterns for B2B clients integrating with the Starship Rewards API. This guide focuses on server-to-server authentication security, API key management, and safe credential storage practices.

API Key Management

Key Rotation Strategy

Implement regular API credential rotation to minimize security risks:

# Rotate API credentials (typically done monthly/quarterly)
# Step 1: Generate new credentials in your dashboard
# Step 2: Test new credentials in staging environment
curl -X POST {{host}}/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "new-api-key@yourcompany.com",
    "password": "new-secure-api-password"
  }'

# Step 3: Update production systems with new credentials
# Step 4: Revoke old credentials after confirming new ones work
curl -X POST {{host}}/auth/revoke-credentials \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer current_access_token" \
  -d '{
    "credential_id": "old_credential_id",
    "reason": "routine_rotation"
  }'

# Monitor for any failed authentication attempts with old credentials
<?php
class APICredentialManager {
    private $currentCredentials;
    private $newCredentials;
    private $rotationSchedule;

    public function __construct() {
        $this->loadCredentials();
        $this->rotationSchedule = 90; // Rotate every 90 days
    }

    public function shouldRotateCredentials() {
        $lastRotation = $this->getLastRotationDate();
        $daysSinceRotation = (time() - $lastRotation) / (24 * 60 * 60);
        return $daysSinceRotation >= $this->rotationSchedule;
    }

    public function performCredentialRotation() {
        if (!$this->shouldRotateCredentials()) {
            return false;
        }

        try {
            // Step 1: Test new credentials
            $this->testNewCredentials();

            // Step 2: Update all systems
            $this->updateProductionCredentials();

            // Step 3: Revoke old credentials
            $this->revokeOldCredentials();

            // Step 4: Update rotation log
            $this->logRotation();

            return true;
        } catch (Exception $e) {
            error_log('Credential rotation failed: ' . $e->getMessage());
            $this->notifySecurityTeam($e);
            return false;
        }
    }

    private function testNewCredentials() {
        $auth = new StarshipAuth();
        $result = $auth->login(
            $this->newCredentials['email'],
            $this->newCredentials['password']
        );

        if (!$result) {
            throw new Exception('New credentials authentication failed');
        }
    }

    private function notifySecurityTeam($error) {
        // Send alerts to security team about failed rotation
        mail(
            'security@yourcompany.com',
            'API Credential Rotation Failed',
            'Error: ' . $error->getMessage() . "\n" .
            'Time: ' . date('Y-m-d H:i:s') . "\n" .
            'System: Production Starship Rewards API Integration'
        );
    }
}
?>

Secure Credential Storage

Server-Side Storage Best Practices

Store API credentials securely in your server infrastructure:

# Use environment variables for credentials (recommended)
export STARSHIP_API_EMAIL="your-api-email@company.com"
export STARSHIP_API_PASSWORD="your-secure-password"

# Access credentials from environment
curl -X POST {{host}}/auth/login \
  -H "Content-Type: application/json" \
  -d "{
    \"email\": \"$STARSHIP_API_EMAIL\",
    \"password\": \"$STARSHIP_API_PASSWORD\"
  }"

# Alternative: Use credential files with restricted permissions
# Create credentials file (readable only by application user)
echo '{"email":"api@company.com","password":"secure_pass"}' > /etc/starship/credentials.json
chmod 600 /etc/starship/credentials.json

# Use credentials from file
curl -X POST {{host}}/auth/login \
  -H "Content-Type: application/json" \
  -d @/etc/starship/credentials.json

# For containerized environments (Docker/Kubernetes)
# Mount credentials as secrets
docker run -e STARSHIP_API_EMAIL="$STARSHIP_API_EMAIL" \
           -e STARSHIP_API_PASSWORD="$STARSHIP_API_PASSWORD" \
           your-app:latest
<?php
class SecureCredentialStorage {
    private const ENCRYPTION_METHOD = 'AES-256-GCM';
    private $encryptionKey;

    public function __construct() {
        // Load encryption key from secure location
        $this->encryptionKey = $this->loadEncryptionKey();
    }

    // Method 1: Environment Variables (Recommended)
    public function loadFromEnvironment() {
        return [
            'email' => getenv('STARSHIP_API_EMAIL'),
            'password' => getenv('STARSHIP_API_PASSWORD')
        ];
    }

    // Method 2: Encrypted Configuration Files
    public function loadFromEncryptedFile($filePath) {
        if (!file_exists($filePath)) {
            throw new Exception('Credentials file not found');
        }

        // Verify file permissions (should be 600)
        $permissions = fileperms($filePath) & 0777;
        if ($permissions !== 0600) {
            throw new Exception('Credentials file has insecure permissions');
        }

        $encryptedData = file_get_contents($filePath);
        return $this->decrypt($encryptedData);
    }

    // Method 3: Database Storage (with encryption)
    public function loadFromDatabase($credentialId) {
        // Use prepared statements to prevent injection
        $stmt = $this->pdo->prepare(
            "SELECT encrypted_credentials FROM api_credentials WHERE id = ? AND active = 1"
        );
        $stmt->execute([$credentialId]);

        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            throw new Exception('Credentials not found');
        }

        return $this->decrypt($row['encrypted_credentials']);
    }

    // Method 4: External Secret Management (HashiCorp Vault, AWS Secrets Manager)
    public function loadFromVault($secretPath) {
        // Example for HashiCorp Vault
        $vaultToken = getenv('VAULT_TOKEN');
        $vaultUrl = getenv('VAULT_ADDR') . '/v1/' . $secretPath;

        $context = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => 'X-Vault-Token: ' . $vaultToken
            ]
        ]);

        $response = file_get_contents($vaultUrl, false, $context);
        $data = json_decode($response, true);

        return [
            'email' => $data['data']['email'],
            'password' => $data['data']['password']
        ];
    }

    private function encrypt($data) {
        $iv = random_bytes(16);
        $tag = '';
        $encrypted = openssl_encrypt(
            json_encode($data),
            self::ENCRYPTION_METHOD,
            $this->encryptionKey,
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        return base64_encode($iv . $tag . $encrypted);
    }

    private function decrypt($encryptedData) {
        $data = base64_decode($encryptedData);
        $iv = substr($data, 0, 16);
        $tag = substr($data, 16, 16);
        $encrypted = substr($data, 32);

        $decrypted = openssl_decrypt(
            $encrypted,
            self::ENCRYPTION_METHOD,
            $this->encryptionKey,
            OPENSSL_RAW_DATA,
            $iv,
            $tag
        );

        return json_decode($decrypted, true);
    }

    private function loadEncryptionKey() {
        // Load from environment or secure key management system
        $key = getenv('ENCRYPTION_KEY');
        if (!$key) {
            throw new Exception('Encryption key not found');
        }
        return base64_decode($key);
    }
}
?>

Token Storage and Management

Access Token Security

Handle access tokens securely in your server applications:

# Store tokens in memory or secure cache (Redis with encryption)
# Never store tokens in log files or databases without encryption

# Use secure headers when making authenticated requests
curl -X GET {{host}}/api/v1/products \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "User-Agent: YourCompany-Integration/1.0" \
  -H "X-Request-ID: $(uuidgen)" \
  --connect-timeout 30 \
  --max-time 60 \
  --retry 3 \
  --retry-delay 1

# Monitor token usage and detect anomalies
echo "$(date): API request to /products" >> /var/log/starship-api.log
<?php
class SecureTokenManager {
    private $redis;
    private $encryptionKey;

    public function __construct() {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1', 6379);
        $this->encryptionKey = $this->loadEncryptionKey();
    }

    public function storeTokens($accessToken, $refreshToken, $expiresIn) {
        $tokenData = [
            'access_token' => $accessToken,
            'refresh_token' => $refreshToken,
            'expires_at' => time() + $expiresIn,
            'created_at' => time()
        ];

        // Encrypt before storing
        $encryptedData = $this->encrypt($tokenData);

        // Store in Redis with TTL
        $key = 'starship_tokens:' . session_id();
        $this->redis->setex($key, $expiresIn + 300, $encryptedData); // 5-minute buffer

        // Log token creation (without token values)
        error_log('New tokens stored for session: ' . session_id());
    }

    public function getValidAccessToken() {
        $key = 'starship_tokens:' . session_id();
        $encryptedData = $this->redis->get($key);

        if (!$encryptedData) {
            throw new Exception('No tokens found');
        }

        $tokenData = $this->decrypt($encryptedData);

        // Check if token is near expiration (refresh 5 minutes early)
        if ((time() + 300) >= $tokenData['expires_at']) {
            $this->refreshTokens($tokenData['refresh_token']);
            return $this->getValidAccessToken(); // Recursive call after refresh
        }

        return $tokenData['access_token'];
    }

    public function clearTokens() {
        $key = 'starship_tokens:' . session_id();
        $this->redis->del($key);
        error_log('Tokens cleared for session: ' . session_id());
    }

    // Add request logging and monitoring
    public function makeSecureRequest($url, $method = 'GET', $data = null) {
        $requestId = uniqid('req_', true);
        $startTime = microtime(true);

        try {
            $accessToken = $this->getValidAccessToken();

            $headers = [
                'Authorization: Bearer ' . $accessToken,
                'Content-Type: application/json',
                'User-Agent: YourCompany-Integration/1.0',
                'X-Request-ID: ' . $requestId
            ];

            $context = [
                'http' => [
                    'method' => $method,
                    'header' => implode("\r\n", $headers),
                    'timeout' => 30
                ]
            ];

            if ($data) {
                $context['http']['content'] = json_encode($data);
            }

            $response = file_get_contents($url, false, stream_context_create($context));
            $duration = microtime(true) - $startTime;

            // Log successful request
            error_log(sprintf(
                'API Request: %s %s - Duration: %.3fs - Request ID: %s',
                $method,
                parse_url($url, PHP_URL_PATH),
                $duration,
                $requestId
            ));

            return json_decode($response, true);

        } catch (Exception $e) {
            $duration = microtime(true) - $startTime;

            // Log failed request
            error_log(sprintf(
                'API Request Failed: %s %s - Error: %s - Duration: %.3fs - Request ID: %s',
                $method,
                parse_url($url, PHP_URL_PATH),
                $e->getMessage(),
                $duration,
                $requestId
            ));

            throw $e;
        }
    }
}
?>

Network Security

HTTPS and Certificate Validation

Ensure secure communication with the Starship Rewards API:

# Always use HTTPS and verify certificates
curl -X GET {{host}}/api/v1/products \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  --cacert /path/to/ca-certificates.crt \
  --cert-status \
  --tlsv1.2 \
  --ssl-reqd

# For production, never use --insecure or -k flags
# These disable certificate verification!

# Monitor certificate expiration
echo | openssl s_client -servername api.starshiprewards.com \
                       -connect api.starshiprewards.com:443 2>/dev/null \
     | openssl x509 -noout -dates
<?php
class SecureHTTPClient {
    private $defaultOptions;

    public function __construct() {
        $this->defaultOptions = [
            'http' => [
                'method' => 'GET',
                'timeout' => 30,
                'user_agent' => 'YourCompany-Integration/1.0',
                'protocol_version' => 1.1
            ],
            'ssl' => [
                'verify_peer' => true,
                'verify_peer_name' => true,
                'allow_self_signed' => false,
                'cafile' => '/etc/ssl/certs/ca-certificates.crt',
                'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
                'ciphers' => 'ECDHE+AESGCM:ECDHE+AES256:ECDHE+AES128:!aNULL:!MD5:!DSS',
                'disable_compression' => true
            ]
        ];
    }

    public function makeRequest($url, $method = 'GET', $data = null, $headers = []) {
        // Validate URL is HTTPS
        if (strpos($url, 'https://') !== 0) {
            throw new Exception('Only HTTPS URLs are allowed');
        }

        $options = $this->defaultOptions;
        $options['http']['method'] = $method;

        if (!empty($headers)) {
            $options['http']['header'] = implode("\r\n", $headers);
        }

        if ($data && in_array($method, ['POST', 'PUT', 'PATCH'])) {
            $options['http']['content'] = is_string($data) ? $data : json_encode($data);
        }

        // Log certificate verification
        $this->verifyCertificate($url);

        $context = stream_context_create($options);
        $response = file_get_contents($url, false, $context);

        if ($response === false) {
            throw new Exception('HTTP request failed');
        }

        return $response;
    }

    private function verifyCertificate($url) {
        $host = parse_url($url, PHP_URL_HOST);
        $port = parse_url($url, PHP_URL_PORT) ?: 443;

        $context = stream_context_create([
            'ssl' => [
                'capture_peer_cert' => true,
                'verify_peer' => true,
                'verify_peer_name' => true
            ]
        ]);

        $stream = stream_socket_client(
            "ssl://{$host}:{$port}",
            $errno,
            $errstr,
            30,
            STREAM_CLIENT_CONNECT,
            $context
        );

        if (!$stream) {
            throw new Exception("SSL connection failed: $errstr ($errno)");
        }

        $params = stream_context_get_params($stream);
        $cert = $params['options']['ssl']['peer_certificate'];
        $certData = openssl_x509_parse($cert);

        // Check certificate expiration
        $expiryDate = $certData['validTo_time_t'];
        $daysUntilExpiry = ($expiryDate - time()) / (24 * 60 * 60);

        if ($daysUntilExpiry < 30) {
            error_log("Warning: SSL certificate for {$host} expires in {$daysUntilExpiry} days");
        }

        fclose($stream);
    }
}
?>

Monitoring and Alerting

Security Event Monitoring

Monitor your integration for security issues:

# Monitor authentication failures
grep "401\|403" /var/log/starship-api.log | tail -10

# Set up automated monitoring
#!/bin/bash
# monitor-api-security.sh

LOG_FILE="/var/log/starship-api.log"
ALERT_EMAIL="security@yourcompany.com"

# Check for suspicious activity
FAILED_AUTHS=$(grep -c "401 Unauthorized" $LOG_FILE)
FORBIDDEN_ACCESS=$(grep -c "403 Forbidden" $LOG_FILE)

if [ $FAILED_AUTHS -gt 10 ]; then
    echo "High number of failed authentications detected: $FAILED_AUTHS" | \
    mail -s "Starship Rewards API Security Alert" $ALERT_EMAIL
fi

if [ $FORBIDDEN_ACCESS -gt 5 ]; then
    echo "High number of forbidden access attempts: $FORBIDDEN_ACCESS" | \
    mail -s "Starship Rewards API Security Alert" $ALERT_EMAIL
fi

# Run this script every 15 minutes via cron
# */15 * * * * /path/to/monitor-api-security.sh
<?php
class SecurityMonitor {
    private $alertThresholds = [
        'failed_auth' => 10,
        'forbidden_access' => 5,
        'unusual_requests' => 100
    ];

    public function monitorSecurityEvents() {
        $events = $this->collectSecurityEvents();

        foreach ($events as $eventType => $count) {
            if ($count > $this->alertThresholds[$eventType]) {
                $this->sendSecurityAlert($eventType, $count);
            }
        }
    }

    private function collectSecurityEvents() {
        $events = [
            'failed_auth' => 0,
            'forbidden_access' => 0,
            'unusual_requests' => 0
        ];

        // Check log files or database for security events
        $logFile = '/var/log/starship-api.log';
        if (file_exists($logFile)) {
            $logs = file_get_contents($logFile);
            $events['failed_auth'] = substr_count($logs, '401 Unauthorized');
            $events['forbidden_access'] = substr_count($logs, '403 Forbidden');
        }

        return $events;
    }

    private function sendSecurityAlert($eventType, $count) {
        $subject = 'Starship Rewards API Security Alert: ' . ucwords(str_replace('_', ' ', $eventType));
        $message = sprintf(
            "Security event detected:\n" .
            "Event Type: %s\n" .
            "Count: %d\n" .
            "Threshold: %d\n" .
            "Time: %s\n" .
            "Server: %s\n",
            $eventType,
            $count,
            $this->alertThresholds[$eventType],
            date('Y-m-d H:i:s'),
            gethostname()
        );

        mail('security@yourcompany.com', $subject, $message);
        error_log("Security alert sent: $eventType ($count events)");
    }

    public function auditAPIUsage() {
        // Track API usage patterns
        $usage = [
            'total_requests' => $this->getTotalRequests(),
            'unique_endpoints' => $this->getUniqueEndpoints(),
            'peak_hours' => $this->getPeakUsageHours(),
            'error_rate' => $this->getErrorRate()
        ];

        // Store for analysis
        $this->storeUsageMetrics($usage);

        return $usage;
    }
}

// Set up monitoring cron job
if (php_sapi_name() === 'cli') {
    $monitor = new SecurityMonitor();
    $monitor->monitorSecurityEvents();
    $monitor->auditAPIUsage();
}
?>

B2B Security Best Practices Summary

1. Credential Management

  • Rotate API credentials every 90 days minimum
  • Use environment variables or secure vaults for credential storage
  • Never commit credentials to version control
  • Implement automated credential rotation processes

2. Token Security

  • Store access tokens in encrypted, secure storage (Redis, database with encryption)
  • Implement automatic token refresh before expiration
  • Clear tokens immediately after use when possible
  • Monitor token usage patterns for anomalies

3. Network Security

  • Always use HTTPS for API communications
  • Validate SSL certificates and monitor expiration
  • Implement proper timeout and retry logic
  • Use secure TLS configurations (TLS 1.2+)

4. Monitoring and Alerting

  • Log all authentication attempts and failures
  • Set up automated alerts for suspicious activity
  • Monitor API usage patterns and error rates
  • Implement security incident response procedures

5. Infrastructure Security

  • Run applications with minimal required permissions
  • Keep systems and dependencies updated
  • Use secure configuration management
  • Implement proper backup and recovery procedures

By following these B2B-focused security practices, you'll maintain a secure integration with the Starship Rewards API while protecting your systems and data from potential threats.