RESTful Webサービス-他のサービスからのリクエストを認証する方法は?


117

ユーザーだけでなく他のWebサービスやアプリケーションからもアクセスする必要があるRESTful Webサービスを設計しています。すべての着信要求は認証される必要があります。すべての通信はHTTPSを介して行われます。ユーザー認証は、サービスが提供する/ sessionリソースに(SSL接続を介して)ユーザー名とパスワードをPOSTすることで取得した認証トークンに基づいて機能します。

Webサービスクライアントの場合、クライアントサービスの背後にエンドユーザー存在しません要求は、スケジュールされたタスク、イベント、またはその他のコンピューター操作によって開始されます。接続サービスのリストは事前にわかっています(明らかに、私は推測します)。他の(ウェブ)サービスからのこれらのリクエストをどのように認証すればよいですか?認証プロセスは、これらのサービスに実装するためにできるだけ簡単にしたいが、セキュリティを犠牲にしてはいけない。このようなシナリオの標準およびベストプラクティスは何ですか?

私が考えることができる(または私に提案された)オプション:

  1. クライアントサービスに「偽の」ユーザー名とパスワードの使用を依頼し、ユーザーと同じ方法でそれらを認証します。私はこのオプションが好きではありません-それはちょうど気分が悪いだけです。

  2. クライアントサービスに永続的なアプリケーションIDを割り当てます。アプリケーションキーも割り当てる可能性があります。私が理解している限り、これはユーザー名とパスワードを持つことと同じです。このIDとキーを使用して、各要求を認証するか、認証トークンを作成して、それ以降の要求を認証できます。どちらにしても、このオプションは好きではありません。アプリケーションIDとキーを入手できる人はだれでもクライアントになりすますことができるからです。

  3. 前のオプションにIPアドレスチェックを追加できました。これにより、偽のリクエストを実行することが難しくなります。

  4. クライアント証明書。独自の認証局をセットアップし、ルート証明書を作成し、クライアントサービスのクライアント証明書を作成します。ただし、2つの問題が頭に浮かびます。a)ユーザーが証明書なしで認証できるようにするにはどうすればよいですか。b)クライアントサービスの観点からこのシナリオを実装するのはどのくらい複雑ですか。

  5. 何か他のもの-そこに他の解決策があるに違いない?

私のサービスはJavaで実行されますが、具体的なフレームワークについての情報は意図的に省略しました。これは、実装の詳細ではなく、基本原則に関心があるためです-これに対する最善の解決策は基礎となるフレームワークに関係なく実装することが可能です。ただし、私はこのテーマに少し慣れていないため、実際の実装に関する具体的なヒントや例(有用なサードパーティのライブラリ、記事など)も高く評価されます。


私が提案する可能性がある場合は、ビッグボックスのWebサイトサービスに慣れ、好きなものを選んでください。ユーザーは、他のRESTfulサービスのベストプラクティスと類似点があることもわかります。
イズミールラミレス

:同様の主題に触れる別の質問(ほぼ2歳)が見つかりstackoverflow.com/questions/1138831/...
トミ

ホストされているサービス(Webとその他の両方)はどのOSですか?同じインフラストラクチャの一部であるサーバーで実行されていますか?
Anders Abel

OSは異なります:Win、* nixなど。そしてクライアントサービスは、私のサービスと同じインフラストラクチャ内にある場合とない場合があります。
Tommi

回答:


34

この問題の解決策はすべて、共有秘密に要約されます。また、ハードコードされたユーザー名とパスワードのオプションは好きではありませんが、非常に単純であるという利点があります。クライアント証明書も良いですが、本当に違いますか?サーバーとクライアントに証明書が1つずつあります。主な利点は、総当たりするのが難しいことです。うまくいけば、それを防ぐために他の保護が整っているはずです。

クライアント証明書ソリューションのポイントAは解決が難しいとは思いません。ブランチを使用するだけです。if (client side certificat) { check it } else { http basic auth }私はJavaの専門家ではないため、クライアント側の証明書を作成するためにJavaを使用したことがありません。しかし、簡単なGoogleは、あなたの路地を正しく調べるこのチュートリアルに私たちを導きます。

