Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: İmzalı URL’leri Sadece Bozan İzleme Bağlantısı Hatası
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » İmzalı URL’leri Sadece Bozan İzleme Bağlantısı Hatası

Yazılım

İmzalı URL’leri Sadece Bozan İzleme Bağlantısı Hatası

teknomers
Son güncelleme: 26 Haziran 2026 02:26
teknomers
Paylaş
Paylaş

Bu örnekte önemli bir hata ile karşılaşıyoruz: e-posta içindeki neredeyse her link sorunsuz çalışırken, özellikle kritik olanlar — imzalı linkler — sessizce bozuluyor. Bu durum, Laravel’in istekleri reddetmesine neden oluyor. Bugün mail-history için tek satırlık bir düzeltme gönderdim ve bunun nedenini anlamak, kod değiştirmekten daha ilginç.

<h2>Kurulum: Kendin Yönettiğin Tıklama Takibi</h2>
<p>mail-history, kendin yönettiğin bir açılma/tıklama takibi yapıyor. Üçüncü taraf bir piksel servisi kullanmadan, çıkan e-postanın HTML'sini yeniden yazarak her <code>&lt;a href=""&gt;</code> bağlantısını önce bir yönlendirme uç noktasına yönlendiriyor. Bu yönlendirme, tıklamayı kaydettikten sonra kullanıcıyı gerçek hedefe yönlendiriyor.</p>

<p>Yolculuk sırasında, orijinal URL'yi takip linkine şifreliyoruz ve çıkışta çözüyoruz. Yeniden yazma işlemi, hem Mailable kaygılarını hem de enjekte edilen dinleyiciyi paylaşan bir trait içinde yer alıyor; böylece mantık tam olarak bir yerde mevcut:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="k">protected</span> <span class="k">function</span> <span class="n">rewriteClickLinks</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$html</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$hash</span><span class="p">):</span> <span class="kt">string</span>

{
if ($hash === ” || $html === ”) {
return $html;
}

<span class="nv">$excludePatterns</span> <span class="o">=</span> <span class="p">(</span><span class="k">array</span><span class="p">)</span> <span class="nf">config</span><span class="p">(</span><span class="s1">'mailhistory.tracking.click.exclude_patterns'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'*unsubscribe*'</span><span class="p">]);</span>

<span class="k">return</span> <span class="p">(</span><span class="n">string</span><span class="p">)</span> <span class="nb">preg_replace_callback</span><span class="p">(</span>
    <span class="s1">'/<a>]*?)href=["\']([^"\']+)["\']/i'</span><span class="p">,</span>
    <span class="k">function</span> <span class="p">(</span><span class="nv">$matches</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$hash</span><span class="p">,</span> <span class="nv">$excludePatterns</span><span class="p">)</span> <span class="p">{</span>
        <span class="nv">$attributes</span>  <span class="o">=</span> <span class="nv">$matches</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
        <span class="nv">$originalUrl</span> <span class="o">=</span> <span class="nv">$matches</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>

        <span class="c1">// mailto:/tel:/#/javascript: ve hariç olan desenleri atla...</span>

        <span class="nv">$trackingUrl</span> <span class="o">=</span> <span class="nf">route</span><span class="p">(</span><span class="s1">'mailhistory.tracking.click'</span><span class="p">,</span> <span class="p">[</span>
            <span class="s1">'hash'</span> <span class="o">=&gt;</span> <span class="nv">$hash</span><span class="p">,</span>
            <span class="s1">'url'</span>  <span class="o">=&gt;</span> <span class="nc">Crypt</span><span class="o">::</span><span class="nf">encryptString</span><span class="p">(</span><span class="nv">$originalUrl</span><span class="p">),</span>
        <span class="p">]);</span>

        <span class="k">return</span> <span class="s1">'<a><span class="mf">.</span><span class="nv">$attributes</span><span class="mf">.</span><span class="s1">'href="'</span><span class="mf">.</span><span class="nb">htmlspecialchars</span><span class="p">(</span><span class="nv">$trackingUrl</span><span class="p">)</span><span class="mf">.</span><span class="s1">'"'</span><span class="p">;</span>
    <span class="p">},</span>
    <span class="nv">$html</span>
<span class="p">);</span>

}

<p>Şu satırı dikkatlice okuyun: <code>$originalUrl = $matches[2]</code>. URL'yi, <em>gerçekleşmiş</em> HTML'den doğrudan alıyoruz.</p>

