Samy is my Hero and Hacking the Magic of Spring Boot (2023)

Samy is my Hero

A few months ago Tim Ferriss interviewed Samy Kamkar on his podcast. Samy’s large claim to fame is being the author of the MySpace Samy worm. This is a worm that infected over a million MySpace accounts in just 20 hours. MySpace actually shut down because of the worm.

Hearing Samy’s version of the story is absolutely hilarious. Samy is a hacker. He loves to see how things work. Samy tells Tim that he didn’t set out to create the fastest spreading virus of all time. He saw an exploit in the MySpace code that would allow him to add Javascript code to his profile to add the string “but most of all, Samy is my Hero” to anyone’s MySpace profile that visitedhis MySpace page, and have them add Samy as their friend.

But Samy was bored with that. He wanted more friends on MySpace. Through his hacking skills, he found a way to add that same script to anyone’s MySpace page that visited his MySpace page. Now anyone who visited someone who had been to Samy’s MySpace page was infected. Anyone visiting an infected MySpace profile would add Samy as their friend on MySpace, add “but most of all, Samy is my Hero” to their profile, and they would also be infected with the Samy worm.

The results were exponential. 5, 10, 30, 80, 1,000, 5,000, 10,000, etc. Every time Samy refreshed his MySpace page he had more friends, and the rate was growing. Before MySpace crashed, I think Samy said the rate was 10’s of thousands – per second!

While I think the exploit was hilarious and relatively harmless, the government did not. Eight months later Samy was raided by the US Secret Service and he was charged with crimes under the Patriot Act. Samy’s punishment was for 3 years, hewas not allowed to use a computer.

Since then Samy has continued hacking. But in a good way. He’s more of white hat hacker. Samy is also the author of Evercookie – an impossible to delete cookie for tracking internet users. A technology that the NSA is a fan of. Both of these exploits drove awareness and changes. The Samy Worm was a XSS exploit, which is now common to defend against. And Evercookie drove privacy changes in all major browsers.

I loveSamy’s passion for hacking. Today he’s hacking keyless car FOBs and consumer drones. The more expensive the car, the easier it is to hack. And did you know you can take over someone else’s drone?

Hacking Spring Boot Autoconfiguration

All programmers are hackers to some degree. We love to figure out how stuff works. So, whenever I start hacking something, often I start thinking about Samy is my hero.

(Video) I Hacked Into My Own Car

This week I’ve been hacking the Spring Boot autoconfiguration. I developed a Spring Boot web application for my Spring Core course, and in my Spring Core Advanced course, I’m un-doing all the magic of Spring Boot autoconfiguration. I’ve been spending hours going through the Spring Boot autoconfiguration code developed by the Spring Boot team:Phillip Webb, Dave Syer, Josh Long, Stéphane Nicoll, Rob Winch, Andy Wilkinson, Marcel Overdijk, Christian Dupuis, Sébastien Deleuze.

The Spring Boot documentation is fairly decent about explaining what is being autoconfigured at a high level. But the documentation does not get down to specifics. My goal is to un-do all the Spring Boot autoconfiguration magic. Ultimately I’m going to remove Spring Boot entirely from my project. It’s not because I don’t like Spring Boot. I’m a total Spring Boot fanboy. Spring Boot is the most exciting thing to happen to Spring since Java annotations. (Really, who misses the XML hell of configuring a Spring / Hibernate project??? Anyone? Buller? Buller?)

No, I’m going through this exercise to show my students the “old” days of Spring application development. To be honest, I’m also gaining a better appreciation for all the things Spring Boot is doing for us. I’ve been using Spring Boot long enough that I was blissfullyforgetting what Spring application development was like before Spring Boot.

But not everyone is lucky as me. I know there are many Spring developers out there wishing they could be using Spring Boot. And a fair amount that are scared of Spring Boot.

And if you’re a Spring developer wondering what Spring Boot is –

After peeking under the covers of Spring Boot Autoconfiguration, I do have to give kudos to the Spring development team. They’ve been doing a real nice job. Overall, I’m impressed. There is a lot going on with Spring Boot autoconfiguration. There is a lot of conditional stuff happening. Much of it isn’t trivial either. Autoconfiguration of Hibernate, popular databases, and Spring Security? Yep, it’s in there.

