security: improve TeapotController header sanitization #13

Closed
opened 2026-02-20 02:39:11 +00:00 by Clotho · 0 comments
Member

Issue

src/Mod/Hub/Controllers/TeapotController.php uses header blacklisting instead of whitelisting.

Current Implementation (lines 50-58)

private function sanitizeHeaders(array $headers): array
{
    $sanitized = $headers;
    unset(
        $sanitized['cookie'],
        $sanitized['authorization'],
        $sanitized['php-auth-user'],
        $sanitized['php-auth-pw']
    );
    return $sanitized;
}

Problem:

  • Blacklist approach: removes specific sensitive headers
  • Could miss new sensitive headers added by proxies/frameworks
  • Better security practice: whitelist only needed headers

Use whitelisting approach:

private function sanitizeHeaders(array $headers): array
{
    // Whitelist approach: only keep headers useful for bot detection
    $allowedHeaders = [
        'user-agent',
        'accept',
        'accept-language',
        'accept-encoding',
        'referer',
        'origin',
        'x-requested-with',
        'x-forwarded-for',
        'x-real-ip',
        'cf-connecting-ip', // Cloudflare
        'x-client-ip',
    ];

    return array_intersect_key(
        $headers,
        array_flip($allowedHeaders)
    );
}

Additional Issues

IPv6 Localhost Detection (line 62)

Current:

if (in_array($ip, ['127.0.0.1', '::1'])) {
    return response('Forbidden', 403);
}

Missing:

  • IPv4-mapped IPv6 addresses: ::ffff:127.0.0.1
  • Docker bridge network: 172.17.0.0/16
  • Private network ranges: 10.0.0.0/8, 192.168.0.0/16

Recommended fix:

if ($this->isPrivateIp($ip)) {
    return response('Forbidden', 403);
}

private function isPrivateIp(string $ip): bool
{
    // Handle IPv4-mapped IPv6
    $ip = str_replace('::ffff:', '', $ip);
    
    return filter_var($ip, FILTER_VALIDATE_IP, 
        FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
}

Referer Validation (line 47)

Current:

$referer = substr($request->header('referer', 'direct'), 0, 500);

Issue:

  • No validation that referer is a valid URL
  • Could contain malicious payloads

Recommended:

$referer = $request->header('referer', 'direct');
if ($referer !== 'direct') {
    $referer = filter_var($referer, FILTER_VALIDATE_URL) 
        ? substr($referer, 0, 500) 
        : 'invalid-url';
}

Testing Requirements

  • Test whitelist blocks sensitive headers
  • Test IPv4-mapped IPv6 detection
  • Test private IP range detection
  • Test referer validation

Priority

Medium - Defense-in-depth improvement, not critical vulnerability.

Discovered by

Automatic codebase scan (issue #3)

## Issue `src/Mod/Hub/Controllers/TeapotController.php` uses header blacklisting instead of whitelisting. ## Current Implementation (lines 50-58) ```php private function sanitizeHeaders(array $headers): array { $sanitized = $headers; unset( $sanitized['cookie'], $sanitized['authorization'], $sanitized['php-auth-user'], $sanitized['php-auth-pw'] ); return $sanitized; } ``` **Problem:** - Blacklist approach: removes specific sensitive headers - Could miss new sensitive headers added by proxies/frameworks - Better security practice: whitelist only needed headers ## Recommended Fix Use whitelisting approach: ```php private function sanitizeHeaders(array $headers): array { // Whitelist approach: only keep headers useful for bot detection $allowedHeaders = [ 'user-agent', 'accept', 'accept-language', 'accept-encoding', 'referer', 'origin', 'x-requested-with', 'x-forwarded-for', 'x-real-ip', 'cf-connecting-ip', // Cloudflare 'x-client-ip', ]; return array_intersect_key( $headers, array_flip($allowedHeaders) ); } ``` ## Additional Issues ### IPv6 Localhost Detection (line 62) **Current:** ```php if (in_array($ip, ['127.0.0.1', '::1'])) { return response('Forbidden', 403); } ``` **Missing:** - IPv4-mapped IPv6 addresses: `::ffff:127.0.0.1` - Docker bridge network: `172.17.0.0/16` - Private network ranges: `10.0.0.0/8`, `192.168.0.0/16` **Recommended fix:** ```php if ($this->isPrivateIp($ip)) { return response('Forbidden', 403); } private function isPrivateIp(string $ip): bool { // Handle IPv4-mapped IPv6 $ip = str_replace('::ffff:', '', $ip); return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false; } ``` ### Referer Validation (line 47) **Current:** ```php $referer = substr($request->header('referer', 'direct'), 0, 500); ``` **Issue:** - No validation that referer is a valid URL - Could contain malicious payloads **Recommended:** ```php $referer = $request->header('referer', 'direct'); if ($referer !== 'direct') { $referer = filter_var($referer, FILTER_VALIDATE_URL) ? substr($referer, 0, 500) : 'invalid-url'; } ``` ## Testing Requirements - Test whitelist blocks sensitive headers - Test IPv4-mapped IPv6 detection - Test private IP range detection - Test referer validation ## Priority **Medium** - Defense-in-depth improvement, not critical vulnerability. ## Discovered by Automatic codebase scan (issue #3)
Clotho added the
review
discovery
labels 2026-02-20 02:39:11 +00:00
Charon added the
clotho
label 2026-02-20 10:57:34 +00:00
Sign in to join this conversation.
No description provided.