Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

php-aegis Documentation

1. Introduction

This documentation covers security best practices and advanced usage of php-aegis.

2. OWASP Top 10 Coverage

php-aegis helps mitigate several OWASP Top 10 vulnerabilities:

OWASP Category Risk php-aegis Mitigation

A03:2021 Injection

XSS, Command Injection

Sanitizer::html(), Sanitizer::stripTags()

A07:2021 Auth Failures

Weak credential validation

Validator::email() for username validation

3. Input Validation Strategies

3.1. Allowlist vs Blocklist

Always prefer allowlist validation:

// GOOD: Allowlist - only accept known-good values
$allowedRoles = ['user', 'admin', 'moderator'];
if (!in_array($role, $allowedRoles, true)) {
    throw new InvalidArgumentException('Invalid role');
}

// BAD: Blocklist - trying to block bad values
$blockedChars = ['<', '>', '"'];  // Will miss attack vectors

3.2. Validation Order

  1. Type validation - Ensure correct PHP type

  2. Format validation - Match expected pattern (email, URL, etc.)

  3. Range validation - Within acceptable bounds

  4. Business validation - Meets domain requirements

function validateAge(mixed $input): int
{
    // 1. Type validation
    if (!is_numeric($input)) {
        throw new TypeError('Age must be numeric');
    }

    $age = (int) $input;

    // 2. Range validation
    if ($age < 0 || $age > 150) {
        throw new RangeException('Age out of valid range');
    }

    return $age;
}

4. Output Encoding

4.1. Context-Aware Encoding

Different contexts require different encoding:

Context Risk Solution

HTML Body

XSS

Sanitizer::html()

HTML Attribute

Attribute injection

Sanitizer::html() + quote attributes

JavaScript

JS injection

json_encode() with flags

URL Parameter

Parameter injection

urlencode()

CSS

CSS injection

Avoid user input in CSS

4.2. HTML Context Examples

$sanitizer = new Sanitizer();
$userInput = '<script>alert("xss")</script>';

// HTML body - use html()
echo '<p>' . $sanitizer->html($userInput) . '</p>';

// HTML attribute - quote and encode
echo '<input value="' . $sanitizer->html($userInput) . '">';

// JavaScript context - use json_encode
echo '<script>var data = ' . json_encode($userInput, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) . ';</script>';

5. Common Attack Patterns

5.1. XSS (Cross-Site Scripting)

5.1.1. Reflected XSS

// VULNERABLE
echo "Hello, " . $_GET['name'];

// SAFE
$sanitizer = new Sanitizer();
echo "Hello, " . $sanitizer->html($_GET['name'] ?? '');

5.1.2. Stored XSS

// When storing user content
$content = $_POST['comment'];
// Validate first
if (strlen($content) > 10000) {
    throw new LengthException('Comment too long');
}
// Store as-is, encode on output

// When displaying
$sanitizer = new Sanitizer();
echo $sanitizer->html($storedContent);

5.2. Email Header Injection

$validator = new Validator();

// VULNERABLE - allows header injection
$to = $_POST['email'];
mail($to, "Subject", "Body");

// SAFE - validate email format
$email = $_POST['email'] ?? '';
if (!$validator->email($email)) {
    throw new InvalidArgumentException('Invalid email');
}
// Also check for newlines
if (preg_match('/[\r\n]/', $email)) {
    throw new InvalidArgumentException('Invalid characters in email');
}
mail($email, "Subject", "Body");

6. Integration Patterns

6.1. With Frameworks

6.1.1. Laravel

// In a Form Request
use PhpAegis\Validator;

public function rules(): array
{
    return [
        'email' => ['required', function ($attribute, $value, $fail) {
            $validator = new Validator();
            if (!$validator->email($value)) {
                $fail('Invalid email format');
            }
        }],
    ];
}

6.1.2. Symfony

// Custom constraint validator
use PhpAegis\Validator as AegisValidator;

class AegisEmailValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint): void
    {
        $validator = new AegisValidator();
        if (!$validator->email($value)) {
            $this->context->buildViolation($constraint->message)
                ->addViolation();
        }
    }
}

6.2. Middleware Pattern

use PhpAegis\Sanitizer;

class SanitizeMiddleware
{
    private Sanitizer $sanitizer;

    public function __construct()
    {
        $this->sanitizer = new Sanitizer();
    }

    public function handle(Request $request, callable $next): Response
    {
        // Sanitize specific input fields
        $sanitized = [];
        foreach (['name', 'comment', 'bio'] as $field) {
            if ($request->has($field)) {
                $sanitized[$field] = $this->sanitizer->stripTags(
                    $request->input($field)
                );
            }
        }
        $request->merge($sanitized);

        return $next($request);
    }
}

7. Testing Security

7.1. Test Vectors

Include these test cases for XSS prevention:

public function xssTestVectors(): array
{
    return [
        ['<script>alert(1)</script>'],
        ['<img src=x onerror=alert(1)>'],
        ['<svg onload=alert(1)>'],
        ["javascript:alert('xss')"],
        ['"><script>alert(1)</script>'],
        ["'><script>alert(1)</script>"],
        ['<iframe src="javascript:alert(1)">'],
        ['<body onload=alert(1)>'],
        ['<input onfocus=alert(1) autofocus>'],
        ['<marquee onstart=alert(1)>'],
    ];
}

/**
 * @dataProvider xssTestVectors
 */
public function test_sanitizer_prevents_xss(string $attack): void
{
    $sanitizer = new Sanitizer();
    $result = $sanitizer->html($attack);

    // Result should not contain executable script elements
    $this->assertStringNotContainsString('<script', $result);
    $this->assertStringNotContainsString('onerror', $result);
    $this->assertStringNotContainsString('onload', $result);
}

8. Performance Considerations

8.1. Caching Validation Results

For expensive validations, consider caching:

class CachedValidator
{
    private Validator $validator;
    private array $cache = [];

    public function email(string $email): bool
    {
        $key = 'email:' . md5($email);

        if (!isset($this->cache[$key])) {
            $this->cache[$key] = $this->validator->email($email);
        }

        return $this->cache[$key];
    }
}

8.2. Batch Processing

$validator = new Validator();
$sanitizer = new Sanitizer();

// Process array of inputs efficiently
$emails = ['user1@example.com', 'invalid', 'user2@example.com'];
$valid = array_filter($emails, [$validator, 'email']);