jasypt-spring-boot icon indicating copy to clipboard operation
jasypt-spring-boot copied to clipboard

Starting with Springboot 3.5 properties from environment variables are not decrypted any more.

Open mwiede opened this issue 6 months ago • 3 comments

I have the following app and the goal is to steer the property foo.bar as environment variable.

@SpringBootApplication
public class DemoApplication {

	@Value("${foo.bar}")
	String fooBar;

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

	@PostConstruct
	void init() {
		System.out.println("Application started successfully!");
		System.out.println("Value of foo.bar: " + fooBar);
	}

}

Running this app with environment variables

FOO_BAR=ENC(5hgCWCvteVEph0fQg0/WOkE+mu0NLEKAT8XaZxb8Np75NwqC64zeTe9TrCiLAuEhup2n1ZKAYASZscAdg6dgow\=\=);JASYPT_ENCRYPTOR_PASSWORD=the password and Spring-Boot Classes of Version 3.5.0 will output:

Application started successfully!
Value of foo.bar: ENC(5hgCWCvteVEph0fQg0/WOkE+mu0NLEKAT8XaZxb8Np75NwqC64zeTe9TrCiLAuEhup2n1ZKAYASZscAdg6dgow==)

so the property was not decrypted.

Putting foo.bar=ENC(5hgCWCvteVEph0fQg0/WOkE+mu0NLEKAT8XaZxb8Np75NwqC64zeTe9TrCiLAuEhup2n1ZKAYASZscAdg6dgow\=\=) into the application.properties file does work and shows:

Application started successfully!
Value of foo.bar: theValueYouWantToEncrypt

Switching back to Spring Boot 3.4.6 also works by only giving environment variables.

Workaround: Also tried the following as workaround with Spring-Boot 3.5:

  • Putting foo.bar=${foo_bar:-} into application.properties does not work.
  • Putting foo.bar=${some_bar:-} into application.properties (so some totally different name to resolve the value from) and starting it with SOME_BAR=ENC(5hgCWCvteVEph0fQg0/WOkE+mu0NLEKAT8XaZxb8Np75NwqC64zeTe9TrCiLAuEhup2n1ZKAYASZscAdg6dgow\=\=)does work.

Alternative Spring Boot 3.5 introduces loading properties from environment variables (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.5-Release-Notes#load-properties-from-environment-variables) and also this setup from the example works:

application.properties:
spring.config.import=env:MY_CONFIGURATION

Starting my app with: JASYPT_ENCRYPTOR_PASSWORD=the password;MY_CONFIGURATION=foo.bar=ENC(5hgCWCvteVEph0fQg0/WOkE+mu0NLEKAT8XaZxb8Np75NwqC64zeTe9TrCiLAuEhup2n1ZKAYASZscAdg6dgow\=\=)

So, there is a breaking change and I am wondering, whether the previous behavior can be restored?

mwiede avatar Jun 05 '25 09:06 mwiede

We noticed the same problem in the project. Request to restore the previous behavior. thanks in advance

grzegorzekkk avatar Jun 09 '25 11:06 grzegorzekkk

I'll take a look but have you tried using the encryptable environment flavor?

new SpringApplicationBuilder()
    .environment(new StandardEncryptableEnvironment())
    .sources(YourApplicationClass.class).run(args);

ulisesbocchio avatar Jun 09 '25 15:06 ulisesbocchio

I have also encountered this issue - a project using system environment variables whose values are encrypted to set properties.

We used the environment variable names in the property files like foo.bar=${foo_bar} and found that spring boot 3.5.0 no longer decrypted them.

I believe spring boot always had the ability to automatically bind system environment variables with a quite high priority and 3.5.0 made a change that likely changed the order or an underlying class that now avoids the decryption - we encountered issues when we removed a property from the application.properties file but still found the property was being set because the environment variable still existed.

A workaround I used was starting the application by setting the environment prefix which then pushes the environment variable names the automatic system environment variable binding to one side i.e. spring boot now looks for anystring_foo_bar when trying to automatically bind the environment variables and foo.bar=${foo_bar} now gets decrypted as expected.

SpringApplication myapp = new SpringApplication(MyAppClass.class);
myapp.setEnvironmentPrefix("anystring");
myapp.run(args);

Obviously this work around won't be appropriate if your application uses the environment variable binding - see https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.files.env-variables and https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.system-environment

dean-mcnabb avatar Jun 14 '25 21:06 dean-mcnabb

I'll take a look but have you tried using the encryptable environment flavor?

new SpringApplicationBuilder() .environment(new StandardEncryptableEnvironment()) .sources(YourApplicationClass.class).run(args);

it does not work

mwiede avatar Jun 23 '25 08:06 mwiede

I'll take a look but have you tried using the encryptable environment flavor?

new SpringApplicationBuilder() .environment(new StandardEncryptableEnvironment()) .sources(YourApplicationClass.class).run(args);

it does not work, please take a look)

taalexlistex avatar Jun 23 '25 12:06 taalexlistex

we are also facing the same issue where environment variables are not getting decrypted with latest spring boot version 3.5.0 and we tried with 3.5.3 as well and issue is still persisting. It seems like env variable are not getting loaded early and hence decryption is not happening.

By setting the properties in SPRING_APPLICATION_JSON in env variables, then it works because spring application json is loaded early. But this is just workaround and not possible to implement to all multiple services.

Kindly update if there is any solution fix worked upon or we have to follow the workaround options so that accordingly it will help us to take the required decisions

saran53rithu avatar Jul 01 '25 10:07 saran53rithu

We noticed the same problem in the project.

moonmm avatar Jul 09 '25 09:07 moonmm

