<p><code>blocked-uri</code> hangi kaynakların engellendiğini gösterir. <code>violated-directive</code> hangi kuralın yakaladığını belirtir. Birlikte, bunun listede unuttuğunuz meşru bir kaynak mı yoksa asla burada olmaması gereken bir şey mi olduğu konusunda hızlıca bilgi verir.</p>

<blockquote>
    <p><strong>Barındırılan alternatif:</strong> Kendi raporlama hattınızı oluşturmak istemiyorsanız, <a href="https://report-uri.com" target="_blank" rel="noopener noreferrer">report-uri.com</a> CSP raporu toplama ve analiz etme servisi sunmaktadır. Gürültü filtreleme, kontrol panelleri ve uyarılar için işlemleri üstlenir.</p>
</blockquote>

<h3>Route</h3>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code>// routes/api.php

Route::post(‘/csp-violation-report’, [CspReportController::class, ‘store’])->middleware(‘throttle:5’);

<p><code>throttle:5</code> her IP için dakikada 5 rapor kısıtlar. Orantı limitlemesi olmadan, bir saldırgan, döngüde ihlalleri tetikleyerek loglama altyapınızı doldurabilir — disk alanını tüketmenize veya gerçek tehditleri gürültü içinde gizlemenize neden olur.</p>

<h3>Controller</h3>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\RateLimiter;

class CspReportController
{
public function store(Request $request)
{
$body = json_decode($request->getContent(), true);
$report = $body[‘csp-report’] ?? $body ?? [];

    if (empty($report) || !is_array($report)) {
        return response()->noContent();
    }

    $json = json_encode($report);
    $ip = $request->ip();
    $blockedUri = data_get($report, 'blocked-uri', '');
    $violatedDirective = data_get($report, 'violated-directive', '');

    // Tarayıcı uzantıları en büyük CSP gürültüsü kaynağıdır.
    // Reklam engelleyicileri, şifre yöneticileri ve Geliştirici Araçları uzantıları tümü rapor tetikler.
    // Bunlar eyleme geçirilebilir değildir - derhal filtreleyin.
    if (
        str_contains($json, 'chrome-extension://') ||
        str_contains($json, 'moz-extension://') ||
        str_contains($json, 'safari-extension://')
    ) {
        return response()->noContent();
    }

    // Düşük risk: genellikle kendi kodunuz veya standart bir kütüphane.
    // Haftalık inceleyin, hemen değil.
    $lowRiskUris = ['about:blank', 'blob:', 'data:', 'inline', 'eval'];

    if (in_array($blockedUri, $lowRiskUris)) {
        Log::channel('csp_low')->info('Düşük riskli CSP ihlali', compact('ip', 'report'));
        return response()->noContent();
    }

    // Yüksek risk: bir harici domain, bilinmeyen bir betik, muhtemelen enjekte edilmiş içerik.
    // Tam bağlamla günlüğe kaydedin. Süreklilik durumunda uyarın.
    Log::channel('csp_high')->warning('Yüksek riskli CSP ihlali', compact('ip', 'report'));

    $this->checkForThresholdAlert($ip, $blockedUri, $violatedDirective);

    return response()->noContent();
}

protected function checkForThresholdAlert(string $ip, string $uri, string $directive): void
{
    // Aynı IP, aynı direktif, 60 saniyede 10+ defa = çözüm, değil gürültü.
    $key = "csp_alert:{$ip}:{$directive}:{$uri}";

    if (RateLimiter::tooManyAttempts($key, 10)) {
        Log::alert('CSP eşiği aşıldı — muhtemel aktif saldırı', [
            'ip' => $ip,
            'uri' => $uri,
            'directive' => $directive,
        ]);
        return;
    }

    RateLimiter::hit($key, 60);
}

}

<h3>Log Kanalları</h3>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code>// config/logging.php

‘csp_low’ => [
‘driver’ => ‘single’,
‘path’ => storage_path(‘logs/csp-low-risk.log’),
‘level’ => ‘info’,
],

‘csp_high’ => [
‘driver’ => ‘single’,
‘path’ => storage_path(‘logs/csp-high-risk.log’),
‘level’ => ‘warning’,
],

