Skip to main content

アプリケーションをセキュアに

JHipsterによって生成されたもののように、単一のWebページアプリケーションでSpring Securityを使用するには、XHRによるログイン/ログアウト/エラービューが必要です。これらのビューを正しく使用するためにSpring Securityを設定し、すべてのJavaScriptとHTMLコードを生成します。

デフォルトでは、JHipsterには2つの異なるユーザがいます。

  • "user":これは"ROLE_USER"権限を持つ通常のユーザーです。デフォルトのパスワードは"user"です。
  • "admin":"ROLE_USER"および"ROLE_ADMIN"権限を持つ管理ユーザーです。デフォルトのパスワードは"admin"です。

"ROLE_USER"と"ROLE_ADMIN"の2つの権限は、エンティティに対して同じアクセスを提供します。つまり、"user"は"admin"と同じCRUD操作する権限を与えられています。この動作は、"user"が任意のエンティティを削除できるなどの理由で、アプリケーションが本番に移行するときに問題になる可能性があります。アクセス制御を改善する方法の詳細については、このブログ投稿を参照してください。

セキュリティ上の理由から、運用環境ではこれらのデフォルトパスワードを変更する必要があります。

JHipsterは、3つの主要なセキュリティメカニズムを提供します。

  1. JSON Web Tokens (JWT)
  2. セッションベースの認証
  3. OAuth 2.0とOpenID Connect

JSON Web Tokens (JWT)

JSON Web Token (JWT)認証はステートレス・セキュリティー・メカニズムなので、複数の異なるサーバー上でアプリケーションを拡張したい場合には良い選択肢です。

マイクロサービスアーキテクチャを使用する場合、これがデフォルトのオプションであることに注意してください。

この認証メカニズムは、デフォルトではSpring Securityには存在せず、Java JWTプロジェクトを統合しJHipsterに特化させたものです。

このソリューションでは、ユーザーのログイン名と権限を保持するセキュア・トークンを使用します。トークンは署名されているため、ユーザーによる変更はできません。

JHipsterは、無効なJWTをカスタムアプリケーションメトリクスとして自動的に追跡します。モニタリングドキュメントを参照してください。

JWTをセキュアに

  • JHipsterは、jhipster.security.authentication.jwt.secretjhipster.security.authentication.jwt.base64-secretという2つのSpring Bootプロパティを使用して設定できる秘密鍵を使用します。 2番目のオプションは、Base 64でエンコードされた文字列を使用し、より安全であると考えられるため、推奨します。両方のプロパティが設定されている場合は、レガシーな理由により、secretプロパティ(セキュリティが低い方)が使用されます。 Base64プロパティを使用しない場合は、アプリケーションの起動時に警告が表示されます。
  • これらのキーの最小長は512ビットである必要があります。十分な長さがない場合は、ログインに使用できません。その場合は、コンソールにその問題を説明する明確な警告が表示されます。
  • 秘密鍵はapplication-*.ymlファイルで設定されています。これらの鍵は秘密にしておく必要があるので、プロダクションのプロファイル用に安全な方法で保管する必要があります。 通常、Spring Bootプロパティ設定を使用して設定できます。JHipster RegistryのようなSpring Cloud Configサーバを使用するか、 環境変数を使用するか、システム管理者によってアプリケーションの実行可能なWARファイルと同じディレクトリにSCPで置かれた特有のapplication-prod.ymlファイルを使用します。
  • デフォルトの"user"および"admin"パスワードは変更する必要があります。これを行う最も簡単な方法は、アプリケーションをデプロイし、"user/user"としてログインしてから"admin/admin"としてログインし、それぞれに対して"Account > Password"メニューを使用してパスワードを変更することです。

セッションベースの認証

これは「古典的な」Spring Security認証メカニズムですが、大幅に改善されています。HTTPセッションを使用するため、ステートフルなメカニズムです。複数のサーバでアプリケーションを拡張する場合は、各ユーザが同じサーバにとどまるように、スティッキセッションを備えたロードバランサを使用するか、Spring Sessionを追加して、メモリではなくデータベースにセッションを格納することを検討する必要があります。