<h2>Tuzağı: Render Edilmiş HTML Kaçırılmış HTML'dir</h2>
<p>Trait, e-posta içeriğini gördüğünde, Blade ve Laravel'in mail şablonları işlerini yapmış olur — ve bu işlerden biri, özellikleri HTML'de kaçırmaktır. Bir <code>href</code>'de ampersand, ampersand olarak kalmaz. <code>&amp;</code> haline gelir.</p>

<p>Normal bir bağlantı için bu görünmezdir çünkü tarayıcı bunu geri çözer. Ancak, biz bunu bir tarayıcıya vermiyoruz. <code>Crypt::encryptString()</code>'i ham yakalanmış dize üzerinde çağırıyoruz. Yani Laravel imzalı bir URL'yi render ettiğinde:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight plaintext"><code>https://example.com/email/verify/1/abc?expires=123&amp;signature=deadbeef
    </code></pre>
</div>

<p><code>href</code> niteliğinde aslında şunlar var:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight plaintext"><code>https://example.com/email/verify/1/abc?expires=123&amp;signature=deadbeef
    </code></pre>
</div>

<p>Biz bunu <em>şifreliyoruz</em> — <code>&amp;</code> ve hepsini. Tıklandığında, yeniden yönlendirme bunu çözüyor ve kullanıcıyı <code>?expires=123&amp;signature=deadbeef</code> olan bir URL'ye yönlendiriyor. Laravel'in imzalı URL doğrulaması Query String üzerindeki imzayı tekrar hesaplıyor; şimdi <code>amp;</code> içeren bir değer görüyor ki, bu daha önce imzalanan herhangi bir şeyin parçası değildi. Doğrulama başarısız olur.</p>

<p>Acı olan taraf ise hata durumu. Normal pazarlama bağlantıları — <code>https://example.com/page</code> — Query String'e sahip değil; dolayısıyla, geçerler ve sağlıklı görünürler. Hızlı bir testte her şeyin doğru göründüğünü sanabilirsiniz. Ancak, yalnızca imzalı URL'ler, <code>expires</code> ve <code>signature</code> <code>&amp;</code> ile ayrılmış olanlar, sessizce ölüyor. Bu hata, güvenlikle ilgili en hassas bağlantılarınızı hedef alıyor ve geri kalanlarını yalnızca bırakıyor.</p>

<h2>Düzeltme: Okuma Öncesi Çöz</h2>
<p>HTML varlıklarını, URL'yi okuduğunuz an çözüp düzeltmeden önce tıklama durumu veya başka bir şey tarafından kullanılmasına izin vermemek önemlidir:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="c1">// Render edilmiş HTML'den alınan href'den (örn; &amp; -&gt; &) kaçarak</span>

// şifreli bağlantının bütünlüğünü koru…
$originalUrl = html_entity_decode($matches[2], ENT_QUOTES | ENT_HTML5);

<p><code>ENT_QUOTES | ENT_HTML5</code> sayesinde, hem alıntı şeklindeki stilleri hem de tam HTML5 varlık kümesini ele alarak, yalnızca dört temel varlık ile sınırlı kalmamış oluyoruz. Artık <code>&amp;</code>, şifrelemeden önce <code>&amp;</code> haline geliyor, çözülen yeniden yönlendirme hedefi, orijinal imzalı URL ile birebir örtüşüyor ve imza doğrulaması geçiyor.</p>

<p>Tek bir satır. Tüm hata, bir değeri okuduğunuz <em>yerle</em> (render edilmiş, kaçırılmış HTML) ve bunu <em>ne</em> diye varsaydığınız (ham URL) arasındaki uyumsuzluktan kaynaklanıyordu.</p>