Much of the Spring Boot is conditional. It only kicks in when the appropriate jars are on your classpath. And typically, key properties can be easily overridden via properties files.

(Video) Testing the *CREEPY* TALKING TOM App for the FIRST time!

I thought I’d share a bit of what I’ve found in my hacking adventures with Spring Boot. After all, Samy is my hero.

Spring Boot Autoconfiguration Classes

To my knowledge all the Spring Boot Autoconfiguration classes are in a single jar. Below is the Maven dependency for Spring Boot Autoconfiguration. Don’t worry, this jar is automatically included as a dependency of Spring Boot. I’m just pointing it out so you can easily hack it with your tool of choice. (IntelliJ for me)

spring-boot-autoconfigure

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>1.3.1.RELEASE</version></dependency>

Inside this jar is a collection of Spring Java configuration classes. These are the classes behind the autoconfiguration in Spring Boot.

Key Spring Boot Autoconfiguration Annotations

@ConditionalOnClass

There’s a Java annotation in the Spring configuration which I was not familiar with called @ConditionalOnClass. In a nutshell this is what will kick in the Spring Boot autoconfiguration. If the specified classes are found, do then do the auto configuration.

@ConditionalOnProperty

This is an annotation to specify properties. If you remember, Spring Boot Autoconfiguration allows you to override properties via Spring Boot properties files. Through this annotation, if a property has not been set in the environment, one can be specified.

@ConditionalOnMissingBean

In Spring Boot, you can supply a bean through normal Spring configuration. I have an example of this in my post on Configuring Spring Boot for Oracle. In this post I show you how to override the Spring Boot data source by just properties, or by creating a DataSource bean in a Spring Java configuration class. First is by properties, ie @ConditionalOnProperty. Second is by bean type, ie @ConditionalOnMissingBean.

With the @ConditionalOnMissingBean annotation, the configuration option will only kick in if the bean is not already contained in the Spring bean factory.

Hacking the Spring Boot Autoconfiguration for Thymeleaf

Spring Boot Default

As an example, lets take a look at hacking the Thymeleaf autoconfiguration of Spring Boot.

(Video) Extreme Try Not To Laugh Challenge!

Here we can see the above Spring Boot autoconfiguration annotations in use as it applies to the autoconfiguration of Thymeleaf.

ThymeleafAutoConfiguration.class

As of version 1.3.1 of Spring Boot.