セッションベースの認証をセキュアに

  • Remember-me認証の場合、Remember-meキーはapplication-dev.ymlおよびapplication-prod.ymlファイル内で、jhipster.security.remember-me.keyプロパティとして設定されます。このキーは秘密にしておく必要があるため、プロダクションのプロファイル用に安全な方法で保存する必要があります。通常、Spring Bootプロパティ設定を使用して設定できます。JHipster RegistryのようなSpring Cloud Configサーバを使用するか、環境変数を使用するか、システム管理者によってアプリケーションの実行可能なWARファイルと同じディレクトリにSCPで置かれた特有のapplication-prod.ymlファイルを使用します。
  • デフォルトの"user"および"admin"パスワードは変更する必要があります。これを行う最も簡単な方法は、アプリケーションをデプロイし、"user/user"としてログインしてから"admin/admin"としてログインし、それぞれに対して"Account > Password"メニューを使用してパスワードを変更することです。

remember-meメカニズムの改善

私たちは、Spring Securityのremember-meメカニズムを変更して、データベース(SQLまたはNoSQLデータベースなど生成時の選択に応じて!)に格納される独自のトークンを持つようにしました。また、標準の実装よりも多くの情報を格納するようにし、トークンの出所(IPアドレス、ブラウザ、日付など)をよりよく理解できます。さらに、完全な管理画面を生成し、別のコンピュータでログアウトするのを忘れた場合などにセッションを無効にできるようにしました。

Cookieの盗難対策

私たちは、完成度の高いCoockie盗難防止メカニズムを追加しました。あなたのセキュリティ情報をCookieとデータベースに保存し、ユーザーがログインするたびにそれらの値を修正し、それらが変更されたかどうかをチェックします。そうすることで、誰かがあなたのCoockieを盗んだ場合でも、使用できるのは最大で1回だけます。

OAuth 2.0とOpenID Connect

OAuthは、HTTPセッションのようなステートフルなセキュリティメカニズムです。Spring Securityは、優れたOAuth 2.0とOIDCのサポートを提供しており、これはJHipsterによって活用されています。OAuthとOpenID Connect(OIDC)が何であるかわからない場合は、OAuthとは何か?を参照してください。

Keycloak

Keycloakは、JHipsterで設定されたデフォルトのOpenID Connectサーバです。

アプリケーションにログインするには、Keycloakを起動して実行する必要があります。JHipsterチームは、デフォルトのユーザーとロールを持つDockerコンテナを作成しました。次のコマンドを使用してKeycloakを起動します。

docker-compose -f src/main/docker/keycloak.yml up

あるいは、次のようなnpmの使用もできます。

npm run docker:keycloak:up

Docker ComposeでKeycloakを使用したい場合は、Docker Composeドキュメントを必ず読み、Keycloak用に/etc/hostsを正しく設定してください。

Apple Silicon(M1)上のJHipster 7.8.1およびKeycloak 16.1.0に関する注意

v18より前のKeycloakは、互換モードのApple Siliconで誤動作する可能性があり、解決策は明らかではありません。この問題はKeycloakイメージをローカルに構築することで解決できます。

git clone [email protected]:keycloak/keycloak-containers.git
cd keycloak-containers/server
git checkout 16.1.0
docker build -t jboss/keycloak:16.1.0 .

このイメージには、src/main/resources/config/application.ymlのセキュリティ設定が構成されています。上記の/etc/hostsに関する注意を参照し、issuer-uriを変更する必要がある場合があることに注意してください。

spring:
...
security:
oauth2:
client:
provider:
oidc:
issuer-uri: http://localhost:9080/auth/realms/jhipster
# localhostは、ホストではなく、ゲスト(コンテナ)にバインドされます。
# Keycloakをデーモンとして実行するには、npm run docker:keycloak:upとなり、/etc/hostsの編集と、
# issuer-uriを次のようにする必要があります。
# issuer-uri: http://keycloak:9080/auth/realms/jhipster
registration:
oidc:
client-id: web_app
client-secret: web_app
scope: openid,profile,email

