Content Security Policy (CSP) je dodatečný bezpečnostní prvek, který
prohlížeči říká, jaké další zdroje může stránka načítat a jak
může být zobrazena. Chrání tak před vkládáním škodlivého kódu a
útokům jako je XSS. Odesílá se v podobě hlavičky sestavené z řady direktiv.
Jeho nasazení ale není vůbec triviální.
Obvykle chceme používat JavaScriptové knihovny umístěné na různých
místech mimo náš server, například měřící kód Google Analytics,
reklamní systémy, captchy atd. A tady bohužel první verze CSP selhává.
Vyžaduje přesnou analýzu načítaného obsahu a nastavení správných
pravidel. Tedy vytvořit whitelist, výčet všech domén, což není snadné,
jelikož některé skripty dynamicky dotahují další skripty z jiných
domén, nebo jsou na jiné domény přesměrované atd. A i když si dáte
práci a seznam vytvoříte ručně, nikdy nevíte, co se může v budoucnu
změnit, takže musíte neustále sledovat, jestli je seznam stále aktuální a
opravovat ho. Analýza Google ukázala, že i to pečlivé ladění ve finále
vede k tomu, že povolíte tak široký přístup, že celý smysl CSP padá,
jen posíláte s každým požadavek mnohem větší hlavičky.
CSP level 2 už k problému přistupuje jinak, pomocí nonce, nicméně
teprve třetí verze řešení dotáhla do konce. Bohužel zatím (rok 2019)
nemá dostatečnou podporu u prohlížečů.
O tom, jak sestavit direktivy script-src
a
style-src
, aby správně fungovaly i ve starších
prohlížečích a přitom s tím bylo co nejméně práce, jsem sepsal podrobný
článek v partnerské sekci Nette. V zásadě výsledná podoba může
vypadat nějak takto:
script-src 'nonce-XXXXX' 'strict-dynamic' * 'unsafe-inline'
style-src 'nonce-XXXXX' * 'unsafe-inline'
Příklad použití v PHP
Vygenerujeme nonce a odešleme hlavičku:
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic' * 'unsafe-inline'");
A vložíme nonce do HTML kódu:
<script nonce="<?=$nonce?>" src="..."></script>
Příklad použití v Nette
Protože Nette má vestavěnou podporu pro CSP a nonce od verze 2.4, stačí
v konfiguračním
souboru uvést:
http:
csp:
script-src: [nonce, strict-dynamic, *, unsafe-inline]
style-src: [nonce, *, unsafe-inline]
A v šablonách pak používat:
<script n:nonce src="..."></script>
<style n:nonce>...</style>
Monitoring
Než nastavíte nová pravidla pro CSP, vyzkoušejte si je nejprve nanečisto
pomocí hlavičky Content-Security-Policy-Report-Only
. Ta funguje
ve všech prohlížečích podporujících CSP. Při porušení pravidel
prohlížeč nezablokuje skript, ale jen pošle notifikaci na URL uvedené
v direktivě report-uri
. K příjmu notifikací a jejich analýze
můžete použít třeba službu Report
URI.
http:
cspReportOnly:
script-src: [nonce, strict-dynamic, *, unsafe-inline]
report-uri: https://xxx.report-uri.com/r/d/csp/reportOnly
Můžete zároveň používat obě hlavičky a v
Content-Security-Policy
mít ověřené a aktivní pravidla a
zároveň v Content-Security-Policy-Report-Only
si testovat jejich
úpravu. Samozřejmě i selhání ostrých pravidlech si můžete nechat
monitorovat.
Po dvou verzích PHP, které nepřinesly nic moc zajímavého, se blíží
verze, pro kterou bude mít opět smysl aktualizovat knihovny. Jde o PHP 7.4 a
hlavním tahákem jsou typed properties, které
uzavírají mnohaletý posun ke striktně typovanému jazyku, což PHP
zvýhodňuje oproti jiným webovým jazykům.
Ve zkratce, tahle novinka vám umožní deklarovat typy přímo
u proměnných třídy:
class Config
{
public string $dsn;
public ?string $user;
public ?string $password;
public bool $debugger = true;
}
Příklad použití najdete i v dokumentaci
Nette Schema, které je na ně už dnes připravené.
Je potřeba říct, že pokud jste si navykli používat privátní
proměnné a přistupovat k nim přes typehintované metody (což je
správně), tak vlastně o žádnou killer feature nejde. Druhotná kontrola
typů je zbytečná a vlastně jen zpomaluje kód. Diametrálně jiná situace
se týká public/protected proměnných, kde dosud neexistoval žádný způsob,
jak mít jejich hodnotu (a dokonce existenci) pod kontrolou. Až dosud.
Což nevyhnutelně povede k otázce:
Je nutné dál psát settery a
gettery?
Sice veškerý boilerplate kód nám dnes na kliknutí generují editory, ale
určitě vypadá hezky, když tohle:
class Circle
{
private $radius;
function setRadius(float $val)
{
$this->radius = $val;
}
function getRadius(): float
{
return $this->radius;
}
}
nahradíte za:
class Circle
{
public float $radius;
}
Nehledě na to, že i užití objektu je stručnější
$circle->radius = $x
vs
$circle->setRadius($x)
.
Problém ale je, že velkou spoustu setterů a getterů nelze jednoduše
nahradit. Třeba zrovna v uvedeném příkladu by se hodilo ještě ověřit,
že poloměr není záporné číslo:
function setRadius(float $val)
{
if ($val < 0) {
throw new InvalidArgumentException;
}
$this->radius = $val;
}
A v ten moment už nelze kód zredukovat do veřejné proměnné.
Jindy zase chceme, aby jednou nastavená hodnota byla neměnná, což nelze
u public proměnné zajistit.
Nebo vůbec nechceme dávat k dispozici getter, protože nepatří do
veřejného API třídy.
Anebo chceme mít setter či getter součástí rozhraní.
Atd, atd.
Zkrátka někdy bude možné použít typované veřejné proměnné místo
metod, jindy ne, rozdíl bude dost často otázkou vnitřní implementace
třídy a pro uživatele neprůhledný. Což je cesta v nekonzistentnímu API,
kdy jednou se používá proměnná, jindy metoda a uživatel v tom nevidí
logiku. (Podobně jako třeba metoda PDOStatement::errorInfo()
vs.
proměnná PDOException::$errorInfo
).
Prohlubování nekonzistence ale nechceš. Raději konzistentní setrvání
u metod, privátních proměnných a všeho toho boilerplate kódu. A pro
privátní proměnné, jak jsem zmiňoval v úvodu, je přínos typehintů
sporný. Nebo ne?
V čem je tedy výhoda?
Vlastně je výhod dost, i když v jiných oblastech. Typované proměnné
budou užitečné pro kompilátor kvůli optimalizacím, pro práci s reflexí
nebo nástroje analyzující kód. Důležité budou v šedé zóně protected
proměnných. Umožňují zapsat prostředky jazyka to, co se dosud obcházelo
komentářem. A navíc přinášejí do jazyka nový příznak neinicializovaného
stavu, jakousi obdobu undefined
z JavaScriptu.
Něco jako když zahlédnete plakát ke koncertu kapely, kterou
si vybavujete z mládí. Oni stále hrají? Nebo se dali dohromady po letech,
protože potřebují peníze? Něco vyždímat na strunách nostalgie?
Texy je můj první open source projekt. Začal jsem ho psát před patnácti lety.
Texy přežilo několik verzovacích systémů. Několik webových služeb
hostujících repositáře. Několik kódování řetězců. Několik
značkovacích jazyků pro tvorbu webových stránek. Několik mých životních
vztahů. Několik měst, ve kterých jsem bydlel.
Texy je tady stále, protože neexistuje nic
lepšího.
Takže jej patnáct let udržuji up-to-date. Začínali jsme v PHP 4, což
byl ten nejhorší programovací jazyk na světě a tedy výzva, pak s úlevou
přešli na PHP 5, o pár let později se ukryli do jmenného prostoru
(Texy::Parser
místo TexyParser
, wow), sledovali, jak
PHP přestávalo být nejhorším jazykem na světě, což otrávilo spoustu
programátorů, kteří si našli náhradu v JavaScriptu, poté bůh stvořil
PHP 7 a s ním typehinty (Texy::process(string $text): string
megawow) a do módy přišla striktnost declare(strict_types=1)
a
tu my ctíme.
A proto je tu Texy 3.0.. Jde
o úplně totéž, jako předchozí verze, ale má všechny vychytávky PHP
7.1. Je to úplně totéž proto, že do dokonalých věcí se nezasahuje.
Texy tu bylo, když jste se narodili. Programátorsky. Texy jednou bude
formátovat váš epitaf. A mezi v
a pokoji
vloží
nedělitelnou mezeru.
V tu-dů listu mi
už druhý týden visí: „Sepsat článek, proč podporovat Nette.“
A zatímco jsem to odkládal, tak kluci z Freelo mě předběhli a napsali to
tak moc dobře, že už nemá smysl se o něco pokoušet 
Díky moc! A tady je 14 důvodů, proč
podpořit Nette.
Co přinesou příští verze Nette Frameworku a jaký je plán
pro další vývoj?
Nette je tvořeno řadou knihoven, z nichž některé patří mezi světovou
špičku: Latte je nejbezpečnější
šablonovací systém, Tracy je mnohými
považován za nejpřívětivější debugovací nástroj, Dependency Injection
Container patří mezi ty nejpohodlněji použitelné. Spousta konceptů vznikla
šťastnou rukou a fungují v prakticky nezměněné podobě už 10 let,
například formuláře nebo komponentový systém presenterů.
Nicméně ve všech oblastech je hodně příležitostí co vylepšovat a
inovovat. A nápadů je spousta.
…pokračování
Nette spouští nový
crowdfundingový program, jehož cílem je získat finanční prostředky
pro vývoj frameworku. Hlavní změnou je, že místo jednorázových
příspěvků je zaměřen na pravidelnou měsíční podporu. Ta může
přicházet jak od jednotlivců, tak od firem, kterým nabízí možnost
zviditelnit se na webu Nette a inzerovat na
fóru nebo přímo v dokumentaci, tedy na místech s nejlepším zásahem
cílové skupiny programátorů.
Každý, kdo staví na Nette, má zájem, aby se framework aktivně vyvíjel.
Aby podporoval nové verze PHP. Aby se opravovaly chyby. Aby přicházel s dalšími novinkami, které usnadní práci
nebo ušetří čas a peníze. Aby framework měl skvělou dokumentaci a
existoval kolem něj užitečný obsah, ať už ve formě článků, návodů
nebo videí.
Řada částí Nette představuje světovou špičku a chceme, aby tomu tak
bylo nadále.
Bez adekvátního financování se nic z toho nedá zajistit. Přitom abyste
se mohli spolehnout, že vyjdou další verze, stačí docela málo: abyste jej
každý měsíc podpořili byť jen malou finanční částkou.
Pojďte do toho a staňte se partnerem
Nette!
Používá vaše firma Nette?
Pokud pracujete ve firmě, které Nette vydělává peníze, vysvětlete
prosím svému šéfovi, že je dobrý nápad se stát partnerem a zajistit tak
zdravé fungování projektu, na který spoléháte. Zvýšíte prioritu
řešení vašich issues a zároveň zviditelníte svoji společnost
v komunitě a přilákáte k sobě vývojáře.
Partnerství totiž přichází s exkluzivními výhodami. Například
s uvedením vašeho loga na webu Nette, možností vkládat pracovní nabídky,
inzerovat na fóru (ukázka)
nebo v dokumentaci (ukázka),
tedy na místech se zcela nejlepším zásahem do skupiny Nette
vývojářů.
Partnerům vystavujeme faktury, aby si mohli podporu dát do nákladů, a to
buď měsíčně, čtvrtletně, půlročně nebo ročně.
Používáte Nette soukromě?
Stojíme také o vaši podporu. Přihlaste se k pravidelným donations přes
PayPal. Vaše jméno bude vidět na webu Nette.
Kolik peněz je potřeba?
V tuto chvíli máme stanoveny tři cíle, kterých chceme dosáhnout.
Prvním cílem je 64.000 Kč měsíčně, které zajistí, že se vývoji
bude věnovat jeden programátor na půl úvazku. Další vývoj Nette tak bude
pokračovat, nicméně polovičním tempem než dosud, což není úplně
ideální. Při dosažení hranice 128.000 Kč získá framework full-time
vývojáře a nové verze mohou přicházet každý rok.
Náklady jsou tak nízké proto, že nepotřebujeme kanceláře a hlavně
pracujeme na něčem, co nás baví.
Při dosažení třetí mety 196.000 Kč měsíčně budeme moci přizvat
další spolupracovníky a vylepšovat tak web, dokumentaci, vytvářet nový
obsah a nechat jej překládat do angličtiny. A tak oslovit zahraniční
komunitu. Čtvrtá meta dává možnost zapojit druhého programátora, což by
vývoj a správu issues značně zrychlilo.
Jakmile se dosáhne tohoto milníku, připravíme další cíle, které by
počítaly s více programátory, mohly by vznikat nové užitečné nástroje
a knihovny. Pak třeba vznikne i kniha o Nette.
Viz také Freelo.cz:
14 důvodů, proč podpořit Nette
Už je to šílených dvanáct let, co jsem na tomto blogu
představil knihovnu Dibi. Dnes se dočkala
čtvrté verze. Využívající všech předností moderního PHP 7.1.
Vývoj a testování verze 4.0 trvalo 11 měsíců a čítá 114 komitů,
zapojilo se do něj několik autorů, kterým děkuji.
A co je nového? Dibi 4 běží v přísném režimu, tedy s
declare(strict_types=1)
. Je plně typovaná, tedy parametry a
návratové hodnoty metod mají nativní typehinty. Což si vyžádalo jednu
drobnou změnu: metody fetch()
nebo fetchSingle()
v případě, že už v tabulce není další řádek, vracejí nově
null
na místo false
, protože návratové hodnoty
mohou být nullable,
nikoliv falseable. Doplněná byla podpora pro JSON (automaticky
dekóduje tyto sloupce), kontroluje, zda s modifikátorem %i
nebo
%f
nepoužijete řetězec, co není číslo, přibyl
Dibi\Expression
a Connection::expression()
(příklad), interface
IConnection
a spousta dalších drobností.
Protože Composer se dnes považuje za standard, jak instalovat balíčky,
archív ZIP i s minifikovanou verzí zmizel v propadlišti dějin.
Změnou je, že metody Connection::query()
a
Fluent::execute()
nevrací v případě DML příkazů počet ovlivněných
řádek, ale objekt Dibi\Result
. Počet řádek zjistíte z něj
($res->getRowCount()
) nebo jako dříve
($connection->getAffectedRows()
).
Dále objekt Dibi\DateTime
je nyní potomkem DateTimeImmutable .
Má v sobě implementovaný magický mechanismus, který by měl odhalit, pokud
někde v kódu stavíte na tom, že je mutable, a došlo by tak k chybě.
Pak jsem dal pryč několik historických reliktů nebo zbytečností,
kompletní přehled najdete v changelogu. Ač ten
seznam může vypadat dlouze, v praxi byste krom výše zmíněného neměli na
žádný BC break narazit.
A ještě pro úplnost: Dibi od verze 3.1 podporuje mikrosekundy, což
může ve specifickém případu vést k BC breaku (viz
vlákno) a od verze 3.2 podporuje jen třídy s namespaces (tedy krom
třídy dibi
).
Co bude dál?
Určitě zajímavé by bylo do Dibi doplnit podporu pro nativní bindování
parametrů, třeba pro upload binárních souborů je to nutnost. A s tím úzce
souvisí i prepared statements. Občas zaznívají žádosti o vylepšení
fluent interface, volání uložených procedur atd.
Zcela na rovinu říkám, že budoucnost stojí zejména na tom, jestli budu
mít za Dibi nějaké příspěvky. Takže pokud máte Dibi rádi, nastavte prosím měsíční
donation a svět bude nadále krásný 
How to mock classes that are defined as final or some of their
methods are final?
Mocking means replacing the original object with its testing imitation that
does not perform any functionality and just looks like the original object. And
pretending the behavior we need to test.
For example, instead of a PDO with methods like query() etc., we create a
mock that pretends working with the database, and instead verifies that the
correct SQL statements are called, etc. More e.g. in the Mockery
documentation.
And in order to be able to pass mock to methods that use PDO
type hint, it is necessary for the mock class to inherit from the PDO. And that
can be a stumbling block. If the PDO or method query() were final, it would not
be possible.
Is there any solution? The first option is not to use the final keyword at
all. This, of course, does not help with the third-party code that it uses, but
mainly detracts from the important element of the object design. For example,
there is dogma that every class should be either final or abstract.
The second and very handy option is to use BypassFinals, which removes
finals from source code on-the-fly and allows mocking of final methods and
classes.
Install it using Composer:
composer require dg/bypass-finals --dev
And just call at the beginning of the test:
require __DIR__ . '/vendor/autoload.php';
DG\BypassFinals::enable();
Thats all. Incredibly black magic 
BypassFinals requires PHP version 5.6 and supports PHP up to 7.2. It can be
used together with any test tool such as PHPUnit or Mockery.
This functionality is directly implemented in the „Nette Tester“: https://tester.nette.org version 2.0 and
can be enabled this way:
require __DIR__ . '/vendor/autoload.php';
Tester\Environment::bypassFinals();
Jak mockovat třídy, které jsou definované jako final nebo
některé z jejich metod jsou final?
Mockování znamená nahrazení původního objektu za jeho testovací
imitaci, která neprovádí žádnou funkci a jen se tváří jako původní
objekt. A předstírá chování, které potřebujeme kvůli testování.
Takže například místo objektu PDO s metodami jako query() apod.
vytvoříme jeho mock, který práci s databází jen předstírá, a místo
toho ověřuje, že se volají ty správné SQL příkazy atd. Více třeba
v dokumentaci
Mockery.
A aby bylo možné mock předávat metodám, které mají type hint
PDO
, je potřeba, aby i třída mocku dědila od PDO. A to může
být kámen úrazu. Pokud by totiž třída PDO nebo metoda query() byla final,
už by to nebylo možné.
Existuje nějaké řešení? První možnost je final
vůbec
nepoužívat. To ovšem nepomůže s kódem třetích stran, který final
používá, ale hlavně se tím ochuzujeme o důležitý prvek objektového
návrhu. Existuje dogma, že každá třída by měla být buď final, nebo
abstract.
Druhou a velmi šikovnou možností je použít Nette Tester, který od verze 2.0 disponuje
vychytávkou, která odstraňuje z kódu klíčové slovo final
on-the-fly. Stačí na začátku testu zavolat:
require __DIR__ . '/vendor/autoload.php';
Tester\Environment::bypassFinals();
A je to. Je za tím ukrytá neskutečně černá magie ?
Pokud nepoužíváte Nette Tester, ale třeba PHPUnit, nebudete ochuzeni,
stačí si nainstalovat BypassFinals:
composer require dg/bypass-finals --dev
A na začátku skriptu zavoláte:
require __DIR__ . '/vendor/autoload.php';
DG\BypassFinals::enable();
Názvoslovný oříšek: jak souhrnně označovat třídy a rozhraní? Jak
třeba nazvat proměnnou, která může obsahovat jak název třídy, tak
rozhraní? Co zvolit místo $class
?
Dá se tomu říkat type ($type
), nicméně to je zase
příliš obecné, protože typem je i řetězec nebo pole. Z pohledu jazyka
jím může být i něco komplikovanějšího, třeba ?array
.
Navíc je sporné, co je v případě objektu jeho typ: je jím název třídy,
nebo je to object
?
Nicméně souhrnné označení pro třídy a rozhraní skutečně existuje:
je jím slovo třída.
Cože?
- Z pohledu deklarace je interface hodně ořezaná třída. Může obsahovat
jen veřejné abstraktní metody. Což také implikuje nemožnost vytvářet
objekty. Rozhraní jsou tedy podmnožinou tříd. A pokud je něco
podmnožinou, tak to můžeme označovat názvem nadmnožiny. Člověk je savec,
stejně jako rozhraní je třída.
- Nicméně je tady ještě pohled užití. Třída může dědit jen od
jedné třídy, ale může implementovat vícero rozhraní. Nicméně tohle je
omezení týkající se tříd, samotné rozhraní za to nemůže. Obdobně:
třída nemůže dědit od final třídy, ale přitom final třídu pořád
vnímáme jako třídu. A také pokud třída může implementovat víc
rozhraní (tj. tříd, viz 1.), stále je vnímejme jako třídy.
A co traity? Ty sem vůbec nepatří, z hlediska OOP jednoduše
neexistují.
Tedy problém se společným pojmenováním tříd a rozhraní je vyřešen.
Říkejme jim prostě třídy.
classes + interfaces = classes
No jo, ale vznikl tady problém nový. Jak říkat třídám, které nejsou
rozhraní? Tedy jejich doplňku. Tomu, co se ještě na začátku článku
nazývalo třídy. Nerozhraní? Nebo implementations? 
To je ještě větší oříšek. To je pořádný ořech. Víte co, raději
zapomeňme na to, že rozhraní jsou také třídy, a tvařme se opět, že
každý OOP identifikátor je buď třída, nebo rozhraní. Bude to
snazší.