/** * {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf. * * @author Dave Syer * @author Andy Wilkinson * @author Stephane Nicoll * @author Brian Clozel */@Configuration@EnableConfigurationProperties(ThymeleafProperties.class)@ConditionalOnClass(SpringTemplateEngine.class)@AutoConfigureAfter(WebMvcAutoConfiguration.class)public class ThymeleafAutoConfiguration {private static final Log logger = LogFactory.getLog(ThymeleafAutoConfiguration.class);@Configuration@ConditionalOnMissingBean(name = "defaultTemplateResolver")public static class DefaultTemplateResolverConfiguration {@Autowiredprivate ThymeleafProperties properties;@Autowiredprivate ApplicationContext applicationContext;@PostConstructpublic void checkTemplateLocationExists() {boolean checkTemplateLocation = this.properties.isCheckTemplateLocation();if (checkTemplateLocation) {TemplateLocation location = new TemplateLocation(this.properties.getPrefix());if (!location.exists(this.applicationContext)) {logger.warn("Cannot find template location: " + location+ " (please add some templates or check "+ "your Thymeleaf configuration)");}}}@Beanpublic TemplateResolver defaultTemplateResolver() {TemplateResolver resolver = new TemplateResolver();resolver.setResourceResolver(thymeleafResourceResolver());resolver.setPrefix(this.properties.getPrefix());resolver.setSuffix(this.properties.getSuffix());resolver.setTemplateMode(this.properties.getMode());if (this.properties.getEncoding() != null) {resolver.setCharacterEncoding(this.properties.getEncoding().name());}resolver.setCacheable(this.properties.isCache());Integer order = this.properties.getTemplateResolverOrder();if (order != null) {resolver.setOrder(order);}return resolver;}@Beanpublic SpringResourceResourceResolver thymeleafResourceResolver() {return new SpringResourceResourceResolver();}}@Configuration@ConditionalOnMissingBean(SpringTemplateEngine.class)protected static class ThymeleafDefaultConfiguration {@Autowiredprivate final Collection<ITemplateResolver> templateResolvers = Collections.emptySet();@Autowired(required = false)private final Collection<IDialect> dialects = Collections.emptySet();@Beanpublic SpringTemplateEngine templateEngine() {SpringTemplateEngine engine = new SpringTemplateEngine();for (ITemplateResolver templateResolver : this.templateResolvers) {engine.addTemplateResolver(templateResolver);}for (IDialect dialect : this.dialects) {engine.addDialect(dialect);}return engine;}}@Configuration@ConditionalOnClass(name = "nz.net.ultraq.thymeleaf.LayoutDialect")protected static class ThymeleafWebLayoutConfiguration {@Beanpublic LayoutDialect layoutDialect() {return new LayoutDialect();}}@Configuration@ConditionalOnClass(DataAttributeDialect.class)protected static class DataAttributeDialectConfiguration {@Bean@ConditionalOnMissingBeanpublic DataAttributeDialect dialect() {return new DataAttributeDialect();}}@Configuration@ConditionalOnClass({ SpringSecurityDialect.class })protected static class ThymeleafSecurityDialectConfiguration {@Bean@ConditionalOnMissingBeanpublic SpringSecurityDialect securityDialect() {return new SpringSecurityDialect();}}@Configuration@ConditionalOnClass(ConditionalCommentsDialect.class)protected static class ThymeleafConditionalCommentsDialectConfiguration {@Bean@ConditionalOnMissingBeanpublic ConditionalCommentsDialect conditionalCommentsDialect() {return new ConditionalCommentsDialect();}}@Configuration@ConditionalOnClass({ Servlet.class })@ConditionalOnWebApplicationprotected static class ThymeleafViewResolverConfiguration {@Autowiredprivate ThymeleafProperties properties;@Autowiredprivate SpringTemplateEngine templateEngine;@Bean@ConditionalOnMissingBean(name = "thymeleafViewResolver")@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)public ThymeleafViewResolver thymeleafViewResolver() {ThymeleafViewResolver resolver = new ThymeleafViewResolver();resolver.setTemplateEngine(this.templateEngine);resolver.setCharacterEncoding(this.properties.getEncoding().name());resolver.setContentType(appendCharset(this.properties.getContentType(),resolver.getCharacterEncoding()));resolver.setExcludedViewNames(this.properties.getExcludedViewNames());resolver.setViewNames(this.properties.getViewNames());// This resolver acts as a fallback resolver (e.g. like a// InternalResourceViewResolver) so it needs to have low precedenceresolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);return resolver;}private String appendCharset(MimeType type, String charset) {if (type.getCharSet() != null) {return type.toString();}LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();parameters.put("charset", charset);parameters.putAll(type.getParameters());return new MimeType(type, parameters).toString();}}@Configuration@ConditionalOnWebApplicationprotected static class ThymeleafResourceHandlingConfig {@Bean@ConditionalOnMissingBean@ConditionalOnEnabledResourceChainpublic ResourceUrlEncodingFilter resourceUrlEncodingFilter() {return new ResourceUrlEncodingFilter();}}}
/** * {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf. * * @author Dave Syer * @author Andy Wilkinson * @author Stephane Nicoll * @author Brian Clozel */@Configuration@EnableConfigurationProperties(ThymeleafProperties.class)@ConditionalOnClass(SpringTemplateEngine.class)@AutoConfigureAfter(WebMvcAutoConfiguration.class)public class ThymeleafAutoConfiguration {private static final Log logger = LogFactory.getLog(ThymeleafAutoConfiguration.class);@Configuration@ConditionalOnMissingBean(name = "defaultTemplateResolver")public static class DefaultTemplateResolverConfiguration {@Autowiredprivate ThymeleafProperties properties;@Autowiredprivate ApplicationContext applicationContext;@PostConstructpublic void checkTemplateLocationExists() {boolean checkTemplateLocation = this.properties.isCheckTemplateLocation();if (checkTemplateLocation) {TemplateLocation location = new TemplateLocation(this.properties.getPrefix());if (!location.exists(this.applicationContext)) {logger.warn("Cannot find template location: " + location+ " (please add some templates or check "+ "your Thymeleaf configuration)");}}}@Beanpublic TemplateResolver defaultTemplateResolver() {TemplateResolver resolver = new TemplateResolver();resolver.setResourceResolver(thymeleafResourceResolver());resolver.setPrefix(this.properties.getPrefix());resolver.setSuffix(this.properties.getSuffix());resolver.setTemplateMode(this.properties.getMode());if (this.properties.getEncoding() != null) {resolver.setCharacterEncoding(this.properties.getEncoding().name());}resolver.setCacheable(this.properties.isCache());Integer order = this.properties.getTemplateResolverOrder();if (order != null) {resolver.setOrder(order);}return resolver;}@Beanpublic SpringResourceResourceResolver thymeleafResourceResolver() {return new SpringResourceResourceResolver();}}@Configuration@ConditionalOnMissingBean(SpringTemplateEngine.class)protected static class ThymeleafDefaultConfiguration {@Autowiredprivate final Collection<ITemplateResolver> templateResolvers = Collections.emptySet();@Autowired(required = false)private final Collection<IDialect> dialects = Collections.emptySet();@Beanpublic SpringTemplateEngine templateEngine() {SpringTemplateEngine engine = new SpringTemplateEngine();for (ITemplateResolver templateResolver : this.templateResolvers) {engine.addTemplateResolver(templateResolver);}for (IDialect dialect : this.dialects) {engine.addDialect(dialect);}return engine;}}@Configuration@ConditionalOnClass(name = "nz.net.ultraq.thymeleaf.LayoutDialect")protected static class ThymeleafWebLayoutConfiguration {@Beanpublic LayoutDialect layoutDialect() {return new LayoutDialect();}}@Configuration@ConditionalOnClass(DataAttributeDialect.class)protected static class DataAttributeDialectConfiguration {@Bean@ConditionalOnMissingBeanpublic DataAttributeDialect dialect() {return new DataAttributeDialect();}}@Configuration@ConditionalOnClass({ SpringSecurityDialect.class })protected static class ThymeleafSecurityDialectConfiguration {@Bean@ConditionalOnMissingBeanpublic SpringSecurityDialect securityDialect() {return new SpringSecurityDialect();}}@Configuration@ConditionalOnClass(ConditionalCommentsDialect.class)protected static class ThymeleafConditionalCommentsDialectConfiguration {@Bean@ConditionalOnMissingBeanpublic ConditionalCommentsDialect conditionalCommentsDialect() {return new ConditionalCommentsDialect();}}@Configuration@ConditionalOnClass({ Servlet.class })@ConditionalOnWebApplicationprotected static class ThymeleafViewResolverConfiguration {@Autowiredprivate ThymeleafProperties properties;@Autowiredprivate SpringTemplateEngine templateEngine;@Bean@ConditionalOnMissingBean(name = "thymeleafViewResolver")@ConditionalOnProperty(name = "spring.thymeleaf.enabled", matchIfMissing = true)public ThymeleafViewResolver thymeleafViewResolver() {ThymeleafViewResolver resolver = new ThymeleafViewResolver();resolver.setTemplateEngine(this.templateEngine);resolver.setCharacterEncoding(this.properties.getEncoding().name());resolver.setContentType(appendCharset(this.properties.getContentType(),resolver.getCharacterEncoding()));resolver.setExcludedViewNames(this.properties.getExcludedViewNames());resolver.setViewNames(this.properties.getViewNames());// This resolver acts as a fallback resolver (e.g. like a// InternalResourceViewResolver) so it needs to have low precedenceresolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);return resolver;}private String appendCharset(MimeType type, String charset) {if (type.getCharSet() != null) {return type.toString();}LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();parameters.put("charset", charset);parameters.putAll(type.getParameters());return new MimeType(type, parameters).toString();}}@Configuration@ConditionalOnWebApplicationprotected static class ThymeleafResourceHandlingConfig {@Bean@ConditionalOnMissingBean@ConditionalOnEnabledResourceChainpublic ResourceUrlEncodingFilter resourceUrlEncodingFilter() {return new ResourceUrlEncodingFilter();}}}

Overriding Spring Boot

Now, here’s the implementation I did for my Spring Core Advanced class.

@Configurationpublic class ThymeleafConfig { @Bean public TemplateResolver defaultTemplateResolver() { TemplateResolver resolver = new TemplateResolver(); resolver.setResourceResolver(thymeleafResourceResolver()); resolver.setPrefix("classpath:/templates/"); resolver.setSuffix(".html"); resolver.setTemplateMode("HTML5"); resolver.setCharacterEncoding("UTF-8"); resolver.setCacheable(true); return resolver; } @Bean public SpringResourceResourceResolver thymeleafResourceResolver() { return new SpringResourceResourceResolver(); } @Bean public SpringTemplateEngine templateEngine(TemplateResolver defaultTemplateResolver) { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.addTemplateResolver(defaultTemplateResolver); return engine; } @Bean public ThymeleafViewResolver thymeleafViewResolver(SpringTemplateEngine templateEngine) { ThymeleafViewResolver resolver = new ThymeleafViewResolver(); resolver.setTemplateEngine(templateEngine); resolver.setCharacterEncoding("UTF-8"); resolver.setContentType("text/html"); resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5); return resolver; } @Bean public LayoutDialect layoutDialect() { return new LayoutDialect(); }}

Now, don’t get on me about hard coding properties. I know, BAD DEVELOPER! My students haven’t learned about externalizing properties – yet.

In a nutshell, I’ve provided the Thymeleaf objects needed to configure Thymeleaf for use with Spring MVC. In doing so, the Spring Boot autoconfiguration won’t kick in (due to the @ConditionalOnMissingBean in the Spring Boot default autoconfiguration class).

Spring Boot autoconfiguration is a very cool feature of Spring Boot. As Spring developers, it saves us ton of time configuring our Spring projects. But the Spring Boot autoconfiguration is a double edge sword. Through sensible defaults, come defaults. Which open the door to hacking. I remember in the early days of Oracle, every Oracle database came with the account SCOTT, with the password TIGER. You also had the equivalent of a root account (aka god account) of SYSTEM, default password manager. Production Oracle databases were getting hacked, because someone forgot to change the password of SYSTEM from ‘manager’.

Spring Boot autoconfiguration is saving us Spring developers a TON of time. But don’t use that as an excuse to be lazy. Put your hacker hat on. Take a peek at what Spring Boot autoconfiguration is doing for you. Get familiar with it. Spring Boot should not be magical. Spring Boot should not be a black box. This is exactly why I’m going through the exercise of removing Spring Boot from a project for my students. I feel they will be better Spring developers if Spring Boot is not a mystery to them.

I encourage you to hack the Spring Boot autoconfiguration. And when you do,say to yourself –

(Video) I Spent $10,000 To Go To SPACE!

“but most of all, Samy is my hero”

Free Introduction to Spring Tutorial

Are you new to the Spring Framework? Checkout my Free Introduction to Spring Online Tutorial.

Learn More

(Video) The Ultimate LinkedIn Sales Guide with Daniel Disney | Salesman Podcast

Videos

1. LIVE Snowboard Buying Advice with TJ - Thoughts on Snowboard Flex
(SnowboardProCamp)
2. Breathtaking makeover for your favorite doll
(SLICK SLIME SAM - DIY, Comedy, Science)
3. POPPY PLAYTIME CHAPTER 3 *ALL GRABPACKS* REVEALED!? (NEW CHAPTER 3 GAME PLAY!)
(LankyBox)
4. FIVE NIGHTS AT FREDDY'S SECURITY BREACH! *FULL GAME* (COMPLETE LANKYBOX WALKTHROUGH + ALL ENDINGS)
(LankyBox)
5. This video will make you forget your name..
(Top10Speed)
6. My GIRLFRIEND Helps Me CHEAT In SLITHER.IO!
(Jelly)
Top Articles
Latest Posts
Article information

Author: Errol Quitzon

Last Updated: 01/25/2023

Views: 6452

Rating: 4.9 / 5 (79 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Errol Quitzon

Birthday: 1993-04-02

Address: 70604 Haley Lane, Port Weldonside, TN 99233-0942

Phone: +9665282866296

Job: Product Retail Agent

Hobby: Computer programming, Horseback riding, Hooping, Dance, Ice skating, Backpacking, Rafting

Introduction: My name is Errol Quitzon, I am a fair, cute, fancy, clean, attractive, sparkling, kind person who loves writing and wants to share my knowledge and understanding with you.