Sandbox
Sandbox, şablonlarda hangi etiketlerin, PHP fonksiyonlarının, metotların vb. kullanılabileceği üzerinde size denetim veren bir güvenlik katmanı sağlar. Sandbox modu sayesinde, uygulamayı tehlikeye atmaktan ya da istenmeyen işlemler yapılmasından endişe etmeden bir müşteriyle veya dış bir kodlayıcıyla şablon oluşturmada güvenle iş birliği yapabilirsiniz.
Nasıl çalışır? Şablonda neye izin vermek istediğimizi basitçe tanımlarız. Başlangıçta her şey yasaktır ve
izinleri kademeli olarak veririz. Aşağıdaki kod, şablon yazarının {block}, {if},
{else} ve {=} etiketlerini (sonuncusu bir
değişkeni veya ifadeyi yazdırmaya yarayan etikettir) ve tüm filtreleri kullanmasına izin verir:
$policy = new Latte\Sandbox\SecurityPolicy;
$policy->allowTags(['block', 'if', 'else', '=']);
$policy->allowFilters($policy::All);
$latte->setPolicy($policy);
Tek tek genel fonksiyonlara, metotlara veya nesne özelliklerine de erişim izni verebiliriz:
$policy->allowFunctions(['trim', 'strlen']);
$policy->allowMethods(Nette\Security\User::class, ['isLoggedIn', 'isAllowed']);
$policy->allowProperties(Nette\Database\Row::class, $policy::All);
allowMethods() ve allowProperties() ile verilen izinler, verilen sınıfın alt sınıflarının
örnekleri için de geçerlidir (denetim is_a() kullanır).
Harika değil mi? Her şeyi çok düşük bir düzeyde denetleyebilirsiniz. Şablon, izin verilmeyen bir fonksiyonu
çağırmayı ya da izin verilmeyen bir metoda veya özelliğe erişmeyi denerse, bir
Latte\SecurityViolationException fırlatır.
Güvenli varsayılan politika
Her şeyin yasak olduğu bir politikayı sıfırdan oluşturmak kullanışlı olmayabilir, bu yüzden güvenli bir temelden başlayabilirsiniz:
$policy = Latte\Sandbox\SecurityPolicy::createSafePolicy();
Bu güvenli temel şu anlama gelir: contentType, debugbreak, dump, extends,
import, include, layout, php, sandbox, snippet,
snippetArea, templatePrint, varPrint, embed dışındaki tüm standart
etiketlere izin verilir. datastream, noescape ve nocheck dışındaki tüm standart
filtrelere izin verilir. Son olarak $iterator nesnesinin metotlarına ve özelliklerine erişime izin verilir.
Sandbox'ı etkinleştirme
Kurallar, {sandbox} etiketiyle eklediğimiz
şablon için geçerlidir. Bu bir bakıma {include}'a benzer: sandbox modunu açar ve {include} gibi
çevredeki değişkenleri otomatik aktarmaz. Ancak onları açıkça aktarabilirsiniz, örneğin
{sandbox 'untrusted.latte', a: 1, b: 2}:
{sandbox 'untrusted.latte'}
Böylece layout ve tek tek sayfalar tüm etiketleri ve değişkenleri serbestçe kullanabilir; kısıtlamalar yalnızca
untrusted.latte şablonuna uygulanır.
Yasak bir etiketin veya filtrenin kullanılması gibi bazı ihlaller derleme zamanında saptanır. Bir nesnenin izin verilmeyen metotlarının çağrılması gibi başkaları çalışma zamanında saptanır. Şablon başka hatalar da içerebilir. Sandbox'lanmış şablondan gelen bir istisnanın tüm render sürecini bozmasını önlemek için, örneğin onu yalnızca günlükleyen kendi istisna işleyicinizi tanımlayabilirsiniz.
Sandbox modunu doğrudan tüm şablonlar için etkinleştirmek istersek, bu kolaydır:
$latte->setSandboxMode();
Üretilen kodun denetimi
Bir kullanıcının sayfaya sözdizimsel olarak doğru ama yasak olan ve PHP Compile Error'a yol açan PHP kodu eklememesini
sağlamak için, şablonların PHP linter tarafından
denetlenmesini öneririz. Bu işlevselliği Engine::enablePhpLinter() metoduyla etkinleştirebilirsiniz. Denetim
için PHP ikili dosyasını çağırması gerektiğinden, yolunu parametre olarak verin:
$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');
Sandbox'ın korumadıkları
İzin verdiğiniz etiketlerin, fonksiyonların, metotların ve özelliklerin ötesinde, sandbox politikadan bağımsız olarak
birkaç yapıyı koşulsuz yasaklar: new operatörü, $this değişkeni, değişken değişkenler
($$var) ve |noescape filtresi.
Sandbox, açık işlemleri güvenilir biçimde korur: fonksiyon, metot ve filtre çağrılarını, ayrıca nesne özelliklerine erişimi. Ancak denetimlerinin ulaşamadığı bir şey var ve onu bilmekte yarar var.
Bir nesneyi yazdırdığınızda veya onu herhangi bir dize bağlamında kullandığınızda, PHP otomatik olarak onun sihirli
__toString() metodunu çağırır. Politika bu örtük dönüşümü denetlemez, bu yüzden
__toString() metodu izin verilenler arasında olmasa bile çalışır. Şablon yazarı böylece, şablonda
ulaşabildiği herhangi bir nesnede __toString()'i tetikleyebilir. Bu, sandbox'ın engellediği açık
{$obj->__toString()} biçiminden farklıdır:
{$obj} {* __toString() çağırır *}
{$obj . '!'} {* aynısı (birleştirme) *}
{="price: $obj"} {* aynısı (dize enterpolasyonu) *}
{$obj|upper} {* aynısı (bir filtre üzerinden) *}
__toString()'in amacı nesnenin metin gösterimini üretmektir, bu yüzden ulaşılabilir olması genellikle zarar
vermez. Sorun ancak __toString()'in yan etkileri varsa (örneğin veritabanına yazmak veya sorgu yapmak) ya da
hassas veri döndürüyorsa doğar.
Latte bu en doğrudan durumları yakalayabilirdi, ama hepsinde güvenilir biçimde değil. Bir nesneyi dizeye dönüştürmek
bir metot çağrısı değil, bir ifadenin içinde birçok yerde ortaya çıkan yerleşik bir dil işlemidir. Bunların
bazılarında (örneğin bir nesneyi bir dizeyle karşılaştırırken ya da çağrılan bir fonksiyonun veya filtrenin içinde),
meşru kullanımı da engellemeden güvenilir biçimde araya girilemezdi. Bu yüzden __toString()'in ulaşılabilir
olduğunu hesaba katmanız gerekir.
__toString()'i yan etkileri olan veya hassas veri açığa çıkaran nesneleri sandbox'a maruz
bırakmayın. Bu yalnızca şablona doğrudan aktardığınız nesneler için değil, yazarın izin verilen bir fonksiyonun,
metodun veya özelliğin dönüş değeri olarak elde ettiği nesneler için de geçerlidir.