Original post written by Jimena Garbarino for Auth0 blog.
Storing secrets in your code is a bad idea. Learn how to use Spring Cloud Config and HashiCorp Vault to make your app more secure.
In 2013, GitHub released a search feature that allows users to scan code in all public repositories. A day after the release, however, they had to partially shut it down. It was speculated that the shutdown was because the feature allowed any user to search for all kinds of secrets stored in GitHub repositories. Later, in 2014, data on 50,000 Uber drivers were stolen. It seems someone got access to the company’s database using login credentials found in a GitHub public repository. Hashicorp Vault, a tool for managing secrets and encrypting data in transit, was first announced in 2015, and Spring Vault, the integration of Spring with Vault, was first released in 2017.
It seems like a long time ago, right? Secrets leakage seems to remain pervasive and constant, happening to all kinds of developers—as explained by this study from NC State University. Exposed secrets lead to cyber-attacks, data loss or corruption, sensitive data breaches, and crypto-jacking (cryptocurrency mining using a victim’s cloud computer power). With tools like Hashicorp’s Vault and Spring Cloud Vault, the risk can be reduced.
Nowadays, it is widely recommended never to store secret values in code. Therefore, this tutorial will demonstrate the following alternatives:
- Using environment variables for Spring Boot secrets
- Secrets encryption with Spring Cloud Config
- Secrets management with HashiCorp’s Vault
- Using Spring Cloud Vault
This tutorial was created with the following frameworks and tools:
‣ Java OpenJDK 17
‣ Okta CLI 0.10.0
‣ Docker 20.10.12
‣ HTTPie 3.2.1
‣ Vault 1.12.0
Use Environment Variables for Secrets; A Precursor to Spring Vault
Spring Boot applications can bind property values from environment variables. To demonstrate, create a vault-demo-app
with OpenID Connect (OIDC) authentication using the Spring Initializr. Then add web
, okta
, and cloud-config-client
dependencies, some of which will be required later in the tutorial:
https start.spring.io/starter.zip \
bootVersion==2.7.4 \
dependencies==web,okta,cloud-config-client \
groupId==com.okta.developer \
artifactId==vault-demo-app \
name=="Spring Boot Application" \
description=="Demo project of a Spring Boot application with Vault protected secrets" \
packageName==com.okta.developer.vault > vault-demo-app.zip
Unzip the file and open the project. Modify its src/main/java/.../Application.java
class to add the /
HTTP endpoint:
package com.okta.developer.vault;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping("/")
String hello(@AuthenticationPrincipal OidcUser user) {
return String.format("Welcome, %s", user.getFullName());
}
}
Disable the cloud configuration for the first run. Edit application.properties
and add the following value:
spring.cloud.config.enabled=false
OpenID Connect authentication with Okta
In a command line session, go to the vault-demo-app
root folder.
Before you begin, you’ll need a free Okta developer account. Install the Okta CLI and run okta register
to sign up for a new account. If you already have an account, run okta login
. Then, run okta apps create
. Select the default app name, or change it as you see fit. Choose Web and press Enter.
Select Okta Spring Boot Starter. Accept the default Redirect URI values provided for you. That is a Login Redirect of http://localhost:8080/login/oauth2/code/okta
and a Logout Redirect of http://localhost:8080
.
What does the Okta CLI do?
The Okta CLI will create an OIDC Web App in your Okta Org. It will add the redirect URIs you specified and grant access to the Everyone group. You will see output like the following when it’s finished:
Okta application configuration has been written to:
/path/to/app/src/main/resources/application.properties
Open src/main/resources/application.properties
to see the issuer and credentials for your app.
okta.oauth2.issuer=https://dev-133337.okta.com/oauth2/default
okta.oauth2.client-id=0oab8eb55Kb9jdMIr5d6
okta.oauth2.client-secret=NEVER-SHOW-SECRETS
> NOTE: You can also use the Okta Admin Console to create your app. See Create a Spring Boot App for more information.
Instead of storing Okta credentials in application.properties
as part of the project code, Spring Boot allows you to bind properties from environment variables. You can see this in action by starting your application with the Maven command below:
OKTA_OAUTH2_ISSUER={yourOktaIssuerURI} \
OKTA_OAUTH2_CLIENT_ID={yourOktaClientId} \
OKTA_OAUTH2_CLIENT_SECRET={yourOktaClientSecret} \
./mvnw spring-boot:run
NOTE: Copy the values of
yourOktaIssuerURI
,yourOktaClientId
, andyourOktaClientSecret
as you will need them for configuration in the next sections. You can also just keep at handyourOktaClientId
and retrieve the configuration withokta apps config --app {yourOktaClientId}
.
In an incognito window, go to http://localhost:8080
. Here, you should see the Okta login page:
In the application logs, you’ll see the security filter chain initializes an OAuth 2.0 authentication flow on startup:
2022-09-07 08:50:09.460 INFO 20676 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with
[org.springframework.security.web.session.DisableEncodeUrlFilter@6b4a4e40,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@46a8c2b4,
org.springframework.security.web.context.SecurityContextPersistenceFilter@640d604,
org.springframework.security.web.header.HeaderWriterFilter@7b96de8d,
org.springframework.security.web.csrf.CsrfFilter@2a0b901c,
org.springframework.security.web.authentication.logout.LogoutFilter@38ac8968,
org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter@7739aac4,
org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter@36c07c75,
org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter@353c6da1,
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@7e61e25c,
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@4f664bee,
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter@21b51e59,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5438fa43,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@512abf25,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@76563ae7,
org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter@3e14d390,
org.springframework.security.web.session.SessionManagementFilter@4dc52559,
org.springframework.security.web.access.ExceptionTranslationFilter@51ac12ac,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@2407a36c]
Using environment variables for passing secrets to containerized applications is now considered bad practice because the environment can be inspected or logged in a number of cases. So, let’s move on to using Spring Cloud Config server for secrets storage.
Top comments (0)