web-dev-qa-db-ja.com

PHP REST API

REST APIをレガシーPHPサイトに追加する作業を行っています。これは、内部アプリのエンドポイントを提供するためです。私はものをデザインし、何をサポートし、何をサポートしません。

このAPIに追加する必要があるのは、ログインして特定のユーザーとしてアクションを実行する方法です。このサイトは何年も前に構築されたものであり、必ずしもその時点でのベストプラクティスではありません。そのため、残念ながら私はこれを行う方法が少し制限されています。これはすべて、PHP 5.4とMySQL 5.6で実行する必要があります。

私はこのための一般的な設計を読んでおり、OAuth1/2は最も一般的な標準のように見えます。しかし、これは私の目的にとっては大げさすぎるように思えます。なぜなら、これには必要のないさまざまな機能があり、実装が非常に複雑だからです。

代わりに、私はこのようなことをすることを計画しています:

  • クライアントはget_sessionランダムセッションIDを生成するAPIエンドポイントは、それをデータベースのテーブルに保存し、クライアントに返します。
  • クライアントはこのセッションIDを保存します。
  • 次に、クライアントはloginエンドポイントにリクエストを送信し、ユーザー名、パスワード、およびセッションIDを(明らかにHTTPS経由で)送信することにより認証します。
  • サーバーはデータをユーザーテーブルと比較し、ログインが正しい場合、セッションテーブルを更新して、セッションIDを対応するユーザーIDに関連付けます。ブルートフォーシングを防ぐために、何らかの方法でこれをレート制限する必要があります。
  • これで、クライアントは、承認のためにセッションIDのみを提供する他のエンドポイントを呼び出すことができます。
  • サーバーは、リクエストごとにセッションIDを検索し、関連付けられているユーザーを確認して正しいアクションを実行します。
  • クライアントは、手動で削除されるか、しばらくしてから期限が切れるまで、将来使用するためにセッションIDを記憶できます。
  • ログアウトするために、クライアントはlogoutエンドポイントにリクエストを送信し、サーバーはユーザーアカウントとの関連付けを削除します。

これは合理的な設計ですか?それほど洗練されたものではないことは明らかですが、大きな手間やサードパーティのライブラリを必要とせずに実装できるものを探しています。

10
Nils

RESTの概念としての主要な点の1つは、RESTエンドポイントのリソースを水平方向にスケーリングしやすくするために、セッション状態の使用を避けることです。 PHPの$_SESSION質問で概説したように、スケールアウトしたい場合に共有セッションストレージを実装しなければならないという難しい立場にいることに気づくでしょう。

OAuthがあなたのやりたいことのための好ましい方法でしょうが、完全な実装はあなたが入れたいと思うよりも多くの仕事でありえます。しかし、あなたは半分の何かを切り分けることができます。あなたはおそらく同様のソリューションを見たことがあります。

  1. APIアカウントがプロビジョニングされると、トークンとシークレットの2つのランダムな値が生成されます。
  2. クライアントがリクエストを行うと、提供されます:
    • 平文のトークン。
    • 一意だが既知の値と秘密から計算された値。例:HMACまたは暗号署名
  3. RESTエンドポイントは、トークンとシークレットのシンプルで集中化されたキー値ストアを維持し、値を計算してリクエストを検証できます。

このようにして、「セッションレス」REST理想を維持し、交換のどの部分でも実際にシークレットを送信することはありません。

クライアントの例:

$token  = "Bmn0c8rQDJoGTibk";                 // base64_encode(random_bytes(12));
$secret = "yXWczx0LwgKInpMFfgh0gCYCA8EKbOnw"; // base64_encode(random_bytes(24));
$stamp  = "2017-10-12T23:54:50+00:00";        // date("c");
$sig    = hash_hmac('SHA256', $stamp, base64_decode($secret));
// Result: "1f3ff7b1165b36a18dd9d4c32a733b15c22f63f34283df7bd7de65a690cc6f21"

$request->addHeader("X-Auth-Token: $token");
$request->addHeader("X-Auth-Signature: $sig");
$request->addHeader("X-Auth-Timestamp: $stamp");

サーバーの例:

$token  = $request->getToken();
$secret = $auth->getSecret($token);
$sig    = $request->getSignature();

$success = $auth->validateSignature($sig, $secret);

タイムスタンプをナンスとして使用することに決めた場合、リプレイ攻撃を防ぐために最後の数分以内に生成されたタイムスタンプのみを受け入れる必要があることに注意してください。他のほとんどの認証方式では、リソースパス、ヘッダーデータのサブセットなど、署名されたデータに追加のコンポーネントが含まれ、単一の要求にのみ適用されるように署名をさらにロックダウンします。

20
Sammitch

一意のトークンを生成し、それを通信に使用する必要があると思います。基本的に:

  • クライアントは、loginリソースにユーザー名/パスワードを送信します。
  • サーバーは、ユーザー名とパスワードの組み合わせを検証します。正しい場合は、一意のtoquenを生成し、sessionsテーブルに保存して、logged_in = TRUEなどのステータス更新とともにユーザーに送り返します。
  • これで、ユーザーが送信する他のすべての要求には、tokenフィールド(POSTフィールドまたはGETパラメーターとして)を含める必要があります。この時点で、RESTを使用して、すべてにPOSTリクエストのみを使用し、operationをPOST =フィールド。これはトークンをURLに追加しないため、履歴、ルーターなどを閲覧するWebにトークンを登録できます。
  • リクエストごとに、サーバーはトークンが存在し、有効かどうかを確認する必要があります。そうでない場合は、403 Forbiddenlogged_in = FALSEなどのエラーメッセージを返すだけです。

システムは、クライアントで生成されたunique idなどの別のデータを送信して、トークンで送信し、サーバー側でチェックする必要がある場合もあります。

1
Alejandro Iván

計画のポイントは、基本的にOAuthの基本機能です。要件によって異なります。 APIが内部使用専用である場合、HMAC-SHA認証を使用して秘密鍵を送信できます。

0

さて、最初からクッキーを発明する方法と、それらをデータベースに保存する方法をたくさん書きました。

同時に、セキュリティデータ転送用のユーザー名、パスワード、およびHTTPSが既にあります。なぜクッキーだけを使用しないのですか?

0
user1597430