私は認証を必要とする REST API を開発しています。認証自体は HTTP 経由の外部 Web サービスを介して行われるため、認証サービスを繰り返し呼び出すことを避けるためにトークンを配布することにしました。これが最初の質問につながります。
これは、クライアントに各リクエストで HTTP 基本認証の使用を要求し、認証サービス サーバー側への呼び出しをキャッシュするよりも本当に優れているのでしょうか?
Basic Auth ソリューションには、コンテンツのリクエストを開始する前にサーバーとの完全な往復を必要としないという利点があります。トークンは、スコープの点でより柔軟になる可能性があります (つまり、特定のリソースまたはアクションに対する権限のみを付与する) が、これは私のより単純なユースケースよりも OAuth コンテキストに適しているようです。
現在、トークンは次のように取得されます:
curl -X POST localhost/token --data "api_key=81169d80...
&verifier=2f5ae51a...
×tamp=1234567
&user=foo
&pass=bar"
api_key
、およびtimestamp
はverifier
すべてのリクエストで必須です。「検証子」は次のように返されます。
sha1(timestamp + api_key + shared_secret)
私の意図は、既知の相手からの通話のみを許可し、通話がそのまま再利用されるのを防ぐことです。
これで十分でしょうか? 不十分でしょうか? 過剰でしょうか?
トークンを手にすることで、クライアントはリソースを取得できます。
curl localhost/posts?api_key=81169d80...
&verifier=81169d80...
&token=9fUyas64...
×tamp=1234567
可能な限り最も単純な呼び出しとしては、これはかなり冗長なようshared_secret
です。これは最終的には (少なくとも) iOS アプリケーションに埋め込まれ、そこから抽出できると想定されることを考えると、これは誤ったセキュリティ感覚以上のものを提供しているのでしょうか?
ベストアンサー1
すべてを分離して、それぞれの問題を個別に解決してみましょう。
認証
認証に関しては、baseauthはプロトコルレベルで成熟したソリューションであるという利点があります。これは多くのことを意味します。「後で発生する可能性があります」問題はすでに解決されています。たとえば、BaseAuth では、ユーザー エージェントはパスワードがパスワードであることを認識しているため、それをキャッシュしません。
認証サーバーの負荷
サーバー上で認証をキャッシュする代わりにユーザーにトークンを配布する場合でも、認証情報をキャッシュするという同じことを行っています。唯一の違いは、キャッシュの責任をユーザーに負わせることです。これはユーザーにとって不必要な労力であり、何の利益ももたらさないと思われるため、提案どおりサーバー上で透過的に処理することをお勧めします。
伝送セキュリティ
SSL 接続を使用できる場合、接続は安全です*。誤って複数実行されないようにするには、複数の URL をフィルターするか、ユーザーに URL にランダムなコンポーネント (「nonce」) を含めるように依頼します。
url = username:[email protected]/api/call/nonce
それが不可能で、送信された情報が秘密でない場合は、トークン アプローチで提案されているように、ハッシュを使用してリクエストを保護することをお勧めします。ハッシュはセキュリティを提供するため、ユーザーにハッシュをベース認証パスワードとして提供するように指示できます。堅牢性を向上させるには、リプレイ攻撃 (同じ秒に 2 つの正当なリクエストが送信される可能性があります) を防ぐために、タイムスタンプの代わりにランダムな文字列を「nonce」として使用することをお勧めします。「shared secret」フィールドと「api key」フィールドを別々に提供する代わりに、単に api key を共有シークレットとして使用し、レインボー テーブル攻撃を防ぐために変更されないソルトを使用できます。ユーザー名フィールドも、認証の一部であるため、nonce を配置するのに適した場所のようです。これで、次のようなクリーンな呼び出しができました。
nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:[email protected]/api/call
確かに、これは少し面倒です。これは、プロトコル レベルのソリューション (SSL など) を使用していないためです。したがって、ユーザーに何らかの SDK を提供して、少なくともユーザーが自分で操作しなくても済むようにするとよいかもしれません。この方法で行う必要がある場合、セキュリティ レベルは適切 (just-right-kill) だと思います。
安全な秘密保管
誰を阻止しようとしているかによって異なります。ユーザーの電話にアクセスできる人が、ユーザーの名前で REST サービスを使用することを阻止する場合は、ターゲット OS で何らかのキーリング API を見つけて、SDK (または実装者) にキーをそこに保存させるのがよいでしょう。それが不可能な場合は、少なくとも暗号化して、暗号化されたデータと暗号化キーを別の場所に保存することで、秘密の取得を少し難しくすることができます。
他のソフトウェアベンダーがAPIキーを入手できないようにして、代替クライアントの開発を阻止したい場合は、暗号化して別々に保存するアプローチのみが有効です。ほとんど機能します。これはホワイトボックス暗号であり、現在まで、このクラスの問題に対する真に安全な解決策を思いついた人はいません。少なくとも、各ユーザーに 1 つのキーを発行して、悪用されたキーを禁止することはできます。
(*) 編集: SSL接続もはや安全とはみなされないそれなし確認するための追加措置を講じる彼ら。