この「何が最善」の議論にもかかわらず、「コードが少ないほど、賢さは少ない方が良い」という別の哲学があることを指摘しておきます。(私は個人的にこの哲学を持っています)。クライアント証明書ソリューションは、多くのコードのように聞こえます。

OAuthについて質問をされたと思いますが、OAuth2の提案には、SSLと組み合わせて使用​​する必要がある「ベアラトークン」と呼ばれる問題の解決策が含まれています。簡単にするために、ハードコードされたユーザー/パス(アプリごとに1つずつ、個別に取り消すことができるようにする)または非常によく似たベアラートークンのどちらかを選択すると思います。


27
クライアント証明書は共有秘密ではありません。それが彼らが存在する理由です。クライアントには秘密鍵があり、サーバーには公開鍵があります。クライアントが秘密を共有することは決してなく、公開鍵は秘密ではありません。
Tim

5
チュートリアルリンクは、チュートリアル記事ではなく、OracleサイトのいくつかのJavaインデックスページに
つながり

2
:あなたはnewz2000は答えた2年後にリンクをしようとしているが、あなたは常にウェイバックマシンを試すことができるためだ@MarjanVenemaまあ、web.archive.org/web/20110826004236/http://java.sun.com/...
ファビオデュケシルバ2014年

1
@MarjanVenema:すみませんが、newz2000がここに来て、リンクが切れた後にリンクを更新することを期待していますか?あなたが言ったように、それはリンク腐敗なので、遅かれ早かれ起こります。アーカイブにアクセスして、その時点で作成者が何を見ていたかを確認するか、新しいリンクを見つけて積極的に貢献してください。あなたのコメントがどのように役に立ったかはわかりません。しかし、ここで、このリンクは、次のとおりです。oracle.com/technetwork/articles/javase/...(それはあまりにも最終的に腐敗だろうことを注意してください)
ファビオ・デュケ・シルバ

2
@FábioSilva:いいえ、私は彼がそうすることを期待していません。私はアーカイブを読みましたが、新しいリンクを探す時間がなかったので、次の最善策を実行しました:コミュニティの誰かが新しい場所を見つけて更新できるように、それは死んでいるというコメントを入れます役職。明らかに新しいリンクを見つける時間があったので、コメントを投稿する代わりに、投稿のリンクを更新してみませんか?
Marjan Venema 2014年

36

あなたの質問を読んだ後、私は、要求された要求を行うための特別なトークンを生成すると言います。このトークンは特定の時間に有効になります(1日で言いましょう)。

次に、認証トークンを生成するためのの例を示します。

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

例:2011年6月3日

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

次に、ユーザーパスワードと連結します。例:「my4wesomeP4ssword!」

11630my4wesomeP4ssword!

次に、その文字列のMD5を実行します。

05a9d022d621b64096160683f3afe804

リクエストを呼び出すときは常にこのトークンを使用し、

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

このトークンは常に毎日一意であるため、この種の保護はサービスを常に保護するには十分すぎると思います。

願ってる

:)


1
私はすべてのリクエストにセキュリティトークンを追加する方法が本当に好きですが、プログラマーがすでに数百のjspページを作成し、その後t0が以前に作成された100ページと作成されるページにセキュリティを実装するとどうなりますか。その場合、各リクエストにトークンを追加するのは正しい選択ではありません。:)
Ankur Verma

4
クロックが同期されていないとどうなりますか?この場合、クライアントは間違ったトークンを生成しませんか?両方がUTCで日時を生成する場合でも、それらのクロックはまだ異なる可能性があり、その結果、トークンが機能しない場合、毎日の時間枠になりますか?
NickG 2013年

@NickG、私は以前にこの問題を抱えていました。サーバー時間を要求してこれを保護する唯一の方法です。これはUTCの問題を99%殺します。もちろん、欠点はサーバーへの追加の呼び出しです。
kororo

しかし、私はまだこのトークンを使ってあなたのウェブサービスを一日使うことができるのですか これがどのように役立つかわかりません
ミナガブリエル

@MinaGabrielでは、トークン生成に時間枠を追加できます。(分* 10)+(時間* 100)+(日* 1000)+(月* 10000)+(年(下2桁)* 100000)
kororo

11

