HTML Injection Attacks: Impact and Mitigation Strategies
PHP traits, treat or threat?
1. PHP traits,
treat or threat?
Nick Belhomme
January 28th, PHPBenelux Conference 2012, Belgium
2. Software Architect / Project Lead
Author of the
Zend Framework 2.0 Cookbook
International Conference Speaker
Contributor to various Open Source Projects
Freelance PHP Consultant
11. PHP Market Position, 11 Jan 2012
Used by high traffic sites
Java
ColdFusion
Perl
ASP.NET
PHP
Used by low traffic sites
Used by fewer sites Used by many sites
13. PHP 5 usage
● Version 5.2 73.8%
● Version 5.3 20.2%
● Version 5.1 5.8%
● Version 5.0 0.2%
● Version 5.4 < 0.1%
14. Slow 5.3 Adoption?
● Shared hosting
● Companies refrain from updating stable
systems / distributions
● You
15. Adopt now
● More stability
● Security
● Better engine (ie garbage collection)
● New language features
● Cleaner code because of more elegant ways of
solving problems
16. PHP 5.4
● Array short syntax (javascript notation)
● Array dereferencing
● Class access on instantiation
● Indirect method call by array variable
● Engine processes OO code faster
● <?=
● JSONSerializable interface
● ...
18. ZF Use Case
ZF Code base used is for illustration purposes only and do
–not-- express future possible implementations.
ZF2.0 is not yet released so giving ZF3.0 implementations
illustrates only my own assumptions.
PHP5.4 stable is not yet released so no real world projects are
and should be running this version.
Because of this no real best practices have been defined.
19. namespace ZendView;
class TemplatePathStack implements TemplateResolver {
public function setOptions($options = array()) {
if (!is_array($options) && !$options instanceof Traversable) {
throw new ExceptionInvalidArgumentException(
__METHOD__ . ' expects an array or Traversable'
);
}
foreach ($options as $key => $value) { $this->setOption($key, $value); }
return $this;
}
public function setOption($key, $value) {
switch (strtolower($key)) {
case 'lfi_protection': $this->setLfiProtection($value);
break;
case 'script_paths': $this->addPaths($value);
break;
default: break;
}
}
}
20. namespace ZendMvcRouter;
class RouteBroker implements Broker
{
public function setOptions($options) {
if (!is_array($options)
&& !$options instanceof Traversable) {
throw new ExceptionInvalidArgumentException(sprintf(
'Expected an array or Traversable; received "%s"',
(is_object($options) ? get_class($options) :
gettype($options))
));
}
foreach ($options as $key => $value) {
switch (strtolower($key)) {
case 'class_loader': // handle this case
Default: break;// ignore unknown options
}
}
return $this;
}
}
21. Problem: Code Duplication
if (!is_array($options) && !$options instanceof Traversable) {
throw new ExceptionInvalidArgumentException(sprintf(
'Expected an array or Traversable; received "%s"',
(is_object($options) ? get_class($options) : gettype($options))
));
}
foreach ($options as $key => $value) {
//handle each case
}
return $this;
22. Solution in PHP5.4?
Multiple Inheritance
● Would provide the setOptions in all child
classes needed
● Introduces the diamond problem
23. NOT an Option!
A better solution called Traits
● Would provide the setOptions in all classes that
use the trait
● Eliminates the diamond problem
24. trait Options
{
public function setOptions($options)
{
if (!is_array($options) && !$options instanceof Traversable)
{
throw new ExceptionInvalidArgumentException(sprintf(
'Expected an array or Traversable; received "%s"',
(is_object($options) ? get_class($options) : gettype($options))
));
}
foreach ($options as $key => $value) {
$this->setOption($key, $value);
}
return $this;
}
}
25. namespace ZendView;
class TemplatePathStack implements TemplateResolver
{
use Options;
public function setOption($key, $value) {
switch (strtolower($key)) {
case 'lfi_protection':
$this->setLfiProtection($value);
break;
case 'script_paths':
$this->addPaths($value);
break;
default:
break;
}
}
}
$templateStack = new TemplatePathStack();
$templateStack->setOptions(['lfi_protection' => true]);
26. namespace ZendMvcRouter;
class RouteBroker implements Broker
{
use Options;
public function setOption($key, $value) {
switch (strtolower($key)) {
case 'class_loader':
// handle this case
default:
// ignore unknown options
break;
}
}
}
$routeBroker = (new RouteBroker)->setOptions(['class_loader'=>'SomeLoader']);
29. namespace ZendLoader {
use RuntimeException;
trait SplRegister {
public function register()
{ spl_autoload_register(array($this, 'autoload')); }
public function unregister() {
spl_autoload_unregister(array($this, 'autoload'));
}
}
class StandardAutoloader {
use SplRegister;
public function unregister() {
throw new RuntimeException(
'you should not unregister the standard autoloader once registered'
);
}
}
}
namespace {
$loader = new ZendLoaderStandardAutoloader();
$loader->register();
$loader->unregister(); // will throw the exception
}
30. Precedence Order
The precedence order is that
members from the current class
override Trait methods.
31. namespace ZendLoader {
use RuntimeException;
class StandardAutoloader {
public function unregister() {
throw new RuntimeException(
'you should not unregister the standard autoloader once registered'
);
}
}
}
namespace NbeZfLoader {
use ZendLoader as ZendLoader;
class StandardAutoloader extends ZendLoaderStandardAutoloader
{
use ZendLoaderSplRegister;
}
}
namespace {
$loader = new NbeZfLoaderStandardAutoloader();
$loader->register(); //will register (trait)
$loader->unregister(); // will unregister (trait)
}
34. namespace ZendLoader {
use RuntimeException;
class StandardAutoloader {
public function unregister() {
throw new RuntimeException(
'you should not unregister the standard autoloader once registered'
);
}
}
}
namespace NbeZfLoader {
use ZendLoader as ZendLoader;
class StandardAutoloader extends ZendLoaderStandardAutoloader {
use ZendLoaderSplRegister {
ZendLoaderStandardAutoloader::unregister insteadof ZendLoaderSplRegister;
}
}
}
namespace {
$loader = new NbeZfLoaderStandardAutoloader();
$loader->register(); //will register (trait)
$loader->unregister(); // will throw RuntimeException (base class)
}
44. BUT
Strict Standards: ZendFilterLocale
and ZendFilterSecondLocale
define the same property ($locale)
in the composition of
ZendFilterAlpha. This might be
incompatible, to improve
maintainability consider using
accessor methods in traits instead.
45. namespace ZendFilter {
trait Locale {
protected $locale = 'nl_BE';
public function getLocale()
public function setLocale($locale = null)
}
trait SecondLocale {
protected $locale = 'en_US';
public function setLocale($locale = null)
}
class Alpha {
use Locale, SecondLocale {
SecondLocale::setLocale insteadof Locale;
}
}
}
namespace {
$alpha = new ZendFilterAlpha();
$alpha->setLocale('nl_BE');
}
46. Fatal error: ZendFilterLocale and
ZendFilterSecondLocale define the
same property ($locale) in the
composition of ZendFilterAlpha.
However, the definition differs and is
considered incompatible.
47. namespace ZendFilter {
trait Locale {
static public $locale = 'en_US';
public function getLocale() { return self::$locale; }
public function setLocale($locale = null)
{ self::$locale = $locale; }
}
class Alpha {
use Locale;
public function get() { return self::$locale; }
}
}
namespace {
$alpha = new ZendFilterAlpha();
echo $alpha->getLocale(); // en_US
echo $alpha->get(); // en_US
$alpha->setLocale('nl_be');
echo $alpha->getLocale(); // nl_be
echo $alpha->get(); // nl_be
}
48. namespace ZendFilter {
trait Locale {
static public $locale = 'en_US';
public function getLocale() { return self::$locale; }
public function setLocale($locale = null)
{ self::$locale = $locale; }
}
class Alpha {
use Locale;
public function get() { return self::$locale; }
}
class Beta { use Locale; }
}
namespace {
$alpha = new ZendFilterAlpha();
echo $alpha->getLocale(); // en_US
$alpha->setLocale('nl_be');
echo $alpha->getLocale(); // nl_be
$beta = new ZendFilterBeta(); echo $beta->getLocale(); //en_US
}
54. namespace ZendView;
class TemplatePathStack implements
TemplateResolver
{
use Options {Options::setOptions as protected}
public function __construct($options = array())
{ $this->setOptions($options); }
}
new TemplatePathStack(['lfi_protection' => true]);
56. namespace ZendView;
trait Options {
public function setOptions($options) {
//set the options
}
}
interface TemplateResolver {
public function setConfig($options);
}
class TemplatePathStack implements TemplateResolver {
use Options {Options::setOptions as setConfig;}
}
(new TemplatePathStack)->setConfig(['lfi_protection' => true]);
57. namespace ZendView;
trait Options {
public function setOptions($options) { }
}
class TemplatePathStack {
use Options {
Options::setOptions as setConfig;
}
}
$templateStack = (new TemplatePathStack)
->setConfig(['lfi_protection' => true]);
var_dump($templateStack instanceof Options);
// false
58. namespace ZendView;
trait Options {
abstract public function setOptions($options);
}
class TemplatePathStack {
use Options;
public function setOptions($options)
{ echo 'implementation enforced by trait'; }
}
$templateStack = (new TemplatePathStack)
->setOptions(['lfi_protection' => true]);
var_dump($templateStack instanceof Options); // false
59. namespace ZendView;
trait Options {
abstract public function setOptions($options);
}
class TemplatePathStack {
use Options {Options::setOptions as setConfig;}
public function setConfig($options)
{ echo 'implementation enforced by trait'; }
}
// Fatal error: Class ZendViewTemplatePathStack
contains 1 abstract method and must therefore be
declared abstract or implement the remaining methods
(ZendViewTemplatePathStack::setOptions)
60. Magic Constants
<?php
trait Id {
public function getClass() {
echo __CLASS__;
}
public function getTrait() {
echo __TRAIT__;
}
}
class Foo {
use Id;
}
(new Foo)->getClass(); //Foo
(new Foo)->getTrait(); //Id
61. Autoloading
<?php
namespace NbeZfLoader{
class foo {
use Id;
}
}
namespace {
function __autoload($class) {
var_dump($class);
die();
}
}
// string(15) "NbeZfLoaderId"
62. Good things about
traits
● Removes code duplication
● Helps keeping your code cleaner
● Maintainability
● Aliassing works for interfaces
● Property handling
● Easy Autoloading
63. Bad things
about traits
● Adds code complexity /
understandability with the
insteadof operator.
● Duck typing
● Aliassing Fails for Abstract
Classes
● Property handling
64. Benchmarks 5.3 vs 5.4
● ZF project: http://nickbelhomme.com
● Benchmark / Profiling tool: xhprof
● How: 2 VirtualBox Machines - debian clones,
with only PHP upgrade (5.4.0RC3)
65. 5.3.3
● Total Incl. Wall Time (microsec): 674,842 ms
● Total Incl. CPU (microsecs): 672,042 ms
● Total Incl. MemUse (bytes): 13,827,864 bytes
● Total Incl. PeakMemUse (bytes): 13,865,976
bytes
● Number of Function Calls: 14,524
66. 5.4.0RC3
Total Incl. Wall Time (microsec): 166,813 ms
● Total Incl. CPU (microsecs): 168,011 ms
● Total Incl. MemUse (bytes):7,969,928 bytes
● Total Incl. PeakMemUse (bytes):8,015,944
bytes
● Number of Function Calls: 14,520