Keycloakはデフォルトで組み込みH2データベースを使用しているため、Dockerコンテナを再起動すると、作成されたユーザが失われます。データを保持するには、Keycloak Dockerドキュメントを参照してください。H2データベースを保持する1つの解決策は、以下を実施します。

  • 永続化するボリュームを追加します:./keycloak-db:/opt/jboss/keycloak/standalone/data
  • マイグレーション方法をOVERWRITE_EXISTINGからIGNORE_EXISTINGに変更します(コマンドセクション内)

本番環境では、HTTPSを使用することがKeycloakによって要求されます。これを実現するには、HTTPSを管理するリバース・プロキシまたはロード・バランサを使用するなど、いくつかの方法があります。このトピックの詳細を知るには、Keycloak HTTPSドキュメントを読むことをお薦めします。

Auth0

Keycloakの代わりにAuth0を使用する場合は、次の設定手順に従います。

Auth0 Admin Dashboardを使用したOIDCアプリケーションの作成

  • https://auth0.com/signup で無料の開発者アカウントを作成します。サインアップが成功すると、アカウントはdev-xxx.us.auth0.comのような独自のドメインに関連付けられます。

  • Regular Web Applicationsタイプの新しいアプリケーションを作成します。Settings タブに切り替えて、次のようにアプリケーションの設定を構成します。

    • Allowed Callback URLs: http://localhost:8080/login/oauth2/code/oidc
    • Allowed Logout URLs: http://localhost:8080/
    • 注意:Consulを使用している場合は、ポート8500のURLも追加します。
    • 注意:JHipsterレジストリを使用している場合は、ポート8761のURLも追加します。
  • User Management > Rolesに移動し、ROLE_ADMINおよびROLE_USERという名前の新しいロールを作成します。

  • User Management > Usersに移動し、新しいユーザーアカウントを作成します。 Roleタブをクリックして、新しく作成したユーザーアカウントに役割を割り当てます。

  • Actions > Flowsに移動し、Loginを選択します。Add Rolesという名前の新しいアクションを作成し、デフォルトのトリガーとランタイムを使用します。onExecutePostLoginハンドラを次のように変更します。

    exports.onExecutePostLogin = async (event, api) => {
    const namespace = 'https://www.jhipster.tech';
    if (event.authorization) {
    api.idToken.setCustomClaim('preferred_username', event.user.email);
    api.idToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
    api.accessToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
    }
    }
  • Deployを選択し、Add Rolesアクションをログインフローにドラッグします。

これらの手順をすべて自動化したい場合は、Auth0 CLIプロジェクトのissue #351に👍を追加してください。

Auth0をOIDCプロバイダとして使用するようにJHipsterアプリケーションを構成

JHipsterアプリケーションで、Auth0設定を使用するようにsrc/main/resources/config/application.ymlを変更します。

spring:
...
security:
oauth2:
client:
provider:
oidc:
# 必ず最後のスラッシュを含めるようにしてください!
issuer-uri: https://{your-auth0-domain}/
registration:
oidc:
client-id: {clientId}
client-secret: {clientSecret}
scope: openid,profile,email
jhipster:
security:
oauth2:
audience: https://{your-auth0-domain}/api/v2/

issuer-uriの値に疑問がある場合は、Applications > {Your Application} > Settings > Advanced Settings > Endpoints > OpenID Configurationから値を取得できます。.well-known/openid-configurationのサフィックスはSpring Securityによって追加されるので削除してください。

Applications > API > API AudienceフィールドからデフォルトのAuth0 Management APIオーディエンス値を使用できます。独自のカスタムAPIを定義して、識別子をAPIオーディエンスとして使用することもできます。

  • Cypressテストを実行する前に、CYPRESS_E2E_USERNAMEおよびCYPRESS_E2E_PASSWORD環境変数を上書きして、Auth0ユーザーの詳細を指定してください。詳細については、Cypressのドキュメントを参照してください。
