未加星标

OOP-NOOB Series The Publicity Stunt

字体大小 | |
[开发(php) 所属分类 开发(php) | 发布者 店小二03 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

This article is part of theOOP-NOOB Series.

What Are We Talking About Here?

OOP makes use of access modifiers to control the accessibility of methods and properties. This is what allows you to use the concept of encapsulation, so that you have a public interface that consumers of your code can develop against, as well as a private implementation that needs to be treated as a black box from the outside.

Having all of your methods and properties be public generally defeats the purpose of using OOP in the first place, as most of the benefits depend on the concept of encapsulation in some form or other.

How Are Access Modifiers Being Misused

There are a few different ways people misuse OOP access modifiers inphp, some of which are more sneaky than others.

Public Offense

The most obvious way to pull the “Publicity Stunt” is to have only public access modifiers all over the place. This results in code like this:

class PublicOffenseUser { public $username; public $password_hash; public function get_username() { return $this->username; } public function set_username( $username ) { $this->username = $this->validate_username( $username ); } public function set_password( $password ) { $this->password_hash = password_hash( $password, PASSWORD_DEFAULT ); } public function check_password( $password ) { return password_verify( $password, $this->password_hash ); } public function validate_username( $username ) { return filter_var( trim( $username ), FILTER_SANITIZE_ENCODED, FILTER_FLAG_STRIP_LOW ); } }}

Although this does look like a valid class, it does not make sense as an object according to OOP principles.

First of all, the validate_username() method should probably not be part of the public interface that is provided to external consumers. This is an implementation detail, and it should be made private so that it can be changed at any time without needing to consider backward compatibility with consumers of that particular code.

What’s worse though, is that the properties $username and $password_hash are public as well. So, even though we have getters and setters that try their best to enforce the consistency and integrity of these properties, any external code can directly change them at will, therefore bypassing all the integrity checks we could put in place. So, how about setting $username to -1 and $password_hash to new RuntimeException( 'Nonsense' ) ? The class will allow it, so the code should need to be able to handle them, right?

This might have the class stamp on it, but don’t be deceived! In terms of how it works, it is closer to procedural code than to OOP. We could just as well have arbitrary variables $username and $password_hash in whatever scope, and provide procedural helper functions to deal with them.

Sins Of Times Past

There’s a disguised version of the above code that looks differently, but results in the exact same behaviour:

class SinsOfTimesPastUser { var $username; var $password_hash; function get_username() { return $this->username; } function set_username( $username ) { $this->username = $this->validate_username( $username ); } function set_password( $password ) { $this->password_hash = password_hash( $password, PASSWORD_DEFAULT ); } function check_password( $password ) { return password_verify( $password, $this->password_hash ); } function validate_username( $username ) { return filter_var( trim( $username ), FILTER_SANITIZE_ENCODED, FILTER_FLAG_STRIP_LOW ); } }

See, no public keywords in the code above!

However, this code behaves exactly as the version before. Why’s that?

This syntax is actually PHP 4 code, from back when PHP did not yet offer a full OOP model. There were no access modifiers back then, and where you denoted a method with a simple function , you did the same for properties with var .

When you use this code in PHP 5, it offers fallbacks for the old syntax, so that old PHP 4 can still work with newer PHP versions. The only way they could provide a smooth migration from PHP 4 code to PHP 5 code was to make both the var as well as the access-modifier-less function fall back to being public. So, yes, internally, this is the exact same code than we had in the first example, with the same issues.

A Leak Of Its Own

There are some code constructs that can be very deceiving in terms of how access to properties is handled. Here’s such an example:

class ALeakOfItsOwn { public $username; public $email; public $phone; private $password_hash; public function set_password( $password ) { $this->password_hash = password_hash( $password, PASSWORD_DEFAULT ); } public function check_password( $password ) { return password_verify( $password, $this->password_hash ); } public function __call( $method, $arguments ) { $prefix = substr( $method, 0, 4 ); if ( ! in_array( $prefix, [ 'get_', 'set_' ], true ) ) { return null; } $property = substr( $method, 4 ); if ( property_exists( $this, $property ) ) { switch( $prefix ) { case 'get_': return $this->$property; case 'set_': $this->$property = $arguments[0] ?? null; } } return null; } }

As you can see above, we have made the properties $username , $email and $phone public for easy access (let’s assume we don’t need validation for now). The $password_hash however is private , and it comes with a setter and a “checker” method.

Then, as we are starting to get a bit too clever, we add a magic method to the class that lets it accept arbitrary method calls. The reason we do this is that we want to reduce the boilerplate code for all the getters and setters for the three public properties. So, in essence, we have replaced 6 methods with only one general-purpose one. Nice!

The problem is that, without necessarily noticing it, we completely disarmed the access modifiers of the class. So, now, even though the $password_hash is private and it has no getter, we can still just use get_password_hash() to get the property directly, as the property is then indirectly accessed from within the class. Worse yet, although we have a set_password() method that makes sure we properly hash the value first before storing it, we can just use set_password_hash() to bypass it.

So, remember that if you use magic methods like __get() , __set() , __call() or __callStatic() , it’s on you to make sure you that you adhere to all the access modifier restrictions.

If you use PHP magic methods, it's on you to enforce access modifier restrictions!

Proper Encapsulation Always remember to build your objects in a way that they provide proper encapsulation

本文开发(php)相关术语:php代码审计工具 php开发工程师 移动开发者大会 移动互联网开发 web开发工程师 软件开发流程 软件开发工程师

主题: PHPAUTI
分页:12
转载请注明
本文标题:OOP-NOOB Series The Publicity Stunt
本站链接:http://www.codesec.net/view/520499.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(php) | 评论(0) | 阅读(48)