1. ホーム
  2. authentication

[解決済み] REST API トークンによる認証

2022-08-06 10:35:52

質問

認証を必要とするREST APIを開発しています。認証自体はHTTP上の外部Webサービスを介して行われるため、認証サービスを繰り返し呼び出すのを避けるために、トークンを配布することを推論しました。これは、私の最初の質問にきちんと私をもたらします。


これは、各リクエストで HTTP Basic Auth を使用するようクライアントに要求し、サーバー側の認証サービスの呼び出しをキャッシュするよりも本当に良いのでしょうか?

Basic Auth ソリューションには、コンテンツへのリクエストを開始する前にサーバーへの完全なラウンドトリップを必要としないという利点があります。トークンは、潜在的により柔軟な範囲 (つまり、特定のリソースまたはアクションへの権利のみを付与) を持つことができますが、それは私の単純な使用例よりも OAuth コンテキストに適しているようです。

現在、トークンはこのように取得されます。

curl -X POST localhost/token --data "api_key=81169d80...
                                     &verifier=2f5ae51a...
                                     &timestamp=1234567
                                     &user=foo
                                     &pass=bar"

api_key , timestampverifier はすべてのリクエストで必要です。"verifier"が返されます。

sha1(timestamp + api_key + shared_secret)

私の意図は、既知の相手からの通話のみを許可し、通話がそのまま再利用されるのを防ぐことです。

これでよいのでしょうか? アンダーキルですか? オーバーキル?

トークンを手にしたクライアントは、リソースを獲得することができます。

curl localhost/posts?api_key=81169d80...
                    &verifier=81169d80...
                    &token=9fUyas64...
                    &timestamp=1234567

可能な限りシンプルな呼び出しのために、これはちょっと恐ろしく冗長なようです。というのは shared_secret が (最低でも) iOS アプリケーションに埋め込まれ、そこから抽出されることを考えると、これは間違ったセキュリティの感覚を超えて何かを提供することになるのでしょうか?

どのように解決するのですか?

すべての問題を分離して、それぞれの問題にアプローチして解決しましょう。

認証

認証のために、baseauthはプロトコルレベルで成熟したソリューションであるという利点があります。これは多くの "might crop up later" の問題がすでに解決されていることを意味します。たとえば、BaseAuth を使用すると、ユーザーエージェントはパスワードがパスワードであることを知っているので、キャッシュされません。

Authサーバの負荷

認証情報をサーバーにキャッシュする代わりに、トークンをユーザーに配布しても、認証情報のキャッシュという点では同じです。唯一の違いは、キャッシュの責任をユーザーに転嫁していることです。これは、ユーザーにとって不必要な労力であり、何の利益もないように思えます。

送信のセキュリティ

SSL接続を使用できれば、それだけで、接続は安全です*。誤って複数回実行されることを防ぐために、複数のURLをフィルタリングしたり、URLにランダムな要素("nonce")を含めるようユーザーに依頼したりすることができます。

url = username:[email protected]/api/call/nonce

それが不可能で、送信される情報が秘密でない場合、トークンのアプローチで提案したように、ハッシュでリクエストを保護することをお勧めします。ハッシュはセキュリティを提供するので、ユーザーにbaseauthパスワードとしてハッシュを提供するように指示することができます。堅牢性を高めるために、タイムスタンプの代わりにランダムな文字列を"nonce"として使用し、リプレイ攻撃(同じ秒数の間に2つの正当なリクエストが行われる可能性がある)を防ぐことを推奨します。共有シークレットフィールドとAPIキーフィールドを別々に提供する代わりに、単純にAPIキーを共有シークレットとして使用し、レインボーテーブル攻撃を防ぐために変更されないソルトを使用することができます。usernameフィールドは認証の一部なので、nonceを置くのに良い場所と思われます。これで、次のようなきれいな呼び出しができるようになりました。

nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:[email protected]/api/call

これが少し手間がかかるのは事実です。これは、(SSLのような)プロトコルレベルのソリューションを使用していないためです。ですから、少なくともユーザー自身がそれを経験する必要がないように、何らかの SDK をユーザーに提供するのは良いアイデアかもしれません。この方法でやる必要があるのなら、セキュリティレベルは適切(ジャスト・ライト・キル)だと思います。

安全な秘密保持

誰を阻止しようとしているかによります。ユーザーの携帯電話にアクセスできる人が、ユーザー名で REST サービスを使用するのを防ぐのであれば、ターゲット OS で何らかのキーリング API を見つけ、SDK (または実装者) がそこにキーを保存するようにするのがよいアイデアでしょう。それが不可能な場合、少なくとも暗号化し、暗号化されたデータと暗号化キーを別々の場所に保存することで、秘密を入手するのを少し難しくすることができます。

代替クライアントの開発を防ぐために、他のソフトウェア ベンダーに API キーを取得させないようにする場合は、暗号化して保存する方法のみが有効です。 ほとんど

が有効です。これはホワイトボックス暗号であり、今日まで、このクラスの問題に対する真に安全な解決策は誰も思いつきませんでした。少なくともできることは、悪用されたキーを禁止できるように、各ユーザーに 1 つのキーを発行することです。

(*) 編集中です。 SSL 接続 は安全であるとみなされなくなりました。 がなければ を検証するための追加の手順を踏まずに を行う必要があります。