Companion Guide: This guide belongs to the Laravel kits. Every Laravel kit ships these checks built in. Explore Kit #1 (Filament), Kit #2 (Livewire), and Kit #3 (MongoDB).
Track 05 Lesson 17

PII Redaction Pipeline in Laravel

Curated by Umar Farooq • 5 min read
Quick Answer & Core Takeaway

A Personally Identifiable Information (PII) redaction pipeline automatically detects, masks, or pseudonymizes sensitive data (emails, phone numbers, credit cards, national IDs) before user text is transmitted to external LLM APIs. In Laravel, this pipeline operates as a pre-processing middleware or service that strips confidential data, replaces it with cryptographic placeholders, and optionally restores the original values when presenting answers back to the authenticated user.

Last reviewed: 2026-10. Applies to: PHP 8.4+, Laravel 11.x – 13.x.

Quick Answer

A Personally Identifiable Information (PII) redaction pipeline automatically detects, masks, or pseudonymizes sensitive data (emails, phone numbers, credit cards, national IDs) before user text is transmitted to external LLM APIs. In Laravel, this pipeline operates as a pre-processing middleware or service that strips confidential data, replaces it with cryptographic placeholders, and optionally restores the original values when presenting answers back to the authenticated user.

Threat Model

Vulnerable Example

[INSECURE] Vulnerable Pattern
<?php

declare(strict_types=1);

namespace App\Services\Ai;

use Illuminate\Support\Facades\Http;

class VulnerableSupportBot
{
    /**
     * [INSECURE]: Transmitting raw, unredacted customer data directly to an external API.
     */
    public function analyzeTicket(string $ticketText): string
    {
        // Vulnerable: raw text containing SSNs, phone numbers, or credit cards sent over wire
        $response = Http::withToken((string) config('services.openai.key'))
            ->post('https://api.openai.com/v1/chat/completions', [
                'model' => 'gpt-4o-mini',
                'messages' => [
                    ['role' => 'user', 'content' => "Categorize this support ticket:\n" . $ticketText],
                ],
            ]);

        return (string) $response->json('choices.0.message.content');
    }
}

Secure Implementation

[SECURE] Hardened Pattern
<?php

declare(strict_types=1);

namespace App\Services\Ai\Support;

final class PiiRedactor
{
    /** @var array<string, string> Map of regex patterns to replacement tokens */
    private const array PATTERNS = [
        // Credit Card Numbers (Major card formats)
        '/\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\b/'
            => '[REDACTED_CREDIT_CARD]',

        // Email Addresses
        '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/'
            => '[REDACTED_EMAIL]',

        // International and Local Phone Numbers (E.164 and common formats)
        '/(?:\+?\d{1,3}[-.\s]?)?\(?\d{2,4}\)?[-.\s]?\d{3,4}[-.\s]?\d{3,4}/'
            => '[REDACTED_PHONE]',

        // Saudi National ID / Iqama (10 digits starting with 1 or 2)
        '/\b[12]\d{9}\b/'
            => '[REDACTED_NATIONAL_ID]',

        // US Social Security Numbers
        '/\b\d{3}-\d{2}-\d{4}\b/'
            => '[REDACTED_SSN]',
    ];

    /**
     * [SECURE]: Scans input string and replaces sensitive entities with sanitized tokens.
     */
    public function redact(string $text): string
    {
        $sanitized = $text;

        foreach (self::PATTERNS as $pattern => $replacement) {
            $sanitized = (string) preg_replace($pattern, $replacement, $sanitized);
        }

        return $sanitized;
    }

    /**
     * Checks whether a string contains any detectable PII entities.
     */
    public function containsPii(string $text): bool
    {
        foreach (array_keys(self::PATTERNS) as $pattern) {
            if (preg_match($pattern, $text) === 1) {
                return true;
            }
        }

        return false;
    }
}

Integrating with Laravel AI Service

[SECURE] Hardened Pattern
<?php

declare(strict_types=1);

namespace App\Services\Ai;

use App\Services\Ai\Support\PiiRedactor;
use Illuminate\Support\Facades\Http;
use RuntimeException;

final class SecureTicketAnalysisService
{
    public function __construct(
        private readonly PiiRedactor $redactor
    ) {}

    public function analyzeTicket(string $rawTicket): string
    {
        // Redact all sensitive identifiers before making outbound network requests
        $sanitizedTicket = $this->redactor->redact($rawTicket);

        $response = Http::withToken((string) config('services.openai.key'))
            ->timeout(15)
            ->post('https://api.openai.com/v1/chat/completions', [
                'model' => 'gpt-4o-mini',
                'messages' => [
                    ['role' => 'system', 'content' => 'Categorize the support inquiry. Sensitive details are replaced with tokens.'],
                    ['role' => 'user', 'content' => $sanitizedTicket],
                ],
            ]);

        if (!$response->successful()) {
            throw new RuntimeException('Failed to analyze ticket with AI provider.');
        }

        return (string) $response->json('choices.0.message.content');
    }
}

Laravel-Specific Notes

Checklist

Further Reading

AI Assistant Shortcuts
Ask AI about this topic
← Prev: Agent Tool Sandboxing
Next: Model Output Validation →
UF

Curated by Umar Farooq

Senior Full-Stack & Security Engineer with 5+ years of production experience in Laravel, PHP, and modern web application security. Author of the Laravel AI Security Masterclass.