web-dev-qa-db-ja.com

スプリングブートを使用した2方向SSLの実装

安らかなWebサービスをいくつか作成し、Spring-Bootを使用して埋め込みTomcatコンテナを作成しています。

要件の1つは、これが2方向SSLを実装することです。私はHttpSecurityオブジェクトを見てきましたが、これを使用してSSLチャネル上でのみWebサービスを実行することができます:

@Override
protected void configure(HttpSecurity http) throws Exception {

    System.out.println("CONFIGURED");

    http
        // ...
        .requiresChannel()
            .anyRequest().requiresSecure();
}

私が見つけられないように見えるのは、有効なクライアント証明書を提供するアプリケーションのみがWebサービスにアクセスできるようにする方法です。

私はSSLの基本的な知識しか持っていないので、正しい方向への一般的なポインタでさえ感謝されます。

これがデプロイされるサーバーには、アプリケーションが混在します。これは、双方向SSLでロックダウンする必要がある唯一のアプリケーションです。私が本当に探しているのは、単一のアプリケーションをロックダウンして、クライアント証明書のみを受け入れる方法です。

14
Andrew Mc

clientAuth=wantを構成できます。 Apache Tomcat 8構成リファレンス を参照してください。

SSLスタックが接続を受け入れる前にクライアントからの有効な証明書チェーンを必要とする場合は、trueに設定します。 SSLスタックがクライアント証明書を要求する場合はwantに設定しますが、提示されない場合は失敗しません。 false値(デフォルト)は、CLIENT-CERT認証を使用するセキュリティ制約で保護されたリソースをクライアントが要求しない限り、証明書チェーンを必要としません。

そして、クライアント証明書を Spring Security-X.509 Authentication で読み取ります:

「相互認証」でSSLを使用することもできます。サーバーは、SSLハンドシェイクの一部としてクライアントに有効な証明書を要求します。サーバーは、証明書が受け入れ可能な機関によって署名されていることを確認することにより、クライアントを認証します。有効な証明書が提供されている場合、アプリケーションのサーブレットAPIを介して取得できます。 Spring Security X.509モジュールは、フィルターを使用して証明書を抽出します。証明書をアプリケーションユーザーにマップし、そのユーザーの付与された権限のセットをロードして、標準のSpring Securityインフラストラクチャで使用します。

そして

クライアントが証明書を提供しなくてもSSL接続を成功させる場合は、clientAuthwantに設定することもできます。証明書を提示しないクライアントは、フォーム認証などの非X.509認証メカニズムを使用しない限り、Spring Securityで保護されたオブジェクトにアクセスできません。

6
dur

私は同様の問題に遭遇し、私が一緒に来た解決策を共有すると思いました。

まず、SSL証明書認証がウェブサーバー側で処理されることを理解する必要があります(cfr。durの説明、「clientAuth = want」設定を使用)。次に、提供された(および許可された)証明書を処理し、それをユーザーにマッピングするなどのために、Webアプリを構成する必要があります。

あなたとのわずかな違いは、スプリングブートアプリケーションをWARアーカイブにパッケージ化し、それを既存のTomcatアプリケーションサーバーにデプロイすることです。

私のTomcatのserver.xml構成ファイルは、次のようにHTTPSコネクターを定義します。

<Connector port="8443" protocol="org.Apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
    keystoreFile="/opt/Tomcat/conf/key-stores/ssl-keystore.jks"
    keystorePass=“some-complex-password“
    clientAuth="want" sslProtocol="TLS"
    truststoreFile="/opt/Tomcat/conf/trust-stores/ssl-truststore.jks"
    truststorePass=“some-other-complex-password” />

混乱を避けるための小さなコメント:keystoreFileにはSSL(のみ)に使用される証明書/秘密キーのペアが含まれ、truststoreFileにはクライアントSSL認証に許可されたCA証明書が含まれます(クライアント証明書をそのトラストストアに直接追加することもできます) 。

Spring Bootアプリケーションで埋め込みTomcatコンテナを使用している場合は、次のプロパティキー/値を使用して、アプリケーションのプロパティファイルでこれらの設定を構成できるはずです。

server.ssl.key-store=/opt/Tomcat/conf/key-stores/ssl-keystore.jks
server.ssl.key-store-password=some-complex-password
server.ssl.trust-store=/opt/Tomcat/conf/trust-stores/ssl-truststore.jks
server.ssl.trust-store-password=some-other-complex-password
server.ssl.client-auth=want

次に、私のWebアプリで、次のように特定のSSL構成を宣言します。

@Configuration
@EnableWebSecurity
//In order to use @PreAuthorise() annotations later on...
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SSLAuthConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${allowed.user}")
    private String ALLOWED_USER;

    @Value("${server.ssl.client.regex}")
    private String CN_REGEX;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure (final HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .authorizeRequests()
                .antMatchers("/url-path-to-protect").authenticated() //Specify the URL path(s) requiring authentication...
            .and()
                .x509() //... and that x509 authentication is enabled
                    .subjectPrincipalRegex(CN_REGEX)
                    .userDetailsService(userDetailsService);
    }

    @Autowired
    //Simplified case, where the application has only one user...
    public void configureGlobal (final AuthenticationManagerBuilder auth) throws Exception {
        //... whose username is defined in the application's properties.
        auth
            .inMemoryAuthentication()
                .withUser(ALLOWED_USER).password("").roles("SSL_USER");
    }

}

次に、UserDetailsS​​ervice Beanを宣言する必要があります(たとえば、アプリケーションのメインクラスで):

@Value("${allowed.user}")
private String ALLOWED_USER;

@Bean
public UserDetailsService userDetailsService () {

    return new UserDetailsService() {

        @Override
        public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
            if (username.equals(ALLOWED_USER)) {
                final User user = new User(username, "", AuthorityUtils.createAuthorityList("ROLE_SSL_USER"));
                return user;
            }
            return null;
        }
    };
}

以上です!次に、保護したいメソッドに@PreAuthorize(“ hasRole( ‘ROLE_SSL_USER″)”)注釈を追加できます。

少しまとめると、認証フローは次のようになります。

  1. ユーザーがSSL証明書を提供します。
  2. Tomcatはtrust-storeに対して検証します。
  3. カスタムWebSecurityConfigurerAdapterは、証明書のCNから「ユーザー名」を取得します。
  4. アプリケーションは、取得したユーザー名に関連付けられたユーザーを認証します。
  5. メソッドレベルで、@ PreAuthorize( "hasRole( 'SSL_USER')")アノテーションが付けられている場合、アプリケーションはユーザーが必要なロールを持っているかどうかを確認します。
10
veebee