<p>Ayrı dosyalar, farklı saklama politikaları ayarlamanıza izin verir — düşük riskli günlükler haftada bir dönerken, yüksek riskli günlükler 90 gün kalır. Üretimde, <code>Log::alert()</code> ile Slack veya PagerDuty ile kablo hazırlayarak yüksek riskli ihlallerin ani bir artışı durumunda birini uyandırabilirsiniz.</p>

<h3>Modern Report-To Başlığı</h3>

<p>Chrome 70+, Edge 79+ ve Firefox 60+ için <a href="https://developer.mozilla.org/en-US/docs/Web/API/Reporting_API" target="_blank" rel="noopener noreferrer">Raporlama API'si</a> (<code>Report-To</code> başlığı) <code>report-uri</code>'den daha etkilidir — raporlar toplu hale gelir ve son nokta tarayıcıda yedeklenir ki kayıtlarda yer bulan başlık içeren sayfalarda böylece gönderilmeye devam eder.</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code>protected function supportsReportTo(Request $request): bool

{
$ua = $request->header(‘User-Agent’, ”);

return (bool)preg_match('/(Chrome\/([7-9][0-9]|[1-9][0-9]{2,}))|(Firefox\/(6[0-9]|[7-9][0-9]|[1-9][0-9]{2,}))|(Edg\/([7-9][0-9]|[1-9][0-9]{2,}))|(Version\/(1[3-9]|[2-9][0-9])) Safari\//', $ua);

}

protected function addCspReportingEndpoint(Response $response): void
{
$reportTo = [
‘group’ => ‘csp-endpoint’,
‘max_age’ => 10886400, // 126 days — browser caches this endpoint
‘endpoints’ => [
[‘url’ => url(‘/api/csp-violation-report’)],
],
‘include_subdomains’ => true,
];

$response->headers->set('Report-To', json_encode($reportTo, JSON_UNESCAPED_SLASHES));

}

<p>Birçok tarayıcı, <code>report-uri</code> ve <code>Report-To</code> başlığı her ikisinin de uyumlu olduğunu bilmektedir; ikisi de mevcut durumda kalabilir.</p>

<hr/>

<h2>Ön Uygulama Kontrol Listesi</h2>

<p>Rapor-modundan uygulama moduna geçmeden önce kontrol edin:</p>
<ul>
    <li>[ ] <code>Vite::useCspNonce($nonce)</code> middleware’de <em>$next($request)</em> çağrısından <em>önce</em> çağrılmalıdır</li>
    <li>[ ] <code>@vite()</code>, <code>@livewireScripts()</code> ve <code>@livewireScriptConfig()</code> nonce'u pastalamalıdır</li>
    <li>[ ] Her el ile yazılmış satır içi <code><script/></code> bloğu <code>nonce="{{ $cspNonce }}"</code> sahip olmalıdır</li>
    <li>[ ] Her el ile yazılmış satır içi <code/> bloğu <code>nonce="{{ $cspNonce }}"</code> sahip olmalıdır</li>
    <li>[ ] <code>onclick=""</code>, <code>onsubmit=""</code> ve diğer olay işleyici nitelikleri şablonlardan çıkarılmalıdır</li>
    <li>[ ] <code>style=""</code> nitelikleri CSS sınıflarıyla değiştirilmelidir (veya bunu kullanan kütüphane belgelenmelidir)</li>
    <li>[ ] Yüklediğiniz tüm CDN domain’leri uygun beyaz listeye alınmalıdır</li>
    <li>[ ] <code>connect-src</code> API son noktalarınızı, WebSocket hostlarınızı ve analitik hedeflerinizi içermelidir</li>
    <li>[ ] <code>connect-src</code> üretim politikanızda <strong>localhost</strong> içermemelidir</li>
    <li>[ ] İhlal raporu son noktası canlı, kısıtlanmış ve günlük kanalları yapılandırılmış durumdadır</li>
    <li>[ ] Rapor-modu en az bir hafta boyunca üretimde çalıştırılmalı ve yeni yüksek riskli ihlaller olmamalıdır</li>
</ul>

<hr/>