I’ve traced some Spring source code related to this issue, and I think the problem lies in SpringConfigurationPropertySource#getConfigurationProperty.

In versions prior to 3.4.x, this method would retrieve the value from the underlying PropertySource (which is EncryptableSystemEnvironmentPropertySourceWrapper).

However, starting from 3.5.x, Spring add a cache specifically for system environment, which results in the value being returned without going through the decryption wrapper.

As a workaround, I’m currently wrapping the system environment property source in a different class to bypass Spring’s internal cache:

var environment = SpringApplication.run(DemoApplication.class, args).getEnvironment();
var ps =
  environment
    .getPropertySources()
    .get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
var copy = new CompositePropertySource(ps.getName());
copy.addPropertySource(ps);
environment.getPropertySources().replace(copy.getName(), copy);

If your encrypted variables are accessed before the application is fully ready (e.g., during database connection initialization), you’ll need to apply this workaround earlier in the lifecycle. such as in a BeanFactoryPostProcessor, but make sure it runs after EnableEncryptablePropertiesBeanFactoryPostProcessor.

shihyuho avatar Jul 09 '25 13:07 shihyuho

We have the same problem and we are using the StandardEncryptableEnvironment.

There was a performance optimization (https://github.com/spring-projects/spring-boot/issues/44862) so that now the getProperty method of the EncryptableSystemEnvironmentPropertySource is no longer called and the underlying source map is used directly.

The workaround does not work for us. Wrapping the SystemEnvironmentPropertySource in a CompositePropertySource prevents the property source from being recognized as the systemEnvironment (see SpringConfigurationPropertySource.isSystemEnvironmentPropertySource). So the optimization is not used and the properties are decrypted again. But that leads to other unintended problems, for example other environment variables are no longer bound correctly. The reason for that seems to be that the SystemEnvironmentPropertyMapper is not added as a direct result of the property source not being recognized as systemEnvironment.

kheckelmann avatar Jul 17 '25 07:07 kheckelmann

@kheckelmann thanks for pointing out this issue. I did some further investigation and refined the original workaround. A better approach, in my opinion, is to follow the concept used in Configuring Random Values, as it avoids potential conflicts with other Spring features. However, this requires a small change to how system environment variables are defined:

Before:

MY_SECRET=ENC(.....)

After:

MY_SECRET=${ENC(.....)}

If you’re okay with this tweak, you can find examples and test cases here: shihyuho/jasypt-spring-boot-3.5.x-systemenv-workaround

shihyuho avatar Jul 24 '25 10:07 shihyuho

My approach is a bit different, because I found that SpringConfigurationPropertySource skips the actual implementation of SystemEnvironmentPropertySource and its subclasses, and directly uses the embedded Map<String, Object> returned by getSource(). So, my idea is to first create a read-only Map backed by a PropertySource and pass it in as the source of EncryptableSystemEnvironmentPropertySource. This way, when SpringConfigurationPropertySource calls getSource(), what it gets is a Map that automatically decrypts values. Then, I just write a BeanFactoryPostProcessor to replace the original EncryptableSystemEnvironmentPropertySource provided by Jasypt.

You can find my implementation of PropertySourceMap and EncryptableSystemEnvironmentPropertySource here.

moonfruit avatar Jul 25 '25 03:07 moonfruit

Thank you guys for the details. I've been working on upgrading the Library to Spring boot 3.5 but it's becoming a large task. I'm upgrading all the libraries, maven config and considering dropping support for JDK < 21 unless you guys need anything lower than that, let me know.

ulisesbocchio avatar Jul 26 '25 00:07 ulisesbocchio

@ulisesbocchio Spring Boot 3.x has Java 17 as its baseline, and our team usually picks Java versions based on what Spring supports. We still have some apps running on 17, so it’d be great if the library could stay aligned with Spring’s Java support.

shihyuho avatar Jul 26 '25 01:07 shihyuho

alright, sounds good I'll drop support for < 17. Thanks!

ulisesbocchio avatar Jul 28 '25 19:07 ulisesbocchio

Spring Boot 4.X apprears to also target Java 17, so 17 seems reasonable. For adoptium it is available At least Oct 2027 (https://adoptium.net/support). Personally Id go for 21 while you are at, but SB4 still targeting 17 sounds clear enough as a direction for the Java community

DRoppelt avatar Jul 30 '25 09:07 DRoppelt

@ulisesbocchio Thanks for working on Spring Boot 3.5 support. Could you already provide an estimation when this becomes available?

tobias-lippert avatar Jul 31 '25 07:07 tobias-lippert

A heartfelt thank you to the open-source authors for your dedication and generosity. Your contributions not only provide invaluable tools and knowledge but also inspire a spirit of collaboration and innovation across the community.

kotenarui-maker avatar Sep 02 '25 05:09 kotenarui-maker

@ulisesbocchio thanks for all the support, really appreciate it. Any update on spring boot 3.5 support? When this be available?

rsinghal57 avatar Sep 22 '25 11:09 rsinghal57

Yes, sorry it's taking this long. I don't have much spare time but I have a working branch spring35 with all the upgrades and samples working. Now I have to look into this issue on that codebase to see if it still comes up and needs further changes.

ulibocchio avatar Sep 23 '25 16:09 ulibocchio

Quick update: I found the specific issue affecting this feature. Turns out Spring boot, only for the System Environment is unwrapping the propertiesSource, pulling the underlying map and looking up the values directly from there. I don't have a solution yet, but hopefully soon. Here's the link to Spring's source where this is happening: https://github.com/spring-projects/spring-boot/blob/main/core/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringConfigurationPropertySource.java#L106-L112

ulibocchio avatar Nov 06 '25 16:11 ulibocchio