Nette Documentation Preview

syntax
Sandbox
*******

.[perex]
Sandbox tworzy warstwę bezpieczeństwa, która daje Ci kontrolę nad tym, jakie tagi, funkcje PHP, metody itd. mogą być używane w szablonach. Dzięki trybowi sandbox możesz bezpiecznie współpracować z klientem lub zewnętrznym koderem przy tworzeniu szablonów, nie obawiając się naruszenia bezpieczeństwa aplikacji ani wykonania niepożądanych operacji.

Jak to działa? Po prostu określamy, co chcemy w szablonie dopuścić. Na początku zabronione jest wszystko i stopniowo nadajemy uprawnienia. Poniższy kod pozwala autorowi szablonu używać tagów `{block}`, `{if}`, `{else}` i `{=}` (ten ostatni to tag do [wypisania zmiennej lub wyrażenia |tags#Wypisywanie]) oraz wszystkich filtrów:

```php
$policy = new Latte\Sandbox\SecurityPolicy;
$policy->allowTags(['block', 'if', 'else', '=']);
$policy->allowFilters($policy::All);

$latte->setPolicy($policy);
```

Możemy też zezwolić na dostęp do poszczególnych funkcji globalnych, metod lub właściwości obiektów:

```php
$policy->allowFunctions(['trim', 'strlen']);
$policy->allowMethods(Nette\Security\User::class, ['isLoggedIn', 'isAllowed']);
$policy->allowProperties(Nette\Database\Row::class, $policy::All);
```

Uprawnienia nadane przez `allowMethods()` i `allowProperties()` obowiązują także dla instancji klas potomnych danej klasy (sprawdzenie używa `is_a()`).

Czy to nie wspaniałe? Wszystko kontrolujesz na bardzo niskim poziomie. Jeśli szablon spróbuje wywołać niedozwoloną funkcję albo sięgnąć po niedozwoloną metodę czy właściwość, zgłosi wyjątek `Latte\SecurityViolationException`.


Bezpieczna domyślna polityka
============================

Tworzenie polityki od zera, gdzie wszystko jest zabronione, może nie być wygodne, dlatego możesz wyjść od bezpiecznej podstawy:

```php
$policy = Latte\Sandbox\SecurityPolicy::createSafePolicy();
```

Ta bezpieczna podstawa oznacza, że dozwolone są wszystkie standardowe tagi z wyjątkiem `contentType`, `debugbreak`, `dump`, `extends`, `import`, `include`, `layout`, `php`, `sandbox`, `snippet`, `snippetArea`, `templatePrint`, `varPrint`, `embed`. Dozwolone są wszystkie standardowe filtry z wyjątkiem `datastream`, `noescape` i `nocheck`. Wreszcie dozwolony jest dostęp do metod i właściwości obiektu `$iterator`.


Aktywacja sandboxa
==================

Reguły dotyczą szablonu, który wstawiamy tagiem [`{sandbox}` |tags#Dołączanie szablonów]. Jest to poniekąd analogiczne do `{include}`: włącza tryb sandbox i, tak jak `{include}`, nie przekazuje automatycznie otaczających zmiennych. Możesz je jednak przekazać jawnie, na przykład `{sandbox 'untrusted.latte', a: 1, b: 2}`:

```latte
{sandbox 'untrusted.latte'}
```

Layout i poszczególne strony mogą więc swobodnie korzystać ze wszystkich tagów i zmiennych; ograniczenia zostaną zastosowane tylko do szablonu `untrusted.latte`.

Niektóre naruszenia, jak użycie zabronionego tagu lub filtra, są wykrywane w czasie kompilacji. Inne, jak wywołanie niedozwolonych metod obiektu, w czasie działania. Szablon może też zawierać dowolne inne błędy. Aby wyjątek z szablonu w sandboxie nie zakłócił całego procesu renderowania, możesz zdefiniować [własny handler wyjątków |develop#Handler wyjątków], który na przykład tylko go zaloguje.

Gdybyśmy chcieli włączyć tryb sandbox od razu dla wszystkich szablonów, to proste:

```php
$latte->setSandboxMode();
```


Kontrola wygenerowanego kodu
============================

Aby użytkownik nie wstawił na stronę kodu PHP, który jest wprawdzie poprawny składniowo, ale zabroniony i powoduje PHP Compile Error, zalecamy [kontrolę szablonów przez linter PHP |develop#Kontrola wygenerowanego kodu]. Tę funkcjonalność włączysz metodą `Engine::enablePhpLinter()`. Ponieważ do sprawdzenia musi wywołać binarkę PHP, przekaż jej ścieżkę jako parametr:

```php
$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');
```


Czego sandbox nie pilnuje
=========================

Poza tagami, funkcjami, metodami i właściwościami, na które zezwolisz, sandbox bezwarunkowo zabrania kilku konstrukcji niezależnie od polityki: operatora `new`, zmiennej `$this`, zmiennych zmiennych (`$$var`) i filtra `|noescape`.

Sandbox niezawodnie pilnuje operacji *jawnych*: wywołań funkcji, metod i filtrów oraz dostępu do właściwości obiektów. Jest jednak jedna rzecz, do której jego kontrole nie sięgają, i warto o niej wiedzieć.

Kiedy wypisujesz obiekt lub używasz go w dowolnym kontekście łańcuchowym, PHP automatycznie wywołuje jego magiczną metodę `__toString()`. Polityka nie sprawdza tej *niejawnej* konwersji, więc wykona się ona nawet wtedy, gdy metody `__toString()` nie ma wśród dozwolonych. Autor szablonu może więc uruchomić `__toString()` na dowolnym obiekcie, do którego w szablonie sięgnie. Różni się to od jawnej postaci `{$obj->__toString()}`, którą sandbox blokuje:

```latte
{$obj}              {* wywoła __toString() *}
{$obj . '!'}        {* to samo (konkatenacja) *}
{="price: $obj"}    {* to samo (interpolacja łańcucha) *}
{$obj|upper}        {* to samo (przez filtr) *}
```

Zadaniem `__toString()` jest utworzenie tekstowej reprezentacji obiektu, więc jego dostępność zwykle nie szkodzi. Problem pojawia się dopiero wtedy, gdy `__toString()` ma efekty uboczne (na przykład zapis do bazy danych lub zapytanie do niej) albo gdy zwraca wrażliwe dane.

Latte mogłoby wyłapać te najbardziej bezpośrednie przypadki, ale nie niezawodnie we wszystkich. Konwersja obiektu na łańcuch nie jest wywołaniem metody, lecz wbudowaną operacją języka, która występuje w wielu miejscach wyrażenia. W części z nich (na przykład przy porównaniu obiektu z łańcuchem albo wewnątrz wywołanej funkcji lub filtra) nie dałoby się jej przechwycić niezawodnie bez blokowania legalnych zastosowań. Dlatego musisz liczyć się z tym, że `__toString()` jest dostępne.

**Nie udostępniaj sandboxowi obiektów, których `__toString()` ma efekty uboczne albo ujawnia wrażliwe dane.** Dotyczy to nie tylko obiektów przekazywanych do szablonu bezpośrednio, ale też tych, które autor otrzyma jako wartość zwracaną dozwolonej funkcji, metody lub właściwości.

Sandbox

Sandbox tworzy warstwę bezpieczeństwa, która daje Ci kontrolę nad tym, jakie tagi, funkcje PHP, metody itd. mogą być używane w szablonach. Dzięki trybowi sandbox możesz bezpiecznie współpracować z klientem lub zewnętrznym koderem przy tworzeniu szablonów, nie obawiając się naruszenia bezpieczeństwa aplikacji ani wykonania niepożądanych operacji.

Jak to działa? Po prostu określamy, co chcemy w szablonie dopuścić. Na początku zabronione jest wszystko i stopniowo nadajemy uprawnienia. Poniższy kod pozwala autorowi szablonu używać tagów {block}, {if}, {else} i {=} (ten ostatni to tag do wypisania zmiennej lub wyrażenia) oraz wszystkich filtrów:

$policy = new Latte\Sandbox\SecurityPolicy;
$policy->allowTags(['block', 'if', 'else', '=']);
$policy->allowFilters($policy::All);

$latte->setPolicy($policy);

Możemy też zezwolić na dostęp do poszczególnych funkcji globalnych, metod lub właściwości obiektów:

$policy->allowFunctions(['trim', 'strlen']);
$policy->allowMethods(Nette\Security\User::class, ['isLoggedIn', 'isAllowed']);
$policy->allowProperties(Nette\Database\Row::class, $policy::All);

Uprawnienia nadane przez allowMethods() i allowProperties() obowiązują także dla instancji klas potomnych danej klasy (sprawdzenie używa is_a()).

Czy to nie wspaniałe? Wszystko kontrolujesz na bardzo niskim poziomie. Jeśli szablon spróbuje wywołać niedozwoloną funkcję albo sięgnąć po niedozwoloną metodę czy właściwość, zgłosi wyjątek Latte\SecurityViolationException.

Bezpieczna domyślna polityka

Tworzenie polityki od zera, gdzie wszystko jest zabronione, może nie być wygodne, dlatego możesz wyjść od bezpiecznej podstawy:

$policy = Latte\Sandbox\SecurityPolicy::createSafePolicy();

Ta bezpieczna podstawa oznacza, że dozwolone są wszystkie standardowe tagi z wyjątkiem contentType, debugbreak, dump, extends, import, include, layout, php, sandbox, snippet, snippetArea, templatePrint, varPrint, embed. Dozwolone są wszystkie standardowe filtry z wyjątkiem datastream, noescape i nocheck. Wreszcie dozwolony jest dostęp do metod i właściwości obiektu $iterator.

Aktywacja sandboxa

Reguły dotyczą szablonu, który wstawiamy tagiem {sandbox}. Jest to poniekąd analogiczne do {include}: włącza tryb sandbox i, tak jak {include}, nie przekazuje automatycznie otaczających zmiennych. Możesz je jednak przekazać jawnie, na przykład {sandbox 'untrusted.latte', a: 1, b: 2}:

{sandbox 'untrusted.latte'}

Layout i poszczególne strony mogą więc swobodnie korzystać ze wszystkich tagów i zmiennych; ograniczenia zostaną zastosowane tylko do szablonu untrusted.latte.

Niektóre naruszenia, jak użycie zabronionego tagu lub filtra, są wykrywane w czasie kompilacji. Inne, jak wywołanie niedozwolonych metod obiektu, w czasie działania. Szablon może też zawierać dowolne inne błędy. Aby wyjątek z szablonu w sandboxie nie zakłócił całego procesu renderowania, możesz zdefiniować własny handler wyjątków, który na przykład tylko go zaloguje.

Gdybyśmy chcieli włączyć tryb sandbox od razu dla wszystkich szablonów, to proste:

$latte->setSandboxMode();

Kontrola wygenerowanego kodu

Aby użytkownik nie wstawił na stronę kodu PHP, który jest wprawdzie poprawny składniowo, ale zabroniony i powoduje PHP Compile Error, zalecamy kontrolę szablonów przez linter PHP. Tę funkcjonalność włączysz metodą Engine::enablePhpLinter(). Ponieważ do sprawdzenia musi wywołać binarkę PHP, przekaż jej ścieżkę jako parametr:

$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');

Czego sandbox nie pilnuje

Poza tagami, funkcjami, metodami i właściwościami, na które zezwolisz, sandbox bezwarunkowo zabrania kilku konstrukcji niezależnie od polityki: operatora new, zmiennej $this, zmiennych zmiennych ($$var) i filtra |noescape.

Sandbox niezawodnie pilnuje operacji jawnych: wywołań funkcji, metod i filtrów oraz dostępu do właściwości obiektów. Jest jednak jedna rzecz, do której jego kontrole nie sięgają, i warto o niej wiedzieć.

Kiedy wypisujesz obiekt lub używasz go w dowolnym kontekście łańcuchowym, PHP automatycznie wywołuje jego magiczną metodę __toString(). Polityka nie sprawdza tej niejawnej konwersji, więc wykona się ona nawet wtedy, gdy metody __toString() nie ma wśród dozwolonych. Autor szablonu może więc uruchomić __toString() na dowolnym obiekcie, do którego w szablonie sięgnie. Różni się to od jawnej postaci {$obj->__toString()}, którą sandbox blokuje:

{$obj}              {* wywoła __toString() *}
{$obj . '!'}        {* to samo (konkatenacja) *}
{="price: $obj"}    {* to samo (interpolacja łańcucha) *}
{$obj|upper}        {* to samo (przez filtr) *}

Zadaniem __toString() jest utworzenie tekstowej reprezentacji obiektu, więc jego dostępność zwykle nie szkodzi. Problem pojawia się dopiero wtedy, gdy __toString() ma efekty uboczne (na przykład zapis do bazy danych lub zapytanie do niej) albo gdy zwraca wrażliwe dane.

Latte mogłoby wyłapać te najbardziej bezpośrednie przypadki, ale nie niezawodnie we wszystkich. Konwersja obiektu na łańcuch nie jest wywołaniem metody, lecz wbudowaną operacją języka, która występuje w wielu miejscach wyrażenia. W części z nich (na przykład przy porównaniu obiektu z łańcuchem albo wewnątrz wywołanej funkcji lub filtra) nie dałoby się jej przechwycić niezawodnie bez blokowania legalnych zastosowań. Dlatego musisz liczyć się z tym, że __toString() jest dostępne.

Nie udostępniaj sandboxowi obiektów, których __toString() ma efekty uboczne albo ujawnia wrażliwe dane. Dotyczy to nie tylko obiektów przekazywanych do szablonu bezpośrednio, ale też tych, które autor otrzyma jako wartość zwracaną dozwolonej funkcji, metody lub właściwości.