Bug(バグ) #3232
完了OAuth のアクセストークンを取得していくる際にシークレットキーを用いた署名を検証できない
0%
説明
概要¶
OAuth のアクセストークンを取得していくる際にシークレットキーを用いた署名を検証できない.
クライアントは通常アクセストークンを取得してくる際に,事前に生成されているリクエストトークンとリクエストトークンのシークレットを用いて署名を行なう.しかし,クライアント側で正しいシークレットを用いた署名の実装を行なっていても,署名の検証に失敗してアクセストークンが取得できない.
原因¶
サーバ側では下記コード部のように,リクエストトークンを取得してくる際にシークレット部分を空文字列としており,このトークンオブジェクトを用いて署名を検証してしまう.
そのため,おそらくシークレットが空文字列であるトークンオブジェクトとして署名の検証を行なってしまうとおもわれるため,クライアントの実装がシークレットを空にするようになっている場合には成功するものと思われる.
(コードは 3.6.6 のもの)
lib/action/opOAuthTokenAction.class.php 86 行目
75 public function executeAccessToken(sfWebRequest $request) 76 { 77 require_once 'OAuth.php'; 78 79 $authRequest = OAuthRequest::from_request(); 80 $requestToken = $authRequest->get_parameter('oauth_token'); 81 $this->information = $this->getTokenTable()->findByKeyString($requestToken); 82 $this->forward404Unless($this->information); 83 $this->forward404Unless($this->information->getIsActive()); 84 $this->forward404Unless($this->information->getVerifier() === $authRequest->get_parameter('oauth_verifier')); 85 86 $token = $this->getServer()->fetch_access_token($authRequest); 87 88 $this->information->delete(); 89 90 $this->getResponse()->setContent((string)$token); 91 92 return sfView::NONE; 93 }
lib/vendor/OAuth/OAuth.php 470 行目
460 /** 461 * process an access_token request 462 * returns the access token on success 463 */ 464 public function fetch_access_token(&$request) { 465 $this->get_version($request); 466 467 $consumer = $this->get_consumer($request); 468 469 // requires authorized request token 470 $token = $this->get_token($request, $consumer, "request"); 471 472 473 $this->check_signature($request, $consumer, $token); 474 475 $new_token = $this->data_store->new_access_token($token, $consumer); 476 477 return $new_token; 478 }
lib/vendor/OAuth/OAuth.php 548 行目
543 /** 544 * try to find the token for the provided request's token key 545 */ 546 private function get_token(&$request, $consumer, $token_type="access") { 547 $token_field = @$request->get_parameter('oauth_token'); 548 $token = $this->data_store->lookup_token( 549 $consumer, $token_type, $token_field 550 ); 551 if (!$token) { 552 throw new OAuthException("Invalid $token_type token: $token_field"); 553 } 554 return $token; 555 }
lib/util/opOAuthDataStore.class.php 110 行目
105 public function lookup_token($consumer, $token_type, $token) 106 { 107 $tokenRecord = $this->getTokenTable()->findByKeyString($token, $token_type, $this->queryTemplate); 108 if ($tokenRecord) 109 { 110 $token = new OAuthToken($tokenRecord->getKeyString(), ''); 111 if ('request' !== $token_type) 112 { 113 $token->secret = $tokenRecord->getSecret(); 114 } 115 return $token; 116 } 117 118 return null; 119 }
修正案¶
lib/util/opOAuthDataStore.class.php 110 行目の空文字列のシークレットではないものを返す.
ただし,そのまま修正してしまうと既存の OpenPNE OAuth クライアントに影響が出るため,シークレットがある場合とない場合での署名の検証を行い,メジャーバージョンアップ時にシークレットがない場合の検証を取り除くという対応が考えられる.
この対応を行う場合にはメジャーバージョンアップ時に確認できるチケットを作成しておく必要が有る.
Yuya Watanabe さんが約12年前に更新
- ステータス を Accepted(着手) から Pending Review(レビュー待ち) に変更
- 進捗率 を 0 から 50 に変更
更新履歴 978cb7c92e9d4f10b0b7a8d3d469a63af980c0fa で適用されました。
Yuya Watanabe さんが11年以上前に更新
https://tools.ietf.org/html/rfc5849#section-2.1
When making the request, the client authenticates using only the
client credentials. The client MAY omit the empty "oauth_token"
protocol parameter from the request and MUST use the empty string as
the token secret value.
isao sano さんが約10年前に更新
- コピー先 Backport(バックポート) #3705: OAuth のアクセストークンを取得していくる際にシークレットキーを用いた署名を検証できない を追加
isao sano さんが約10年前に更新
- コピー先 Backport(バックポート) #3706: OAuth のアクセストークンを取得していくる際にシークレットキーを用いた署名を検証できない を追加
Youichi Kimura さんが約8年前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 担当者 を Youichi Kimura にセット
- 進捗率 を 50 から 70 に変更
- 3.6 で発生するか を Unknown (未調査) から Yes (はい) に変更
- 3.8 で発生するか を Unknown (未調査) から Yes (はい) に変更
元のコードで opOAuthDataStore::lookup_token()
で $token_type
が 'request'
の場合のみ token_secret を空文字列にする実装となっているのは、OAuth 1.0a でのリクエストトークン取得時には署名に空の token_secret が使われることを意識したものではないかと思います。
しかし実際には、リクエストトークン取得時は常に新規のトークンを発行するため OAuthDataStore::lookup_token()
が呼ばれることはありません。
一方で OAuthDataStore::lookup_token()
でリクエストトークンを要求される場面はさらに後のアクセストークン取得時であり、この時点では request_secret を使用した署名が行われています。ここで token_secret を空文字列に書き換えてしまうと、クライアントが生成した署名と検証のために生成した署名が一致しないことになってしまいます。
よって、そもそも OAuthDataStore::lookup_token()
を実装する上で token_secret を空にする処理は不要であり、DB から取得したトークンの組をそのまま返せばよいことになります。
以上のことから今回の不具合は 978cb7c9 の修正で問題ないと判断しました。
Youichi Kimura さんが約8年前に更新
下記の手順で動作確認できます (https://gist.github.com/upsilon/9a3ad8fb319276519ef90abe5f472235 を使用する):
$ wget https://github.com/openpne/OpenPNE3/raw/OpenPNE-3.8.20/lib/vendor/OAuth/OAuth.php $ wget https://gist.github.com/upsilon/9a3ad8fb319276519ef90abe5f472235/raw/25ad4b89a529e0235436cbcbdad557645eb7215f/apitest.php $ vim apitest.php # BASE_URL, CONSUMER_KEY, CONSUMER_SECRET を書き換え $ php apitest.php Authorize URL: http://sns.example.com/pc_backend.php/oauth/authorize?oauth_token=*** PIN Code > (Authorize URLにアクセスして表示されるPINコードを入力)