<h2>Güvenle Yayınla: Önce Rapor-Modunda Başla</h2>

<p>CSP'yi mevcut bir uygulamaya ekliyorsanız, zorla uygula moduna atlamayın. Önce başlık adını değiştirin:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code>// Şunu değiştirin:

$response->headers->set(‘Content-Security-Policy’, implode(‘; ‘, $policy));

// Geçici olarak şuna:
$response->headers->set(‘Content-Security-Policy-Report-Only’, implode(‘; ‘, $policy));

<p>Tarayıcı ihlalleri rapor eder ama hiçbir şeyi engellemez. Üretimde bir ile iki hafta çalıştırın ve <code>csp-high-risk.log</code>'a göz atın. Her giriş, ya beyaz listeye almanız gereken bir kaynağı ya da orada asla olmaması gereken bir şeyi gösterir. Günlük suskun hale geldiğinde, <code>Content-Security-Policy</code>'ye geri dönün.</p>

<p>Bu adım, yumuşak bir dağıtım ile sinirli bir destek kuyruğu arasındaki ayrımı oluşturur.</p>

<blockquote>
    <p><strong>Başlıklarınızı doğrulayın:</strong> Yanıt başlıklarınızı derecelendirmek için <a href="https://securityheaders.com" target="_blank" rel="noopener noreferrer">securityheaders.com</a> kullanın. Politikanızı yaygın zayıflıklara karşı kontrol etmek için Google’ın <a href="https://csp-evaluator.withgoogle.com/" target="_blank" rel="noopener noreferrer">CSP Evaluator</a>'ini kullanın. Her ikisi de ücretsizdir.</p>
</blockquote>

<hr/>

<h2>Farklı Yapmak İstediklerimiz</h2>

<p><strong>Drop <code>X-XSS-Protection</code>.</strong> Kullanımı artık geçersizdir ve modern tarayıcılar tarafından yok sayılır. Eski IE sürümlerinde bir hata olduğunu biliyorum, exploite edilmiş olabilir. Bunu tutuyoruz çünkü aktif bir şekilde zarar vermiyor, ama yeni uygulamalarla uğraşmayın.</p>

<p><strong>Büyük bir ekibiniz varsa, ilgili alanları ayrı tutun.</strong> Tüm başlıkları tek bir middleware içinde toplamak iyi çalışıyor ama ayrı bir <code>CspMiddleware</code> daha kolay test edilmektedir. Eğer özelleştirilmiş bir çözüm yerine yapılandırılmış, politika sınıfı bazlı bir yaklaşım arıyorsanız, <a href="https://github.com/spatie/laravel-csp" target="_blank" rel="noopener noreferrer">Spatie'nin laravel-csp</a> paketi ilk sınıf test desteği ile size bunu sağlayacaktır.</p>

<p><strong>Gerçekten statik olan satır içi içerikler için hash kullanın.</strong> Taşınamayacak çok küçük bir satır içi CSS için — örneğin, veritabanı değerlerinden gelen bir arka plan rengi — CSP, noncelar yerine SHA-256 hashlerini destekler. Tam dizeyi önceden hesaplayıp beyaz listeleyebilirsiniz. Amansız hassasiyet ile çalışan bir zaman aşımınız yoktur.</p>

<p><strong>Middleware'den açıkça <code>api</code> grubunu hariç tutun.</strong> Uygulamamız, middleware'i <code>web</code> grubuna ekler. Ancak, eğer global olarak herhangi bir rota kaydederseniz — veya düz bir rota dosyasına geçerseniz — API yanıtları, gereksiz bir CSP başlığı hukuktan zararsız bir şekilde alabilir. Bir grup dışında bırakma ile açık olmak, kayıt sırasına güvenmekten daha güvenlidir:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code>// Laravel 11 — bootstrap/app.php

->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\App\Http\Middleware\SecureHeadersMiddleware::class,
]);
// api grubu kasten hariç tutulmuş — JSON yanıtların CSP’ye ihtiyacı yoktur
})

<hr/>

<h2>Sonuç</h2>