あなたが取ることができるいくつかの異なるアプローチがあります。

  1. RESTful純粋主義者は、BASIC認証を使用し、すべての要求で資格情報を送信することを望んでいます。彼らの論理的根拠は、誰もいかなる状態も保存していないということです。

  2. クライアントサービスは、セッションIDを維持するCookieを保存できます。私が個人的にこれを聞いた純粋主義者の一部ほど不快に思うことはありません-何度も何度も認証することは高価になる可能性があります。しかし、あなたはこの考えをあまり好きではないようです。

  3. あなたの説明から、これまでOAuth2に興味を持っているように思えますが、これまでの私の経験から、これは一種の混乱を招くものであり、一種の最先端です。そこに実装がありますが、それらはほとんどありません。Javaでは、Spring3のセキュリティモジュールに統合されていることを理解しています。(彼らのチュートリアルは上手く書かれています。)Restletに拡張機能があるかどうかを待ち望んでいましたが、今のところ、提案されていて、インキュベーターにある可能性がありますが、まだ完全には組み込まれていません。


オプション2に反対することは何もありません。RESTfulアプリではそれは素晴らしい解決策だと思いますが、最初にクライアントサービスはどこからトークンを取得しますか?彼らはどのようにして初めて認証するのですか?多分私はこれを間違っていると思っていますが、クライアントサービスがこのために独自のユーザー名とパスワードを必要とするのは奇妙に思えます。
Tommi、

エンドユーザーがあなたのユーザーである場合、仲介サービスは最初のリクエストで資格情報を渡して、Cookieまたは他のトークンを返すことができます。
jwismar

同様に、OAuthシナリオでは、エンドユーザーは中間サービスにWebサービスへのアクセスを委任しています。
jwismar、2011年

誤解があるようです- クライアントサービスの背後にエンドユーザーまったくいないのです。状況をよりよく説明するために質問を更新しました。
Tommi、

1
上記の#1オプションはHTTPSでのみ実行する必要があることを追加します。
mr-sk

3

私はアプローチを信じています:

  1. 最初のリクエスト、クライアントはID /パスコードを送信します
  2. 一意のトークンのID /パスを交換する
  3. トークンが期限切れになるまで、後続の各リクエストでトークンを検証します

実装方法やその他の特定の技術的詳細に関係なく、かなり標準的です。

本当にエンベロープをプッシュしたい場合は、資格情報が検証されるまでクライアントのhttpsキーを一時的に無効な状態と見なし、資格情報が検証されない場合は情報を制限し、検証されたときに再び有効期限に基づいてアクセスを許可します。

お役に立てれば


3

クライアント証明書のアプローチに関する限り、クライアント証明書を持たないユーザーを許可しながら実装することはそれほど難しくありません。

実際に独自の自己署名証明書発行局を作成し、各クライアントサービスにクライアント証明書を発行した場合、これらのサービスを簡単に認証できます。

使用しているWebサーバーによっては、クライアント証明書を受け入れるが、それを必要としないクライアント認証を指定する方法が必要です。たとえば、Tomcatでhttpsコネクタを指定する場合、「true」または「false」の代わりに「clientAuth = want」を設定できます。次に、自己署名CA証明書をトラストストアに追加します(Webサーバー構成で別のファイルを指定しない限り、デフォルトで、使用しているJREのcacertsファイル)。そのため、信頼できる証明書は、自己署名CA。

サーバー側では、リクエストからクライアント証明書を取得できる場合(null以外)、保護するサービスへのアクセスのみを許可し、追加のセキュリティが必要な場合はDNチェックに合格します。クライアント証明書のないユーザーの場合、ユーザーはサービスにアクセスできますが、リクエストに証明書が存在しないだけです。

私の意見では、これは最も「安全な」方法ですが、学習曲線とオーバーヘッドがあるので、ニーズに最適なソリューションであるとは限りません。


3

5.何か他に-そこに他の解決策があるに違いない?

そうです、あります!そして、それはJWT(JSON Web Tokens)と呼ばれています。

JSON Web Token(JWT)はオープンな標準(RFC 7519)で、パーティ間で情報をJSONオブジェクトとして安全に送信するためのコンパクトで自己完結型の方法を定義しています。この情報はデジタル署名されているため、検証および信頼できます。JWTは、シークレット(HMACアルゴリズムを使用)またはRSAを使用した公開鍵/秘密鍵のペアを使用して署名できます。

