LaraFoundry, yeniden kullanılabilir bir Laravel SaaS çekirdeği olarak kamuya açık bir şekilde inşa ediliyor. Mevcut bir CRM’den çıkarıldı ve aşama aşama gönderiliyor. Bu gönderi, ayar depolama, profil merkezi ve veritabanı destekli bir e-posta şablon editörü olmak üzere üç hizmet modülü içeren 5.1 aşamasıdır. E-posta editörü, ilginç güvenlik hikayesi nedeniyle bu makalenin çoğunu kapsamaktadır.
<h2>
<a name="what-shipped-in-phase-51" href="#what-shipped-in-phase-51"></a>
5.1 aşamasında gönderilenler
</h2>
<p>Her biri kendi gözden geçirme süreci olan üç parça:</p>
<ul>
<li>Ayarlar: bir yapılandırma kaydı tarafından yönlendirilen, üç kapsamlı (uygulama, şirket, kullanıcı) genel anahtar-değer deposu.</li>
<li>Profil merkezi: ad ve e-posta, şifre, iki faktörlü kimlik doğrulama, PIN, oturumlar, avatar ve UI tercihleri için tek bir sekmeli sayfa.</li>
<li>E-posta şablonları: her dil için temel e-postaların konu ve HTML gövdesi için süper-yönetici editörü, veritabanında saklanmaktadır.</li>
</ul>
<h2>
<a name="the-email-template-editor-and-why-rendering-is-the-scary-part" href="#the-email-template-editor-and-why-rendering-is-the-scary-part"></a>
E-posta şablon editörü ve render işleminin neden korkutucu olduğu
</h2>
<p>Özellik sıradan: Bir operatörün onay e-postasının, şifre sıfırlama e-postasının, hoş geldin mesajının ve şirket davetinin metnini, her desteklenen dilde, bir dağıtım olmadan değiştirmesine izin verir. Bunu bir tabloda saklar, yönetim konsolunda düzenler.</p>
<p>Tehlike, nasıl render edildiğindedir. Tembel versiyon, saklanan dizeyi Blade veya başka bir ifade motoru aracılığıyla geçirmek olur, böylece <code>{{ $user->name }}</code> çalışır. Bunu yaptığınız an, bir yönetici tarafından oluşturulan bir dize çalıştırılabilir hale gelir. Yönetim oturumuna sahip olan herkes, veya bir şekilde oturumunu ele geçiren biri, uzaktan kod çalıştırmaya ulaşabilir. Sunucu tarafı şablon enjeksiyonu tam olarak bu hatayı ifade eder.</p>
<p>Bu nedenle, render edici Blade kullanmaz ve <code>eval</code> kullanmaz. Tek bir geçiş ile <code>{{name}}</code> token'ları üzerinde işler ve başka bir şey yok:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="k">public</span> <span class="k">function</span> <span class="n">render</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$template</span><span class="p">,</span> <span class="kt">array</span> <span class="nv">$data</span><span class="p">,</span> <span class="kt">bool</span> <span class="nv">$keepUnknown</span> <span class="o">=</span> <span class="kc">false</span><span class="p">):</span> <span class="kt">string</span>{
return (string) preg_replace_callback(
‘/{{\s(\w+)\s}}/’,
function (array $matches) use ($data, $keepUnknown) {
$key = $matches[1];
<span class="k">if</span> <span class="p">(</span><span class="nb">array_key_exists</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span> <span class="nv">$data</span><span class="p">))</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="nv">$data</span><span class="p">[</span><span class="nv">$key</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nv">$keepUnknown</span> <span class="o">?</span> <span class="nv">$matches</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">:</span> <span class="s1">''</span><span class="p">;</span>
<span class="p">},</span>
<span class="nv">$template</span><span class="p">,</span>
<span class="p">);</span>}
<p>Buradan bazı önemli özellikler ortaya çıkmaktadır:</p>
<p>Değiştirme işlemi tek bir geçiş ile gerçekleşir. Değiştirme değeri asla yeniden taranmaz, dolayısıyla bir veri parçası <code>{{something}}</code> içeriyorsa, bu ikinci bir değiştirme turuna neden olamaz. Hiyerarşik genişlemelere neden olabilecek durumlar yaşanmaz.</p>
<p>Saklanan dize asla bir ifade motoruna ulaşmaz. Derleme aşaması yoktur, <code>eval</code> yoktur, Blade yoktur. Veritabanındaki bir şablon, yapısı gereği kod çalıştırma yeteneğine sahip değildir; bu durum bir kara liste aracılığıyla değil, yapılandırma ile sağlanmıştır.</p>
<p>Bilinmeyen token'lar varsayılan olarak temizlenir, böylece bir alıcı, gelen kutusuna hiçbir şekilde ham <code>{{token}}</code> görmez. Önizleme süreci <code>keepUnknown</code> geçirir, bu nedenle bir operatör hala neyin bağlı olduğunu görebilir.</p>
<p>O render edici, birincil güvenlik sınırıdır. Diğer her şey, derinlikte savunmayı teşkil eder:</p>
<p>Katı değişken beyaz listesi. Her şablon, kullanmasına izin verilen değişkenleri açıkça belirtir. Kaydetme sırasında, konu ve gövdeye başvuran her <code>{{variable}}</code> belirli dillerdeki beyaz liste ile kontrol edilir. Beyaz listelerde belirtilmemiş bir şeye başvurursanız, gönderim zamanında sessiz bir sürpriz yerine 422 alırsınız.</p>
<p>HTML sanitizasyonu. Gövde, yazma ve önizleme sırasında, e-posta dostu bir izin listesi ile HTMLPurifier'dan geçirilir (tablolar, satır içi stiller ve e-postanın gerçekten ihtiyaç duyduğu şeyler). Betikler, olay işleyicileri ve <code>javascript:</code> URL'leri kaldırılır. Bu, bir çerçeve sarmalayıcısının büyük sürümlerinden etkilenmemesi için doğrudan arındırıcı üzerinde inşa edilmiştir.</p>
<p>Sandıklenmiş önizleme. Canlı önizleme, <code>sandbox</code> ile bir iframe içinde render edilir ve betikler kapalıdır, bu nedenle önizleme yüzeyi bile ikinci bir bağımsız engel oluşturur, tek başına değil.</p>
<p>Tehdit modeli, aktörün kim olduğuyla dürüst olur. Süper-yönetici güvenilir bir operatördür, düşmanca bir son kullanıcı değildir. Kod çalıştırmayacak bir render edici, gerçek savunmadır; arındırma ve sandbox, bir hesap ele geçirildiğinde veya hatalı bir biçimlendirilmiş kod yapıştırıldığında kalan durumlar içindir. İnşa etmeden önce tehdit modelini yazdım, böylece katmanların var olma sebebi vardı; bu, güvenlik gösterisi yerine gerçek bir güvenlikti.</p>
<h2>
<a name="a-generic-settings-store-with-three-scopes" href="#a-generic-settings-store-with-three-scopes"></a>
Üç kapsamlı genel bir ayar deposu
</h2>
<p>İkinci parça, üç kapsam (platform (uygulama), şirket ve kullanıcı) için hizmet veren tek bir anahtar-değer deposudur. İlgili her konudan bir tablo yerine, tek bir tablo ve tek bir yapılandırma kaynağı gerçeği vardır:<br/></p>
<div class="highlight js-code-highlight">
<pre class="highlight php"><code><span class="s1">'settings'</span> <span class="o">=></span> <span class="p">[</span>
<span class="s1">'support_email'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'scope'</span> <span class="o">=></span> <span class="s1">'app'</span><span class="p">,</span> <span class="s1">'type'</span> <span class="o">=></span> <span class="s1">'string'</span><span class="p">,</span> <span class="s1">'public'</span> <span class="o">=></span> <span class="kc">true</span><span class="p">],</span>
<span class="s1>'signups_enabled'</span> <span class="o">=></span> <span class="p">[</span><span class="s1">'scope'</span> <span class="o">=></span> <span class="s1>'app'</span><span class="p">,</span> <span class="s1>'type'</span> <span class="o">=></span> <span class="s1>'boolean'</span><span class="p">,</span> <span class="s1>'public'</span> <span class="o">=></span> <span class="kc">true</span><span class="p">],</span>
<span class="s1>'timezone'</span> <span class="o">=></span> <span class="p">[</span><span class="s1>'scope'</span> <span class="o">=></span> <span class="s1>'company'</span><span class="p">,</span> <span class="s1>'type'</span> <span class="o">=></span> <span class="s1>'string'</span><span class="p">,</span> <span class="s1>'validation'</span> <span class="o">=></span> <span class="p">[</span><span class="s1>'timezone'</span><span class="p">]],</span>
<span class="s1>'email_notifications'</span> <span class="o">=></span> <span class="p">[</span><span class="s1>'scope'</span> <span class="o">=></span> <span class="s1>'user'</span><span class="p">,</span> <span class="s1>'type'</span> <span class="o">=></span> <span class="s1>'boolean'</span><span class="p">],</span>
<span class="p">],</span>