<p>Rapor-modunda iki hafta ve izin listesi ayarları için bir öğleden sonra çalıştıktan sonra, uygulama moduna geçtik. Güvenlik başlıkları puanımız, <a href="https://securityheaders.com" target="_blank" rel="noopener noreferrer">securityheaders.com</a> üzerinden D'den A+'ya çıktı.</p>

<p>O zamandan beri, <code>csp-high-risk.log</code>, izni olmayan üçüncü taraf bir CDN'nin izleme skriptlerini enjekte etme girişiminde bulunduğu iki durumu yakaladı. Middleware, kod incelemesinin yakalayamayacağı şeyleri tespit etti.</p>

<p>Kendi politikanızı yapılandırmak istiyorsanız ama baştan inşa etmek istemiyorsanız, <a href="https://csp-generator.shakiltech.com" target="_blank" rel="noopener noreferrer">Laravel CSP Generator</a> size bunu görsel olarak sunar — CDN'lerinizi, entegrasyonlarınızı ve çevre ayarlarınızı değiştirin, tam politikanın dizesini ve yapıştırmaya hazır PHP middleware yöntemini oluşturur. Ön uygulama kontrol listesi entegre edilmiştir ve yapılandırmanıza göre uyum gösterir.</p>

<p>CSP masum bir gümüş mermi değildir — CSRF koruması, giriş doğrulama, hazırlanmış ifadeler ve HTTPS ile birlikte derinliği olan bir savunma yaklaşımındaki bir katmandır. Ancak diğer savunmalardan farklı olarak, bir dizi saldırıyı tarayıcı seviyesinde aktif olarak engeller, uygulama katmanı savunmalarınızın her şeyle başa çıkmasını beklemektense.</p>

<p>Bugün rapor-modda başlayın. Bir hafta boyunca günlüklerinizi izleyin. Sonra uygulamaya koyun. 🚀</p>

<hr/>

<h2>Hızlı Referans PDF'sini Alın</h2>

<p>Bir belge isterseniz, açık tutmak için — tam yönerge referansı, ön uygulama kontrol listesi, yaygın hatalar ve hızlı başlangıç parçalarıyla birlikte — hepsini 4 sayfalık bir PDF'de derledim.</p>

<p>Aşağı e-posta adresinizi bırakın, hemen gelen kutunuza ulaşacaktır. Seriler yok, spam yok — sadece PDF.</p>

<blockquote>
    <p><strong><a href="https://csp-generator.shakiltech.com#guide" target="_blank" rel="noopener noreferrer">→ Laravel CSP Hızlı Referans PDF'sini Alın</a></strong></p>
    <p>4 sayfa · 13 yönerge · Ön uygulama kontrol listesi · 5 yaygın hata · Ücretsiz</p>
</blockquote>

<hr/>

<h2>Sıkça Sorulan Sorular</h2>

<p><strong>CSP mevcut Laravel uygulamalarını kırar mı?</strong></p>
<p>Kırabilir — özellikle de nonce'siz satır içi betik veya stilleriniz varsa. İlk başta <code>Content-Security-Policy-Report-Only</code> kullanarak engellenecek her şeyi ortaya çıkarın, şablonlarınızı düzeltin, ardından uygulama moduna geçin. Bu, zaten üretim aşamasında olan bir uygulama için tek güvenilir göç yolu.</p>

<p><strong>Zaten CSRF korumam var, bu nedenle CSP’ye ihtiyacım var mı?</strong></p>
<p>Evet. Farklı saldırılara karşı koruma sağlar. CSRF, diğer domainlerden gelen sahte istekleri engeller. CSP, sahte betikleri kendi domaininizde <em>çalışmaktan</em> engeller. Birisi diğerinin yerini almaz.</p>

<p><strong>Uygulamamın kullanıcı tarafından üretilen içeriği yoksa CSP'ye ihtiyacım var mı?</strong></p>
<p>Evet. XSS, kullanıcıların herhangi bir şey göndermesini gerektirmez. Eğer bir bağımlılık, bir tehlikeye düşmüş CDN betiği veya URL parametreleri aracılığıyla DOM tabanlı XSS var ise, tüm bunlar kullanıcı yüklemesi ile ilgili olmayan gerçek saldırı vektörleridir.</p>