JWTを調べることを強くお勧めします。代替ソリューションと比較すると、これらは問題に対するはるかに単純なソリューションです。

https://jwt.io/introduction/


1

サーバー上にセッションを作成sessionIdし、各REST呼び出しでクライアントとサーバー間で共有できます。

  1. 最初にRESTリクエストを認証します/authenticate。(クライアントのフォーマットに従って)で応答を返しますsessionId: ABCDXXXXXXXXXXXXXX

  2. この店sessionIdではMap実際のセッションで。Map.put(sessionid, session)またはSessionListener、キーを作成して破棄するために使用します。

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. 次のようにURL?jsessionid=ABCDXXXXXXXXXXXXXX(または他の方法で)REST呼び出しごとにセッションIDを取得します。

  4. を使用HttpSessionしてマップから取得しますsessionId
  5. セッションがアクティブな場合、そのセッションのリクエストを検証します。
  6. 応答またはエラーメッセージを返信します。

0

ユーザーがリクエストを承認すると、他のアプリが認証に使用する一意のトークンを生成するリクエストを承認すると、アプリケーションがユーザーをサイトにリダイレクトするアプリケーションを使用します。このようにして、他のアプリケーションはユーザー資格情報を処理せず、他のアプリケーションをユーザーが追加、削除、および管理できます。Foursquareと他のいくつかのサイトはこの方法で認証し、他のアプリケーションとして実装するのは非常に簡単です。


ええと、説明をたどることができたかどうかはわかりません。どのユーザーについて話しているのですか?アプリケーションが別のアプリケーションと通信することについて話している。あなたはこれを理解したと思いますが、私はまだ完全には理解できません。たとえば、この「トークン」が期限切れになるとどうなりますか?
Tommi

生成して他のアプリケーションに送り返すトークンは永続的なトークンであり、ユーザーとアプリケーションに関連付けられています。これがfoursquares docs developer.foursquare.com/docs/oauth.htmlへのリンクです。基本的にはoauth2なので、適切な認証ソリューションを探してください。
Devin M

誤解があるようです- クライアントサービスの背後にエンドユーザーまったくいないのです。あなたがリンクしたfoursquareのドキュメントは、ユーザーレスアクセスについて簡単に述べていますが、少なくともある程度は役に立ちました。ありがとうございます。しかし、それが実際にどのように機能するかを完全に把握することはできません。
Tommi、

アプリケーションのキーを生成するだけで、アプリケーションのアクセスを許可するだけであれば、単純なapplication_idとapplication_keyが認証に機能するはずです。それらをトークンで認証させたい場合は、単にdeviseのトークン認証オプションを使用して調べてください。これは、アプリケーションへのurlリクエストで渡されるパラメーターに過ぎないからです。
Devin M '27

しかし、これはユーザー名+パスワード=セッション認証トークンのシナリオとまったく同じではありませんか?application_idとapplication_keyは、ユーザー名とパスワードの同義語ではありませんか?:)これが本当にこのような状況の標準的な方法である場合は問題ありません-私が言ったように、私はこれについて経験がありません-しかし、他のオプションがあるかもしれないと思っただけです...
Tommi

-3

認証以外にも、全体像について考えることをお勧めします。認証なしでバックエンドRESTfulサービスを作成することを検討してください。次に、非常に単純な認証が必要な中間層サービスをエンドユーザーとバックエンドサービスの間に配置します。


エンドユーザーとバックエンドサービスの間?これにより、クライアントサービスが完全に認証されないままになりませんか?それは私が欲しいものではありません。もちろん、その中間層をWebサービスとクライアントサービスの間に配置することもできますが、それでも疑問は残ります。実際の認証モデルはどうなるのでしょうか。
Tommi、

中間層はNginxなどのWebサーバーにすることができ、そこで認証を行うことができます。認証モデルはセッションベースにすることができます。
Dagang

私の質問で説明しようとしたように、これらのクライアントサービスにはセッションベースの認証方式は必要ありません。(質問への私の更新を参照してください。)
Tommi '27

名前とパスワードの代わりに、ホワイトIPリストまたはIP範囲を使用することをお勧めします。通常、クライアントサービスIPは安定しています。
Dagang
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.