traPtitech / knoQ

進捗部屋・イベント管理システム
8 stars 2 forks source link

不定期でサーバーのアクセストークンを利用した traQ API の利用ができなくなる #562

Closed iChemy closed 1 month ago

iChemy commented 2 months ago

不定期でタイトルの現象が発生し,TraQUserMiddleware などでエラーが返ってしまうためその間全ユーザーがknoQを利用できなくなる.

誰かがsessionを消しログインに成功すれば解消する.

iChemy commented 2 months ago

原因は var traqAPIConfig = traq.NewConfiguration()HTTPClient フィールドが NewOauth2APIClient を通して不正なトークンを利用したものに書き換えられてしまうこと.

iChemy commented 2 months ago

func (repo *TraQRepository) GetUser(userID uuid.UUID) (*traq.User, error)traqAPIConfig を元に traQ の API を叩くが func (repo *TraQRepository) GetUser(userID uuid.UUID) (*traq.User, error) 内で HTTPClient を書き換えることはないため先述のことが起こった後だと 不正な HTTPClient フィールドの traqAPIConfig でtraQ の API を叩きエラーが返ってしまう.

これは (当然) HTTPClient が有効なものに書き換えられない限り続く

iChemy commented 2 months ago

誰かがログインし直すと,その過程で func (repo *TraQRepository) GetUserMe(token *oauth2.Token) (*traq.User, error) が呼ばれ,これは func NewOauth2APIClient(ctx context.Context, token *oauth2.Token) *traq.APIClienttraqAPIConfigHTTPClient フィールドを書き換え,適切なトークンを利用してこれが行われれば有効な traqAPIConfig は有効な HTTPClient フィールドを保持するためエラーがなくなる.

iChemy commented 2 months ago

再現方法例

必要なもの: 2つの異なるブラウザ (ブラウザA, ブラウザBと呼称),bot の traQ へのアクセストークン

  1. あの手この手で ブラウザAに bot ユーザーとしてのsessionを保持さる.
  2. ブラウザBでは普通にtraQユーザーとしてのsessionを保持させる.
  3. func (repo *Repository) LoginUser(query, state, codeVerifier string) (*domain.User, error) を下のように書き換える (1例)
    
    var once = 0

func (repo Repository) LoginUser(query, state, codeVerifier string) (domain.User, error) { // 省略 t, err := repo.TraQRepo.GetOAuthToken(query, state, codeVerifier) // 省略 if once == 0 { // たまたま不正なトークンに書き換える fmt.Println("GetOAuthToken unexpectedly return invalid token") t = &oauth2.Token{ AccessToken: "123456789123456789123456789123456789", // invalid token } once++ } traQUser, err := repo.TraQRepo.GetUserMe(t) // 省略 }


4. 書き換えた後に docker で開発環境を立ち上げる.このとき,各ブラウザは session を保持したままである必要がある.
5. ブラウザA, B ともに session があるため,ログインせずに localhost:3000 にアクセスしてknoQ が利用できることを確認する. (このとき,ブラウザAのユーザーがちゃんとbotになっていることを確認すること)
6. ブラウザB の session を削除しログインを試みる.このとき上述の `LoginUser` が走り `traqAPIConfig` の `HTTPClient` フィールドが不正なトークンで書き換えられるのでログインはできないはずである.
7. ここで無関係なブラウザAで knoQ を利用しようとするとできなくなっていることを確認する.(<- knoQを開いてみたらなんか利用できなくなってる現象を再現できた)
8. ブラウザBで再びログインを試みる.一回しか不正なトークンに書き換えないため今度は成功するはずである.
9. ブラウザAでknoQが利用できることを確認する.(<-誰かがログインし直すことでなぜか利用できるようになる現象を再現できた)
iChemy commented 2 months ago
client := oauth2Config.Client(ctx, oauth2Token)

によって作成された *http.Client

req.Header.Set("Authorization", "Bearer ANOTHER_TOKEN")

なる *http.Requestreq で リクエストを送った場合, Authorization ヘッダーには oauth2Token の方が設定されていることが確認できたので HTTPClient フィールドを書き換えてるのが悪いという考えであってそう.

iChemy commented 1 month ago

closed by #565