Keycloak

Keycloak最新情報

NRIのOpenStandiaが提供するKeycloak最新情報

OSSの保守サポートサービスの内容はこちら OSSの導入については、こちらのフォームからお問い合わせ下さい OSS全般の事例紹介はこちら

Keycloakの実際・翻訳プロジェクト紹介

翻訳した日本語ドキュメント(Keycloakのインストール方法や使い方ガイド)のリンク集は日本語ドキュメントにございます。

Keycloakのサポート対応事例

Javaアダプターでトークン更新処理が競合してエラーが発生する問題を解消

KeycloakのJavaアダプターには、アクセストークンの有効期限が切れる前(もしくは切れた後)に内部処理でリフレッシュトークンを利用してトークン更新を行う機能があります。

ある顧客環境ではセキュリティの要件により、Keycloakでリフレッシュトークンの再利用不可(Revoke Refresh Token: ON、Refresh Token Max Reuse: 0)に設定していました。この設定の場合、発行されたリフレッシュトークンは1回だけ利用可能で、同じ値の再利用は不可となります。このような環境でJavaアダプターのトークン更新処理が競合してしまい、リフレッシュトークンの再利用不可のエラーが発生するという問い合わせがあり、調査を行いました。

再現確認および調査

顧客環境では、Keycloak 6.0.1(およびJavaサーブレットフィルターアダプター)が使われていました。まずは、その当時の最新版であるKeycloak 9.0.0でも再現するかどうか、シンプルなSPAを作成し再現確認を行います。

Keycloakでは下記のようにリフレッシュトークンの再利用不可の設定および、アクセストークンの有効期限を最小(1分)に設定します。

keycloakブルートフォース攻撃対策

Javaサーブレットフィルターアダプターの適用されているSPAでは、Ajaxにより指定した回数のリクエストを同時に投げられるボタンをいくか用意しておき、非同期で複数のリクエストが行われる状況を再現できるようにしておきます(カッコ内の数字が同時に投げるリクエスト数を表しています)。

keycloakブルートフォース攻撃対策

問題発生時のcatalina.out

下記はこのアプリケーションにログインしてから、数分経過後(アクセストークンの有効期限切れの後)に "ajaxPosting(3)"ボタンを押下した場合のcatalina.outログの一部抜粋です。


03-Mar-2020 20:15:35.743 DEBUG [http-nio-8080-exec-1] org.keycloak.adapters.RefreshableKeycloakSecurityContext.refreshExpiredToken Token Verification succeeded!
03-Mar-2020 20:15:35.794 ERROR [http-nio-8080-exec-10] org.keycloak.adapters.RefreshableKeycloakSecurityContext.refreshExpiredToken Refresh token failure status: 400 {"error":"invalid_grant","error_description":"Maximum allowed refresh token reuse exceeded"}
03-Mar-2020 20:15:35.957 ERROR [http-nio-8080-exec-2] org.keycloak.adapters.RefreshableKeycloakSecurityContext.refreshExpiredToken Refresh token failure status: 400 {"error":"invalid_grant","error_description":"Maximum allowed refresh token reuse exceeded"}

最初のリクエストに対応するスレッド(http-nio-8080-exec-1)では、リフレッシュトークンは成功しますが、後続の2リクエストに対応するスレッド(http-nio-8080-exec-10、http-nio-8080-exec-2)でもトークン更新処理が実行されてしまい、リフレッシュトークンの再利用不可のエラー("Maximum allowed refresh token reuse exceeded")が発生していることが分かります。

そこで、Javaサーブレットフィルターアダプター側のトークン更新箇所のソースコード調査を進めたところ、下記の「★★★」の箇所のメソッド呼び出しに排他制御がかかっておらず、同タイミングで複数のリクエストがあると、同じリフレッシュトークンでトークン更新処理を複数のスレッドで競合して呼び出してしまうことが分かりました。

OIDCFilterSessionStore#checkCurrentToken メソッド内


@Override
public void checkCurrentToken() {
    HttpSession httpSession = request.getSession(false);
    if (httpSession == null) return;
    SerializableKeycloakAccount account = (SerializableKeycloakAccount)httpSession.getAttribute(KeycloakAccount.class.getName());
    if (account == null) {
        return;
    }

    RefreshableKeycloakSecurityContext session = account.getKeycloakSecurityContext();
    if (session == null) return;

    // just in case session got serialized
    if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);

    if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;

    // FYI: A refresh requires same scope, so same roles will be set.  Otherwise, refresh will fail and token will
    // not be updated
    boolean success = session.refreshExpiredToken(false);  <=== ★★★
    if (success && session.isActive()) return;

    // Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
    //log.fine("Cleanup and expire session " + httpSession.getId() + " after failed refresh");
    cleanSession(httpSession);
    httpSession.invalidate();
}

暫定的にこのメソッド内のsessionインスタンスの利用される範囲をsynchronizedブロックで排他制御を加えたところ、問題が再現しなくなることが確認できました。つまり、Javaアダプターのリフレッシュトークン処理ではスレッドの排他制御に関して、潜在的なバグがあることが判明しました。

まとめ

上記のような調査結果から、以下の3つの条件を満たした場合に、問題の事象が再現することが確認できました。

  • Keycloak 10.0.0より前のJavaアダプターを利用
  • Keycloakのセキュリティ設定で、リフレッシュトークンの再利用不可(Revoke Refresh Token: ON"、Refresh Token Max Reuse: 0)の設定を利用
  • アクセストークンの有効期限が切れた状態で、アプリケーションのAjaxなどから、Javaアダプターに同時に複数のリクエストが発生する

直接の原因は、Javaアダプター内のトークン更新処理に排他制御がかかっていなことに起因したものでした。当該問題に関しては、Keycloakのコミュニティに報告を行い、バグとして修正されました。 実際のやりとりは以下の通りです。

Keycloak 10.0.0以上でこの問題は修正されています。

OpenStandiaに関する
資料請求・お問い合わせはこちら

  • 資料請求
  • お問い合わせ

※定期的にメンテナンスを実施しておりますが、一部情報が古い場合がございます。ご了承ください。

  • OpenStandiaサポート対象オープンソース|50種類以上のOSSのサポートをご提供します。
  • 人気midPoint
  • 人気Keycloak
  • 注目MongoDB
  • ForgeRock AM(OpenAM)
  • ForgeRock IDM(OpenIDM)
  • MongoDB
  • Postfix
  • Apache HTTP Server
  • ZABBIX
  • PostgreSQL
  • Apache Struts
  • Apache Kafka
  • Apache Hadoop
  • Apache Spark
  • Spring Framework
  • Apache Tomcat
  • Solr
  • iBATIS
  • DRBD
  • MySQL
  • JBoss
  • Ruby on Rails
  • Jaspersoft
  • OpenLDAP
  • Apache log4j
  • Apache Subversion
  • ForgeRock DS(OpenDJ)
  • Pacemaker
  • Samba
  • Red Hat Enterprise Linux
  • Nginx
  • BIND
  • Dovecot
  • Pentaho
  • sendmail
  • Courier-IMAP
  • ForgeRock DS(OpenDJ)
  • Heartbeat
  • Hibernate
  • Hinemos
  • MyBatis
  • MySQL Cluster
  • Apache Axis2
  • Squid
  • OpenSSO