export CYPRESS_E2E_USERNAME=<your-username>
export CYPRESS_E2E_PASSWORD=<your-password>

注意:Auth0では、ユーザーは最初のログイン時に認可の同意を提供する必要があります。同意フローは現在、Cypressテストスイートでは処理されません。この問題を軽減するには、すでに同意を付与しているユーザーアカウントを使用して、対話型ログインを介したアプリケーションアクセスを許可できます。

Cypressで認証の問題が発生した場合の回避策については、このガイドを参照してください。

環境変数の使用

環境変数を使用してのデフォルトの上書もできます。次に例を示します。

export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI="https://{your-auth0-domain}/"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID="{client-id}"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET="{client-secret}"
export JHIPSTER_SECURITY_OAUTH2_AUDIENCE="https://{your-auth0-domain}/api/v2/"

これを~/.auth0.envファイルに入れてsource ~/.auth0.envを実行すると、デフォルトのKeycloak設定をAuth0でオーバーライドして、MavenまたはGradleでアプリを起動できます。登録した資格情報を使用してサインインできるはずです。

注意:Windowsの場合は、sourceコマンドが動作するように、WSLをインストールする必要があります。

Auth0でモバイル用のネイティブアプリを作成する

JHipsterのIonicまたはReact NativeのBlueprintを使用してモバイルアプリを開発している場合、OIDCを使用しているなら、Auth0でネイティブアプリを作成することになる可能性があります。

  1. Nativeアプリケーションを作成し、次のAllowed Callback URLsを追加します。

    • Ionic: http://localhost:8100/callback,dev.localhost.ionic:/callback
    • React Native: http://localhost:19006/,https://auth.expo.io/@<username>/<appname>
  2. Allowed Logout URLsを次のように設定します。

    • Ionic: http://localhost:8100/logout,dev.localhost.ionic:/logout
    • React: http://localhost:19006/,https://auth.expo.io/@<username>/<appname>
  3. Allowed Origins (CORS)を設定します。

    • Ionic: http://localhost:8100,capacitor://localhost,http://localhost
    • React Native: http://localhost:19006

Ionicアプリをアップデートする

生成されたクライアントIDを使用するように、ionic/src/environments/environment.tsを更新します。server_hostの値は、JHipsterアプリケーション(/api/auth-info)から検索されますが、フォールバック値としての定義もできます。また、audienceも指定する必要があります。以下は例です。

const oidcConfig: IAuthConfig = {
client_id: '<native-client-id>',
server_host: 'https://<your-auth0-domain>/',
...
};

export const environment = {
...
audience: 'https://<your-auth0-domain>/api/v2/',
...
};

Ionicアプリを再起動し、Auth0でログインします!

Reactネイティブアプリのアップデート

クライアントIDをapp/config/app-config.jsにコピーします。

app/modules/login/login.utils.tsaudienceを更新します。

audience: 'https://<your-auth0-domain>/api/v2/',

React Nativeアプリを再起動し、Auth0でログインします!

Okta

Keycloakの代わりにOktaを使用したい場合は、Okta CLIを使用するとかなり早いです。インストールしたら、次のコマンドを実行します。

okta register

次に、JHipsterアプリのディレクトリでokta apps create jhipsterを実行します。これにより、Oktaアプリが設定され、ROLE_ADMINグループとROLE_USERグループが作成され、Okta設定を含む.okta.envファイルが作成され、IDトークンにgroupsクレームが設定されます。

source .okta.envを実行し、MavenまたはGradleでアプリを起動します。登録した資格情報を使用してサインインできるはずです。

Windowsの場合は、sourceコマンドが動作するように、WSLをインストールする必要があります。

Okta Admin Consoleから手動で設定する場合は、次の手順を参照してください。

Okta管理コンソールを使用したOIDCアプリケーションの作成

まず、 https://developer.okta.com/signup で無料の開発者アカウントを作成する必要があります。その後、https://dev-123456.okta.comのような名前の独自のOktaドメインを取得します。

