ホームページ  >  記事  >  バックエンド開発  >  PHP フレームワークがコード インジェクションを防止する

PHP フレームワークがコード インジェクションを防止する

WBOY
WBOYオリジナル
2016-06-20 12:34:06819ブラウズ

プロパティの挿入


$Component->property1 にアクセスすると、Yii は裏で何をしているのでしょうか? yiibaseComponent::__get()


public function __get($name)

{

$getter = 'get' . $name;

if (method_exists($this, $getter)) {

return $this->$getter();

} else {

// この else 分岐の内容は yiibaseObject::__get() の

// 違いは

$this-> ;ensureBehaviors();

foreach ($this->_behaviors as $behavior) {

if ($behavior->canGetProperty($name)) {

// 属性は動作内でパブリックである必要があります。それ以外の場合は、以下のフォームからアクセスすることはできません。

return $behavior->$name;

$name)) {

throw new InvalidCallException('書き込み専用プロパティの取得: .

get_class($this) . '::' . $name);

throw new UnknownPropertyException('不明なプロパティを取得しています: ' .

get_class($this) . $name);

}

yiibaseComponent::__get() と yiibaseObject::__get() の違いに注目してください。 つまり、未定義のゲッター関数以降の処理では、yiibaseObject が直接例外をスローし、アクセスしたいプロパティが存在しないことを通知します。 しかし、yiibaseComponent の場合、ゲッターがない後でも、それが挿入された動作の属性であるかどうかをチェックする必要があります:

まず、$this->ensureBehaviors()と呼ばれます。このメソッドは、主に動作がバインドされていることを確認するために前に説明しました。

動作がバインドされていることを確認したら、 $this->_behaviors の走査を開始します。 Yii は、クラスのすべてのバインドされた動作を yiibaseComponent::$_behaviors[] 配列に保存します。

最後に、このプロパティがビヘイビアーの canGetProperty() を通じてバインドされたビヘイビアーの読み取り可能なプロパティであるかどうかを判断し、そうであれば、このビヘイビアーのプロパティ $behavior->name を返します。 プロパティの読み取りを完了します。 canGetProperty() については、:ref::property セクションで簡単に説明されており、後で対象を絞った方法で紹介されます。


セッターのコードも同様なので、ここではスペースを取りません。


メソッドインジェクション

__get() __set() マジックメソッドによる属性インジェクションと同様、Yii インジェクションビヘイビア内のメソッドは、__call() マジック メソッドによって実現されます。


public function __call($ name, $params)

{

$this->ensureBehaviors();


foreach ($this->_behaviors as $object) {

if ($object->hasMethod($name)) {

return call_user_func_array([$object, $name], $params);

}

}

throw new UnknownMethodException('不明なメソッドの呼び出し: ' .

get_class($this) . "::$name()");

}

上記のコードからわかるように、Yii は最初に $this->ensureBehaviors() を呼び出して、動作がバインドされていることを確認します。

次に、 yiibaseComponent::$_behaviros[] 配列も走査されます。 hasMethod() メソッドを通じてメソッドが存在するかどうかを確認します。 バインドされた動作で呼び出されるメソッドが存在する場合は、PHP の call_user_func_array() を使用して呼び出します。 hasMethod() メソッドについては、後ほど説明します。

プロパティとメソッドのアクセス制御を挿入します

前のセクションでは、パブリック、プライベート、およびビヘイビアーの保護されたメンバー バインドされたクラスでアクセスできるかどうかについて、具体的な例が示されています。 ここではコードレベルからその理由を分析します。

上記の内容では、プロパティがアクセス可能かどうかは主に canGetProperty() と canSetProperty() の動作に依存することがわかります。 メソッドを呼び出せるかどうかは、主に hasMethod() の動作に依存します。 yiibaseBehavior は古い友人である yiibaseObject を継承しているため、上記の 3 つの判定メソッドのコードは実際には Object 内にあります。一つずつ見てみましょう:


public function canGetProperty($name, $checkVars = true)

{


return method_exists($this, 'get' . $name) || $checkVars &&

property_exists($this, $name);

}

public function canSetProperty($name, $checkVars = true)

{

return method_exists($this, 'set' . $name) || $checkVars &&

property_exists($this, $name);

}

public function hasMethod($name)

{

return method_exists($this, $name);

}


これら 3 つのメソッドは実際にはまったく複雑ではありません。この点に関して、次の結論を導き出すことができます。


コンポーネントにバインドされた動作にプロパティを読み取る (書き込む) とき、その動作がゲッター (セッター) を定義している場合にアクセスできます。 または、ビヘイビアーにこのメンバー変数がある場合、この時点でメンバー変数はパブリック、プライベート、または保護される可能性があります。 ただし、最終的にはパブリック メンバー変数のみが正しくアクセスできます。その理由は、注射の原理の説明で説明しました。

コンポーネントにバインドされたビヘイビアのメソッドを呼び出す場合、そのビヘイビアがメソッドを定義していれば、上記の判定をパスすることができます。 現時点では、このメソッドはパブリック、プライベート、または保護のいずれかにできます。 ただし、最終的には public メソッドのみを正しく呼び出すことができます。前の理由が理解できれば、ここでも理解できるでしょう。




Dependency Injection Container

Dependency Injection (DI) コンテナはオブジェクトとそのオブジェクトが依存するすべてのオブジェクトを初期化および構成する方法を知っているオブジェクト。 Martin の記事では、DI コンテナーがなぜ有用であるかをすでに説明しています。ここではYiiが提供するDIコンテナの使い方を中心に説明します。


Dependency Injection


Yii は、yiidiContainer クラスを通じて DI コンテナ機能を提供します。次のタイプの依存性注入をサポートします:

1. セッターとプロパティの注入。

4. コンストラクター インジェクション

パラメーター型ヒントを利用して、DI コンテナーはコンストラクター インジェクションを実装します。コンテナーを使用して新しいオブジェクトを作成する場合、タイプ ヒントによって、コンテナーがどのクラスまたはインターフェイスに依存しているかがわかります。コンテナーは、依存するクラスまたはインターフェイスのインスタンスを取得しようとし、コンストラクターを通じてそれを新しいオブジェクトに注入します。例:


class Foo


{

public function __construct(Bar $bar)

{

}

}

$foo = $container->get('Foo');

// 上記のコードなど。価格:

$bar = new Bar;

$foo = new Foo($bar);

セッターとプロパティの注入


セッターとプロパティの注入は、構成によってサポートされます。依存関係を登録するとき、または新しいオブジェクトを作成するときに、対応するセッターまたはプロパティを通じて依存関係を注入するためにコンテナーに提供される構成を指定できます。例:



use yiibaseObject;

class Foo extends Object

{

public $bar;

private $_qux;

public function getQux()

{

return $this->_qux;

}

public function setQux(Qux $qux)

{

$this->_qux = $qux;

}

}

$container->get('Foo' 、[]、[

'bar' => $container->get('Bar'),

'qux' => ' ),

]);

PHP コールバック インジェクション


この場合、コンテナは登録された PHP コールバックを使用してクラスの新しいインスタンスを作成します。コールバックは、依存関係を解決し、新しく作成されたオブジェクトに依存関係を適切に挿入する役割を果たします。例:




$container->set('Foo', function () {

return new Foo(new Bar);

});

$foo = $container->get('Foo');

依存関係を登録する


yiidiContainer::set() を使用して依存関係を登録できます。登録では、依存関係名と依存関係定義を使用します。依存関係名には、クラス名、インターフェイス名、またはエイリアスを使用できます。依存関係の定義には、クラス名、構成配列、または PHP コールバックを指定できます。



$container = new yiidiContainer;


// 類似した名前を同じ名前の依存関係。これは省略できます。

$container->set('yiidbConnection');

// インターフェースを登録します

// クラスがこのインターフェースに依存する場合、対応するクラスは依存オブジェクトとして初期化されます。

$container->set('yiimailMailInterface', 'yiiswiftmailerMailer');

// エイリアスを登録します。

// $container->get('foo') を使用して Connection インスタンスを作成できます

$container->set('foo', 'yiidbConnection');

// 設定を通じてクラスを登録します

// get() を通じて初期化されると、設定が使用されます。

$container->set('yiidbConnection', [

'dsn' => 'mysql:host=127.0.0.1;dbname=demo',

'ユーザー名' => 'root',

'パスワード' => '',

'charset' => 'utf8',

]);

// クラス設定を通じてエイリアスを登録します

// この場合、「class」要素を通じてクラスを指定する必要があります

$container- > ;set('db', [

'class' => 'yiidbConnection',

'dsn' => 'mysql:host=127.0.0.1;dbname=demo' ,

'ユーザー名' => 'root',

'パスワード' => '',

'文字セット' => 'utf8',

]);

// PHP コールバックを登録します

// $container->get('db') が呼び出されるたびに、コールバック関数は実行されました。

$container->set('db', function ($container, $params, $config) {

return new yiidbConnection($config);

} );

// コンポーネント インスタンスを登録します

// $container->get('pageCache') は呼び出されるたびに同じインスタンスを返します。

$container->set('pageCache', new FileCache);


ヒント: 依存関係名と依存関係関係の定義が同じであれば、DIコンテナを介して依存関係を登録する必要はありません。

set() を通じて登録された依存関係は、使用されるたびに新しいインスタンスを生成します。 yiidiContainer::setSingleton() を使用してシングルトンの依存関係を登録できます:


$container->setSingleton('yiidbConnection', [

'dsn' => 'mysql:host=127.0.0.1;dbname=demo',

'ユーザー名' => 'root',

'パスワード' => '',

'charset' => 'utf8',

]);


依存関係を解決する


依存関係を登録した後、DI コンテナを使用して新しいオブジェクトを作成できます。コンテナーは依存関係を自動的に解決し、インスタンス化し、新しく作成されたオブジェクトに注入します。依存関係の解決は再帰的です。依存関係に他の依存関係がある場合、これらの依存関係は自動的に解決されます。


yiidiContainer::get() を使用して新しいオブジェクトを作成できます。このメソッドは、クラス名、インターフェイス名、またはエイリアスなどの依存関係名を受け取ります。依存関係名は、set() または setSingleton() を介して登録できます。オプションで、クラス コンストラクターのパラメーター リストと、新しく作成されたオブジェクトを構成するための構成を提供できます。例:







// "db" は以前に定義されたエイリアスです

$db = $container->get('db');

// 以下と同等: $engine = new appcomponentsSearchEngine($apiKey, ['type' => 1]);

$engine = $container->get(' appcomponentsSearchEngine' , [$apiKey], ['type' => 1]);


コードの背後で、DI コンテナーはオブジェクトを作成するだけではありませんたくさんの仕事。コンテナーはまずクラスのコンストラクターをチェックして依存するクラス名またはインターフェイス名を見つけ、次にこれらの依存関係を再帰的に自動的に解決します。


次のコードは、より複雑な例を示しています。 UserLister クラスは UserFinderInterface インターフェイスを実装するオブジェクトに依存し、UserFinder クラスはこのインターフェイスを実装し、Connection オブジェクトに依存します。これらすべての依存関係は、クラス コンストラクター パラメーターの型ヒントを通じて定義されます。プロパティの依存関係を登録すると、DI コンテナはこれらの依存関係を自動的に解決し、単純な get('userLister') 呼び出しで新しい UserLister インスタンスを作成できます。



名前空間 appmodels;

yiibaseObject を使用;

yiidbConnection を使用;

yiidiContainer を使用;

インターフェース UserFinderInterface

{

関数 findUser();

}

class UserFinder extends Objectimplements UserFinderInterface

{

public $db;

public function __construct(Connection $db, $config = [])

{

$this->db = $db;

parent::__construct( $config);

}

public function findUser()

{

}

}

class UserLister extends Object

{

public $finder;

public function __construct(UserFinderInterface $ finder, $config = [])

{

$this->finder = $finder;

parent::__construct($config);

}

}

$container = 新しいコンテナ;

$container->set('yiidbConnection', [

'dsn' => '...',

]);

$container->set('appmodelsUserFinderInterface', [

'クラス ' => 'appmodelsUserFinder',

]);

$container->set('userLister', 'appmodelsUserLister');

$lister = $container->get('userLister');

// 以下と同等:

$db = new yiidbConnection( ['dsn' => '...']);

$finder = new UserFinder($db);

$lister = new UserLister($finder);


アプリケーションの実践


アプリケーションのphpファイルのエントリスクリプトにYiiを導入する場合、 Yii は DI コンテナを作成します。この DI コンテナには Yii::$container を通じてアクセスできます。 Yii::createObject() が呼び出されると、このメソッドは実際にこのコンテナの yiidiContainer::get() メソッドを呼び出して新しいオブジェクトを作成します。前述したように、DI コンテナーは依存関係 (存在する場合) を自動的に解決し、新しく作成されたオブジェクトに依存関係を挿入します。 Yii はほとんどのコアコードで Yii::createObject() を使用して新しいオブジェクトを作成するため、Yii::$container を通じてこれらのオブジェクトをグローバルにカスタマイズできます。


たとえば、yiiwidgetsLinkPager でデフォルトのページング ボタンの数をグローバルにカスタマイズできます。




Yii::$container->set('yiiwidgetsLinkPager', ['maxButtonCount' => 5]);


このように、次のコードを通じてビューでこのウィジェットを使用すると、その maxButtonCount プロパティは、クラスで定義されているデフォルト値 10 ではなく 5 に初期化されます。 。





echo yiiwidgetsLinkPager ::widget();


ただし、DI コンテナを介して設定された値をオーバーライドすることはできます:



echo yiiwidgetsLinkPager::widget(['maxButtonCount' => 20]);


別の例は、DI コンテナでの自動コンストラクター インジェクションの利点を借用することです。コントローラー クラスがホテル予約サービスなどの他のオブジェクトに依存しているとします。コンストラクター パラメーターを通じて依存関係を宣言し、DI コンテナーに依存関係を自動的に解決させることができます。




namespace appcontrollers;

use yiiwebController;

use appcomponentsBookingInterface;

class HotelController extends Controller

{

protected $bookingService;

public function __construct($id, $module, BookingInterface $bookingService, $config = [])

{

$this->bookingService = $bookingService;

parent::__construct($id, $module, $config);

}

}


ブラウザからこのコントローラーにアクセスすると、BookingInterface をインスタンス化できないことを通知するエラー メッセージが表示されます。これは、DI コンテナーにこの依存関係を処理する方法を指示する必要があるためです。


Yii::$container->set('appcomponentsBookingInterface', 'appcomponentsBookingService');

このコントローラに再度アクセスすると、 appcomponentsBookingService のインスタンスが作成され、3 番目のパラメーターとしてコントローラーのコンストラクターに挿入されます。


依存関係を登録するタイミング


新しいオブジェクトを作成するときに依存関係を解決する必要があるため、その登録は次のようにする必要がありますできるだけ早く行われます。以下は推奨される方法です:


アプリケーション開発者の場合は、アプリケーションのエントリ スクリプトまたはエントリ スクリプトによって導入されたスクリプトに依存関係を登録できます。

再頒布可能な拡張機能の開発者は、依存関係を拡張機能のブートストラップ クラスに登録できます。

QANDA.RENより転載

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。