<p><strong>Bütün bu CSP düzenlemelerini yaptıktan sonra vites HMR’im durdu. Sorun ne?</strong></p>
<p>İki şeyi kontrol etmeniz gerekiyor. İlk olarak, <code>Vite::useCspNonce($nonce)</code>'nin <code>$next($request)</code> 'den önce çağrıldığından emin olun. İkincisi, <code>connect-src</code>'nin yerel ortamınızda <code>ws://localhost:5173</code> (veya Vite portu) içerdiğini doğrulayın. HMR, bir WebSocket üzerinde çalışır ve eğer bu WebSocket bağlantısı engellenirse başarısız olur.</p>

<p><strong><code>report-uri</code> ve <code>Report-To</code> arasındaki fark nedir?</strong></p>
<p><code>report-uri</code> orijinal mekanizmadır ve yaygın olarak desteklenmektedir. <code>Report-To</code> daha yeni Raporlama API'sidir — raporlar topluca gider, son nokta tarayıcı tarafından aylarca önbelleğe alınır ve yalnızca CSP ile ilgilenmez. Her ikisini de kullanın: <code>report-uri</code> genel düşüş noktası olarak, <code>Report-To</code> ise modern tarayıcılar için. Alternatif olarak, kendi tasarımınızı sıfırdan yapmamak için <a href="https://report-uri.com" target="_blank" rel="noopener noreferrer">report-uri.com</a>'u kullanın.</p>

<p><strong>Geliştirmede CSP ihlalleri nasıl ele alınır?</strong></p>
<p>Geliştirici Araçlarını açın → Konsol. Her CSP ihlali engellenen tam kaynak ve ihlal edilen yönergeyi kaydeder. Bu en hızlı hata ayıklama aracıdır. Rapor-modunda iseniz, aynı ihlaller yaşanır ama hiçbir şey bozulmaz.</p>

<p><strong>CSP ile Livewire nasıl ele alınır?</strong></p>
<p>Livewire satır içi JavaScript enjekte eder ki bu nonce gerektirir. v2 için: <code>@livewireScripts(['nonce' =&gt; $cspNonce])</code>. v3 için: <code>@livewireScriptConfig(['nonce' =&gt; $cspNonce])</code>. Her ikisi de yukarıdaki Aşama 3'te gösterilmiştir.</p>

<p><strong>Google Analytics veya Tag Manager hakkında ne dersiniz?</strong></p>
<p><code>script-src</code>'ye <code>https://www.googletagmanager.com</code>'u, <code>connect-src</code>'ye ise <code>https://www.google-analytics.com</code>'u ekleyin. GTM'nin özel HTML etiketleri <code>'unsafe-inline'</code> gerektirir ki bu sizin politikanızı zayıflatır — sunucu tarafı GTM veya doğrudan GA4 entegrasyonu, taşımaya değen daha temiz alternatiflerdir.</p>

<p><strong>Spatie'nin <code>laravel-csp</code> paketini özel middleware yerine kullanabilir miyim?</strong></p>
<p>Kesinlikle. <a href="https://github.com/spatie/laravel-csp" target="_blank" rel="noopener noreferrer">Spatie'nin paketi</a>, yapılandırılmış, politika sınıfı bazlı bir yaklaşımla birlikte ilk sınıf test araçları sağlar. Bizim özel middleware’imiz, tüm güvenlik başlıklarını bir arada tutmak istediğimiz için anlamlıydı, ancak her iki yaklaşım geçerli ve seçiminiz, ekibinizin kontrol ve gelenek tercihlerine bağlıdır.</p>

<hr/>

<p><em>Üretim Laravel uygulamanızda CSP'yi uyguladınız mı? İyi bir dağıtım alımı yaptıysanız, üçüncü taraf bir kütüphaneyle bir sorun yaşadıysanız veya hâlâ rapor-modunda oturup ne yapacağınızı düşünüyorsanız — yorum bırakın. Hangi kaynakların engellendiğini görmekte zorlandığınızı veya başlattığınızda sizi en çok şaşırtan engellenen kaynakların neler olduğunu duymak isterim.</em></p>

Kaynak: Orijinal Makale

Bu Makaleyi Paylaş