Oktaの設定を使うためにsrc/main/resources/config/application.ymlを変更します。ヒント:{yourOktaDomain}をあなたの組織の名前に置き換えてください(例:dev-123456.okta.com)。

security:
oauth2:
client:
provider:
oidc:
issuer-uri: https://{yourOktaDomain}/oauth2/default
registration:
oidc:
client-id: {client-id}
client-secret: {client-secret}
scope: openid,profile,email

OktaでOIDCアプリを作成し、{client-id}{client-secret}を取得します。これを行うには、Okta Developerアカウントにログインし、Applications > Applications > Add Application > Create New Appに移動します。Web, OpenID Connectを選択し、Createをクリックします。アプリに覚えやすい名前を付け、ログインリダイレクトURIとしてhttp://localhost:8080/login/oauth2/code/oidcを指定します。ログアウトリダイレクトURIとしてhttp://localhost:8080を追加し、Saveをクリックします。クライアントIDとシークレットをapplication.ymlファイルにコピーします。

ROLE_ADMINおよびROLE_USERグループを作成し(Directory > Groups > Add Group)、それらにユーザーを追加します。サインアップしたアカウントを使用するか、新しいユーザーを作成します(Directory > People > Add Person)。Security > API > Authorization Serversに移動し、defaultサーバーをクリックします。Claimsタブをクリックし、Add Claimをクリックします。groupsという名前を付けて、IDトークンに含めます。値のタイプをGroupsに設定し、フィルタを.*の正規表現に設定します。Createをクリックします。

Add Claim

これらを変更した後、問題なく使用できるはずです! もし問題があればStack Overflowに投稿してください。質問には必ず"jhipster"と"okta"のタグを付けてください。

e2eテストの実行時にOktaを使用するには、環境変数を設定できます。

export CYPRESS_E2E_USERNAME=<your-username>
export CYPRESS_E2E_PASSWORD=<your-password>

Protractorを使用している場合は、CYPRESS_プレフィックスを削除してください。

環境変数の使用

環境変数を使用してデフォルトの上書もできます。次に例を示します。

export SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI="https://{yourOktaDomain}/oauth2/default"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID="{client-id}"
export SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET="{client-secret}"

これを~/.okta.envファイルに入れてsource ~/.okta.envを実行すると、KeycloakをOktaでオーバーライドできます。

次に、Herokuにデプロイするときにこれらのプロパティを設定できます。

heroku config:set \
SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI="$SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI" \
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID="$SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID" \
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET="$SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET"

Oktaでモバイル用のネイティブアプリを作成する

JHipsterのIonicまたはReact NativeのBlueprintを使用してモバイルアプリを開発している場合、OIDCを使用しているなら、Oktaでネイティブアプリを作成することになる可能性があります。

