jasypt-spring-boot
jasypt-spring-boot copied to clipboard
Starting with Springboot 3.5 properties from environment variables are not decrypted any more.
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:-}intoapplication.propertiesdoes not work. - Putting
foo.bar=${some_bar:-}intoapplication.properties(so some totally different name to resolve the value from) and starting it withSOME_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?
We noticed the same problem in the project. Request to restore the previous behavior. thanks in advance
I'll take a look but have you tried using the encryptable environment flavor?
new SpringApplicationBuilder()
.environment(new StandardEncryptableEnvironment())
.sources(YourApplicationClass.class).run(args);
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
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
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)
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
We noticed the same problem in the project.
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.
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 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
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.
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 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.
alright, sounds good I'll drop support for < 17. Thanks!
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
@ulisesbocchio Thanks for working on Spring Boot 3.5 support. Could you already provide an estimation when this becomes available?
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.
@ulisesbocchio thanks for all the support, really appreciate it. Any update on spring boot 3.5 support? When this be available?
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.
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