<h2>Test ile Sabitle</h2>
<p>Böyle tek satırlık düzeltmeler, gelecekteki "temizlik" refaktörlerinde sessizce geri alınabilecek türdendir. Bu nedenle, gerçek semptomu — çözülen URL'nin gerçek <code>&amp;</code> içerdiğini doğrulayan bir Pest testi oluşturulmuştur:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="nf">it</span><span class="p">(</span><span class="s1">'tracked URL'deki HTML varlıklarını çözerek imzalı query stringlerin geçişini korur'</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="p">{</span>
        <span class="c1">// Laravel'in mail şablonları üzerinden render edildiğinde, imzalı URL'deki ampersand &amp; niteliğinde HTML kaçırılır.</span>
        <span class="nv">$email</span> <span class="o">=</span> <span class="p">(</span><span class="k">new</span> <span class="nc">Email</span><span class="p">)</span><span class="o">-&gt;</span><span class="nf">html</span><span class="p">(</span>
            <span class="s1>'<a href="https://example.com/email/verify/1/abc?expires=123&amp;signature=deadbeef">Doğrula</a>'</span>
        <span class="p">);</span>

        <span class="nv">$rewritten</span> <span class="o">=</span> <span class="nf">rewriteFixture</span><span class="p">(</span><span class="nv">$email</span><span class="p">,</span> <span class="n">hash</span><span class="o">:</span> <span class="s1>'abc123'</span><span class="p">);</span>

        <span class="nb">preg_match</span><span class="p">(</span><span class="s1>'/url=([^"&amp;]+)/'</span><span class="p">,</span> <span class="nv">$rewritten</span><span class="p">,</span> <span class="nv">$m</span><span class="p">);</span>
        <span class="nv">$decrypted</span> <span class="o">=</span> <span class="nc">Crypt</span><span class="o">::</span><span class="nf">decryptString</span><span class="p">(</span><span class="nb">urldecode</span><span class="p">(</span><span class="nv">$m</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span>

        <span class="nf">expect</span><span class="p">(</span><span class="nv">$decrypted</span><span class="p">)</span>
            <span class="o">-&gt;</span><span class="nf">toBe</span><span class="p">(</span><span class="s1>'https://example.com/email/verify/1/abc?expires=123&amp;signature=deadbeef'</span><span class="p">)</span>
            <span class="o">-&gt;</span><span class="n">not</span><span class="o">-&gt;</span><span class="nf">toContain</span><span class="p">(</span><span class="s1>'&amp;'</span><span class="p">);</span>
    <span class="p">});</span>
    </code></pre>
</div>

<p>Bu test, iki iş yapıyor: hem mutlu yolu onaylıyor (tam olarak imzalı URL'ye geri uygun bir şekilde şifrelenmiş) ve geri dönüş kısıtlaması olarak kayıptan kaçınıyor (<code>not-&gt;toContain('&amp;')</code>). Eğer altı ay sonra <code>html_entity_decode</code> çağrısını kaldıran birisi olursa, bu test kırılacak ve onlara <em>nedenini</em> gösterecek — yorum tam burada testte bulunuyor.</p>

<h2>Sonuç</h2>
<p>Çizimden çıkardıktan sonra, bir veriyi bir tarayıcı olmayan bir yere — şifreleme, imzalama, yönlendirme, webhook — beslediğinizde, bunun varlık kaçırılma ihtimali olduğunu varsayın ve önce çözün. Tarayıcı sizi affederdi. <code>Crypt::encryptString()</code> bunu affetmeyecektir. Ve düzeltme yalnızca bir satırsa, onu adlandıran özel bir test için ekstra on dakikanızı harcayın; çünkü tek satırlık değişiklikler, gelecekteki temizlik için sessizce geri alınması en kolay olanlardır.</p>

Kaynak: Orijinal Makale

Veritabanı Dar Boğazlarını Önleyin: Laravel’de Redis Cache Etiketlerini Usta Çalışan Yapın
Her Sabah amoCRM Kontrol Etmeyi Nasıl Bıraktım ve Yerine Bir Telegram Botu Geliştirdim
Yılda 1 $’ın Altında Web Uygulama Hosting’i (15.000 IDR) Cloudflare Tüneli ile
Monolitleri Sözsüzleştirmek: Laravel’de Olay Tabanlı Mimari
Laravel’da Etkinlikler, Dinleyiciler ve Gözlemciler: İş Mantığınızı Ayrıştırarak Daha Hızlı İterasyon Sağlayın
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Otonom Araçlar İçin Fren Pedali Zorunluluğu Kaldırılıyor mu?
Sonraki Makale AMD Ryzen 7 5800X3D yeniden inceleme: DDR4’ün oyun potansiyeli

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Walmart’ta Kaçırılmayacak 21 Fırsatı Keşfedin! Alışverişe Başlayın!
Genel
Amazon Prime Günü’nde dördüncü gün, en iyi teknik fırsatlar
Donanım
Apple ve Audi Çalışanları’ndan Ay Tekerlekli Araca Dayanan Lüks Elektrikli Araç
Genel
Riot Vanguard, sürekli açık gereksinimini kaldırdı; yeni mod Windows 11 şartı istiyor
Donanım
Laravel’de Sıfır Kesinti ile Veri Tabanı Göçleri
Yazılım
2026 Prime Day’de Kaçırılmaması Gereken Eylem Kamerası Fırsatları!
Genel
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?