Okta CLIを使用してokta apps createを実行します。デフォルトのアプリ名を選択するか、必要に応じて変更します。Nativeを選択してEnterを押します。

  • Ionic:リダイレクトURIを[http://localhost:8100/callback,dev.localhost.ionic:/callback]に変更し、ログアウトリダイレクトURIを[http://localhost:8100/logout,dev.localhost.ionic:/logout]に変更します。
  • React Native:リダイレクトURIには[http://localhost:19006/,https://auth.expo.io/@<username>/<appname>]を使用してください。

注意: dev.localhost.ionicはデフォルトのスキームですが、com.okta.dev-133337(ここでdev-133337.okta.comはあなたのOkta OrgのURLです)のような、よりトラディショナルなものも使用できます。これを変更する場合は、Ionicアプリのsrc/environments/environment.tsschemeを必ず更新してください。

Okta CLIは、Okta OrgにOIDC Appを作成します。指定したリダイレクトURIを追加し、Everyoneグループへのアクセスを許可します。

Okta application configuration:
Issuer: https://dev-133337.okta.com/oauth2/default
Client ID: 0oab8eb55Kb9jdMIr5d6

注意:Okta管理コンソールを使用してもアプリを作成できます。詳細については、ネイティブアプリの作成を参照してください。

Ionicアプリをアップデートする

ionic/src/environments/environment.tsを開き、NativeアプリからクライアントIDを追加します。server_hostの値はJHipsterアプリ(/api/auth-info)から検索されますが、フォールバック(代替)値として定義できます。以下は例です。

oidcConfig: {
client_id: '<native-client-id>',
server_host: 'https://<your-okta-domain>/oauth2/default',
...
}

また、http://localhost:8100用にトラステッド・オリジンを追加する必要があります。Okta Admin Consoleで、Security > API > Trusted Origins > Add Originに移動します。次の値を使用します。

  • Name: http://localhost:8100
  • Origin URL: http://localhost:8100
  • Type: CORS and Redirectの両方をチェック

Saveをクリックします。

Ionicアプリを再起動して、Oktaでログインします!

Reactネイティブアプリのアップデート

クライアントIDをapp/config/app-config.jsにコピーします。

Ionicアプリを再起動して、Oktaでログインします!

OpenID Connectチュートリアル

JHipster 5とOIDC with Oktaの詳細については、JHipsterでOpenID Connectサポートを使用するを参照してください。

JHipster 6を使用している場合は、Java 12とJHipster 6によるJavaの改善、高速化、軽量化を参照してください。JHipster 6でマイクロサービスを使用している場合は、Spring Cloud ConfigとJHipsterを使ったJavaマイクロサービスを参照してください。

JHipster 7については、Spring BootとJHipsterを使ったリアクティブJavaマイクロサービスを参照してください。

Okta開発者ブログには、MicronautとQuarkusへの ❤️ も掲載されています。

HTTPS

HTTPSの使用を強制するには、次の設定をSecurityConfiguration.javaに追加します。

// Spring MVC
http.requiresChannel(channel -> channel
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null).requiresSecure());

// WebFlux
http.redirectToHttps(redirect -> redirect
.httpsRedirectWhen(e -> e.getRequest().getHeaders().containsKey("X-Forwarded-Proto")));

詳細については、Spring SecurityのServletWebFluxのドキュメントを参照してください。

これはHerokuとGoogle Cloudでテストされ、動作することが確認されています。Herokuでのプロダクションのヒントについては、Herokuでの運用のためのSpring Bootアプリの準備を参照してください。

実装詳細の漏洩

すべての失敗/例外は、問題データ構造にマップされ、クライアントに返されます。

{  
"type": "https://www.jhipster.tech/problem/problem-with-message",
"title": "Service Unavailable",
"status": 503,
"detail": "Database not reachable"
}

JHipsterはデフォルトではスタックトレースを含んでいませんが、detailには例外のmessageが含まれ、APIを介して公開されたくない技術的詳細 が表れてしまう可能性があります。

{  
"type": "https://www.jhipster.tech/problem/problem-with-message",
"title": "Bad Request",
"status": 400,
"detail": "JSON parse error: Cannot deserialize instance of
`java.util.LinkedHashMap<java.lang.Object,java.lang.Object>` out of VALUE_NUMBER_INT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap<java.lang.Object,java.lang.Object>`
out of VALUE_NUMBER_INT token\n at [Source: (PushbackInputStream); line: 1, column: 1]"
}

これを防ぐために、JHipsterは、実装の詳細の漏れを軽減するための専用のメカニズムを提供します。

  • 既知の例外をチェックし、メッセージを一般的なメッセージに置き換えます(例:Unable to convert http message)。
  • メッセージに潜在的なパッケージ名(例:java.または.org)が含まれているかどうかをチェックし、メッセージを一般的なもの(例:Unexpected runtime exception)で置き換えます。

ログには依然として詳細な例外が含まれているため、外部からの攻撃者がAPIを悪用して貴重な技術的詳細を得ることができない間に、 実際の問題を特定できます(訳注:miusing→misusing)。

ロジックを変更する必要がある場合(メッセージに技術的な詳細が含まれていても検出されなかった場合など)は、 必要なロジックをExceptionTranslator.javaprepareメソッドに追加することで変更できます。