#[Requires] 属性の使用方法

Web アプリケーションを作成していると、アプリケーションの特定の部分へのアクセスを制限する必要性にしばしば直面します。一部のリクエストはフォーム(つまり POST メソッド)を使用してのみデータを送信できるようにしたり、AJAX コールのみにアクセスできるようにしたりしたい場合があります。Nette Framework 3.2 では、このような制限を非常にエレガントかつ明確に設定できる新しいツールが登場しました。それが #[Requires] 属性です。

属性は、クラスまたはメソッドの定義の前に追加する PHP の特別なマークです。これは実際にはクラスであるため、以下の例が機能するためには、use 句を含める必要があります。

use Nette\Application\Attributes\Requires;

#[Requires] 属性は、Presenter クラス自体、および以下のメソッドで使用できます。

  • action<Action>()
  • render<View>()
  • handle<Signal>()
  • createComponent<Name>()

最後の 2 つのメソッドはコンポーネントにも関連するため、属性はコンポーネントでも使用できます。

属性が指定する条件が満たされない場合、HTTP エラー 4xx がスローされます。

HTTP メソッド

アクセスが許可される HTTP メソッド(GET、POST など)を指定できます。たとえば、フォームの送信によるアクセスのみを許可したい場合は、次のように設定します。

class AdminPresenter extends Nette\Application\UI\Presenter
{
	#[Requires(methods: 'POST')]
	public function actionDelete(int $id): void
	{
	}
}

状態を変更するアクションになぜ GET ではなく POST を使用すべきか、そしてその方法については? ガイドを読む

メソッドまたはメソッドの配列を指定できます。特別なケースは値 '*' で、これはすべてのメソッドを許可します。これは、Presenter が通常 セキュリティ上の理由で許可されていません

AJAX コール

Presenter またはメソッドを AJAX リクエストに対してのみ利用可能にしたい場合は、次を使用します。

#[Requires(ajax: true)]
class AjaxPresenter extends Nette\Application\UI\Presenter
{
}

同一オリジン

セキュリティを強化するために、リクエストが同一ドメインから行われることを要求できます。これにより、CSRF 脆弱性 を防ぐことができます。

#[Requires(sameOrigin: true)]
class SecurePresenter extends Nette\Application\UI\Presenter
{
}

handle<Signal>() メソッドでは、同一ドメインからのアクセスが自動的に要求されます。したがって、逆に任意のドメインからのアクセスを許可したい場合は、次のように指定します。

#[Requires(sameOrigin: false)]
public function handleList(): void
{
}

フォワード経由のアクセス

Presenter へのアクセスを間接的にのみ、たとえば他の Presenter から forward() または switch() メソッドを使用してのみ利用可能にするように制限すると便利な場合があります。このようにして、たとえばエラー Presenter が URL から呼び出されるのを防ぎます。

#[Requires(forward: true)]
class ForwardedPresenter extends Nette\Application\UI\Presenter
{
}

実際には、Presenter 内のロジックに基づいて初めてアクセスできる特定のビューを指定する必要があることがよくあります。つまり、再び、直接開くことができないようにするためです。

class ProductPresenter extends Nette\Application\UI\Presenter
{

	public function actionDefault(int $id): void
	{
		$product = $this->facade->getProduct($id);
		if (!$product) {
			$this->setView('notfound');
		}
	}

	#[Requires(forward: true)]
	public function renderNotFound(): void
	{
	}
}

特定のアクション

特定のコード、たとえばコンポーネントの作成などを、Presenter 内の特定のアクションに対してのみ利用可能にするように制限することもできます。

class EditDeletePresenter extends Nette\Application\UI\Presenter
{
	#[Requires(actions: ['add', 'edit'])]
	public function createComponentPostForm()
	{
	}
}

単一のアクションの場合、配列を記述する必要はありません:#[Requires(actions: 'default')]

カスタム属性

#[Requires] 属性を同じ設定で繰り返し使用したい場合は、#[Requires] を継承し、必要に応じて設定する独自の属性を作成できます。

たとえば、#[SingleAction]default アクション経由でのみアクセスを許可します。

#[\Attribute]
class SingleAction extends Nette\Application\Attributes\Requires
{
	public function __construct()
	{
		parent::__construct(actions: 'default');
	}
}

#[SingleAction]
class SingleActionPresenter extends Nette\Application\UI\Presenter
{
}

または、#[RestMethods] は REST API に使用されるすべての HTTP メソッド経由でのアクセスを許可します。

#[\Attribute]
class RestMethods extends Nette\Application\Attributes\Requires
{
	public function __construct()
	{
		parent::__construct(methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
	}
}

#[RestMethods]
class ApiPresenter extends Nette\Application\UI\Presenter
{
}

まとめ

#[Requires] 属性は、Web サイトへのアクセス方法について大きな柔軟性とコントロールを提供します。シンプルでありながら強力なルールを使用して、アプリケーションのセキュリティと適切な動作を向上させることができます。ご覧のとおり、Nette で属性を使用すると、作業が容易になるだけでなく、安全にもなります。