additionalInfo) {}
35 |
--------------------------------------------------------------------------------
/bootstrap-keycloak.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Application Security for Java Developers
4 | #
5 | # Copyright (C) 2025 Ionut Balosin
6 | # Website: www.ionutbalosin.com
7 | # Social Media:
8 | # LinkedIn: ionutbalosin
9 | # Bluesky: @ionutbalosin.bsky.social
10 | # X: @ionutbalosin
11 | # Mastodon: ionutbalosin@mastodon.social
12 | #
13 | # Licensed to the Apache Software Foundation (ASF) under one
14 | # or more contributor license agreements. See the NOTICE file
15 | # distributed with this work for additional information
16 | # regarding copyright ownership. The ASF licenses this file
17 | # to you under the Apache License, Version 2.0 (the
18 | # "License"); you may not use this file except in compliance
19 | # with the License. You may obtain a copy of the License at
20 | #
21 | # http://www.apache.org/licenses/LICENSE-2.0
22 | #
23 | # Unless required by applicable law or agreed to in writing,
24 | # software distributed under the License is distributed on an
25 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26 | # KIND, either express or implied. See the License for the
27 | # specific language governing permissions and limitations
28 | # under the License.
29 | #
30 |
31 | echo ""
32 | echo "******************************"
33 | echo "* Start Keycloak with Docker *"
34 | echo "******************************"
35 | echo ""
36 |
37 | docker compose -f ./docker-compose-keycloak.yml \
38 | up -d
39 | if [ $? -ne 0 ]; then
40 | echo "Error: Docker services failed to start."
41 | exit 1
42 | fi
43 |
44 | echo ""
45 | echo "🎉 Congratulations! Everything was successful."
--------------------------------------------------------------------------------
/bootstrap-webgoat.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Application Security for Java Developers
4 | #
5 | # Copyright (C) 2025 Ionut Balosin
6 | # Website: www.ionutbalosin.com
7 | # Social Media:
8 | # LinkedIn: ionutbalosin
9 | # Bluesky: @ionutbalosin.bsky.social
10 | # X: @ionutbalosin
11 | # Mastodon: ionutbalosin@mastodon.social
12 | #
13 | # Licensed to the Apache Software Foundation (ASF) under one
14 | # or more contributor license agreements. See the NOTICE file
15 | # distributed with this work for additional information
16 | # regarding copyright ownership. The ASF licenses this file
17 | # to you under the Apache License, Version 2.0 (the
18 | # "License"); you may not use this file except in compliance
19 | # with the License. You may obtain a copy of the License at
20 | #
21 | # http://www.apache.org/licenses/LICENSE-2.0
22 | #
23 | # Unless required by applicable law or agreed to in writing,
24 | # software distributed under the License is distributed on an
25 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26 | # KIND, either express or implied. See the License for the
27 | # specific language governing permissions and limitations
28 | # under the License.
29 | #
30 |
31 | echo ""
32 | echo "***********************************"
33 | echo "* Start OWASP WebGoat with Docker *"
34 | echo "***********************************"
35 | echo ""
36 |
37 | docker compose -f ./docker-compose-webgoat.yml \
38 | up -d
39 | if [ $? -ne 0 ]; then
40 | echo "Error: Docker services failed to start."
41 | exit 1
42 | fi
43 |
44 | echo ""
45 | echo "🎉 Congratulations! Everything was successful."
--------------------------------------------------------------------------------
/docker-compose-pizza-application.yml:
--------------------------------------------------------------------------------
1 | version: '3.5'
2 | services:
3 | #---------------------#
4 | # Pizza Order Service #
5 | #---------------------#
6 | pizza-order-service:
7 | container_name: pizza-order-service.local
8 | image: pizza-order-service:local
9 | ports:
10 | - 18080:8080
11 | - 18000:8000
12 | networks:
13 | - security-practices-network
14 | environment:
15 | - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
16 | - SPRING_PROFILES_ACTIVE=dockerlocal
17 | #-----------------------#
18 | # Pizza Cooking Service #
19 | #-----------------------#
20 | pizza-cooking-service:
21 | container_name: pizza-cooking-service.local
22 | image: pizza-cooking-service:local
23 | ports:
24 | - 28080:8080
25 | - 28000:8000
26 | networks:
27 | - security-practices-network
28 | environment:
29 | - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
30 | - SPRING_PROFILES_ACTIVE=dockerlocal
31 | #------------------------#
32 | # Pizza Delivery Service #
33 | #------------------------#
34 | pizza-delivery-service:
35 | container_name: pizza-delivery-service.local
36 | image: pizza-delivery-service:local
37 | ports:
38 | - 38080:8080
39 | - 38000:8000
40 | networks:
41 | - security-practices-network
42 | environment:
43 | - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
44 | - SPRING_PROFILES_ACTIVE=dockerlocal
45 | networks:
46 | security-practices-network:
47 | name: security-practices
--------------------------------------------------------------------------------
/serialization-deserialization/src/main/java/ionutbalosin/training/application/security/practices/serialization/deserialization/clazz/TrustedClazz.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.serialization.deserialization.clazz;
30 |
31 | import java.io.Serializable;
32 |
33 | public class TrustedClazz implements Serializable {
34 |
35 | private static final long serialVersionUID = 2L;
36 |
37 | public TrustedClazz() {
38 | System.out.printf("A trusted class constructor has been invoked.%n");
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/Application.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service;
30 |
31 | import org.springframework.boot.SpringApplication;
32 | import org.springframework.boot.autoconfigure.SpringBootApplication;
33 | import org.springframework.cloud.openfeign.EnableFeignClients;
34 |
35 | @EnableFeignClients
36 | @SpringBootApplication
37 | public class Application {
38 |
39 | public static void main(String[] args) {
40 | SpringApplication.run(Application.class, args);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/pizza-delivery-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/delivery/service/Application.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.delivery.service;
30 |
31 | import org.springframework.boot.SpringApplication;
32 | import org.springframework.boot.autoconfigure.SpringBootApplication;
33 | import org.springframework.cloud.openfeign.EnableFeignClients;
34 |
35 | @EnableFeignClients
36 | @SpringBootApplication
37 | public class Application {
38 |
39 | public static void main(String[] args) {
40 | SpringApplication.run(Application.class, args);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/config/MapperConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service.config;
30 |
31 | import ionutbalosin.training.application.security.practices.pizza.order.service.mapper.PizzaCookingOrderDtoMapper;
32 | import org.springframework.context.annotation.Bean;
33 | import org.springframework.context.annotation.Configuration;
34 |
35 | @Configuration
36 | public class MapperConfig {
37 |
38 | @Bean
39 | public PizzaCookingOrderDtoMapper pizzaCookingOrderDtoMapper() {
40 | return new PizzaCookingOrderDtoMapper();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/zap-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Application Security for Java Developers
4 | #
5 | # Copyright (C) 2025 Ionut Balosin
6 | # Website: www.ionutbalosin.com
7 | # Social Media:
8 | # LinkedIn: ionutbalosin
9 | # Bluesky: @ionutbalosin.bsky.social
10 | # X: @ionutbalosin
11 | # Mastodon: ionutbalosin@mastodon.social
12 | #
13 | # Licensed to the Apache Software Foundation (ASF) under one
14 | # or more contributor license agreements. See the NOTICE file
15 | # distributed with this work for additional information
16 | # regarding copyright ownership. The ASF licenses this file
17 | # to you under the Apache License, Version 2.0 (the
18 | # "License"); you may not use this file except in compliance
19 | # with the License. You may obtain a copy of the License at
20 | #
21 | # http://www.apache.org/licenses/LICENSE-2.0
22 | #
23 | # Unless required by applicable law or agreed to in writing,
24 | # software distributed under the License is distributed on an
25 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26 | # KIND, either express or implied. See the License for the
27 | # specific language governing permissions and limitations
28 | # under the License.
29 | #
30 |
31 | # This script implicitly scans the 'pizza-order-service' microservice.
32 | # TODO: To scan a different microservice, please update the TARGET_URL below.
33 | TARGET_URL=${TARGET_URL:-http://pizza-order-service.local:8080/public/v3/api-docs/swagger-config}
34 |
35 | echo "🔍 Starting the ZAP API scan against target: [$TARGET_URL] ..."
36 | zap-api-scan.py -I -t $TARGET_URL -f openapi -r zap-report.html -c zap-api-scan-rules.conf
37 | SCAN_STATUS=$?
38 |
39 | echo "The ZAP API scan finished with status [$SCAN_STATUS]"
40 |
41 | echo "Copying ZAP API scan report to host machine under /zap/reports/"
42 | cp /zap/wrk/zap-report.html /zap/reports/
43 |
44 | exit $SCAN_STATUS
--------------------------------------------------------------------------------
/postman/Java-Application-Security-Practices.postman_environment.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "fd45e3f8-b2f8-4812-b87e-ac9cb44cf87c",
3 | "name": "Java-Application-Security-Practices",
4 | "values": [
5 | {
6 | "key": "token",
7 | "value": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJuaEJmUFZFOEl0U3hyY2tOOHoxSGpYajdkc3pFOEZkWC0wT2NKUWladTJrIn0.eyJleHAiOjE3MzEwODIzMjYsImlhdCI6MTczMTA4MTQyNiwianRpIjoiZjZkMGQwMTAtODkwZC00M2ZkLTllNTYtZjZkM2M3OWQzNTc2IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5MDkwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMDE5Y2IzZDMtYmM1ZC00ZWRkLTljMTEtYjg0NGQ0NTViNGVhIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZGVtb19wdWJsaWNfY2xpZW50Iiwic2lkIjoiYjllMmY1NDMtNzMwOS00NDQ1LTkyZWYtODg2ZjdmNGRlYjJiIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwczovL29hdXRoLnBzdG1uLmlvIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZW1vX3VzZXJfcm9sZSIsImRlZmF1bHQtcm9sZXMtbWFzdGVyIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYW1lIjoiZGVtb191c2VyIGZpcnN0TmFtZSBkZW1vX3VzZXIgbGFzdE5hbWUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJkZW1vX3VzZXIiLCJnaXZlbl9uYW1lIjoiZGVtb191c2VyIGZpcnN0TmFtZSIsImZhbWlseV9uYW1lIjoiZGVtb191c2VyIGxhc3ROYW1lIiwiZW1haWwiOiJkZW1vX3VzZXJAa2V5Y2xvYWsuY29tIn0.LJXeUjA2fgC3BJW5eJB2-4gdH-r7CAJRxJ-PtJ9XEA1oaLH3pE339mQITWGnq4nhCMtfWKwILH29J1knOy0X6Y_yGo1At6lkGkUGwOYhlxqmwoH2COYGnbh8oNqs3ck79JT7FD7IkRCnasB9cmKEHeJOB_JA5sOjGXiSUjV4JB15UIIhNTSGk9mEpDYToP2I0iTvqVkLPiHuMawJih6bJFB3q8KF5eU2i1kH_w0FBHYiLiI0WHdbcIQIbsQ5DmP4LDyjwXqxq8DWl7w6oNcUzOrd83R30wLheYYXqQs7o34wQUm3g8HrTWnqmy2kIB03uTU7vU9YrK6VGglFMvMNag",
8 | "type": "any",
9 | "enabled": true
10 | },
11 | {
12 | "key": "orderId",
13 | "value": "69cba318-52ad-4fa7-8178-7bbf023a76d4",
14 | "type": "any",
15 | "enabled": true
16 | }
17 | ],
18 | "_postman_variable_scope": "environment",
19 | "_postman_exported_at": "2024-11-08T16:01:08.505Z",
20 | "_postman_exported_using": "Postman/11.12.0"
21 | }
--------------------------------------------------------------------------------
/security-slf4j-logger-enricher/src/main/java/ionutbalosin/training/application/security/practices/slf4j/logger/enricher/LoggerInterceptorConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.slf4j.logger.enricher;
30 |
31 | import org.springframework.context.annotation.Configuration;
32 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
33 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
34 |
35 | @Configuration
36 | public class LoggerInterceptorConfig implements WebMvcConfigurer {
37 | private final LoggerInterceptor logInterceptor;
38 |
39 | public LoggerInterceptorConfig() {
40 | logInterceptor = new LoggerInterceptor();
41 | }
42 |
43 | @Override
44 | public void addInterceptors(InterceptorRegistry registry) {
45 | registry.addInterceptor(logInterceptor);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/resources/confidential_file.txt:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus suscipit augue ac efficitur iaculis. Nulla facilisi. Curabitur vel ultricies lorem. Etiam venenatis cursus velit non luctus. Integer auctor ac velit ac vulputate. Suspendisse consectetur erat at augue condimentum, at tempor purus condimentum.
2 |
3 | Morbi non facilisis eros. Mauris vulputate sapien ac metus faucibus, et dignissim metus euismod. Vivamus vitae leo orci. Aliquam erat volutpat. Phasellus vitae nisi ac dolor dictum posuere ut non risus. Nam vestibulum nulla id metus gravida, sit amet aliquam sem efficitur.
4 |
5 | Quisque fermentum sapien nec felis ultricies, eget fermentum elit gravida. Ut sed neque ac nulla tristique tempus sit amet sed nunc. Donec tempor posuere libero vel gravida. Suspendisse vel sollicitudin nunc. Integer facilisis orci ut lacus elementum maximus. Fusce cursus volutpat eros at dictum.
6 |
7 | In consequat, elit et auctor sodales, velit orci pretium magna, in rhoncus justo sem eu tortor. Fusce et dolor ut turpis fringilla pharetra. Nam lobortis augue vitae sapien aliquam, a rutrum sem fermentum. Nam non arcu vehicula, pretium eros non, tincidunt erat.
8 |
9 | Sed efficitur, mauris sit amet ultricies lobortis, elit leo efficitur lectus, ac varius odio ipsum at ante. Cras ut suscipit felis. Vivamus tincidunt justo eu sapien consectetur, at fringilla sem dignissim. Nulla facilisi. Fusce tincidunt, felis ac convallis scelerisque, nisi odio lacinia libero, at dapibus ligula eros et libero.
10 |
11 | Sed ut purus eget mi tincidunt gravida. Proin tincidunt, ante ac vestibulum dictum, enim felis faucibus sapien, non auctor dui lectus nec felis. Curabitur a ex dui. Suspendisse potenti. Sed ac sagittis orci. Integer vehicula nibh a magna aliquam ultricies.
12 |
13 | Donec iaculis leo non quam consequat tincidunt. Ut condimentum libero ut sem suscipit, et maximus ante feugiat. Aliquam erat volutpat. Ut nec viverra ex. Cras sit amet tortor nec mi tempor fermentum. Integer pharetra neque ut lectus faucibus, nec laoreet enim tincidunt.
14 |
--------------------------------------------------------------------------------
/pizza-cooking-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/cooking/service/Application.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.cooking.service;
30 |
31 | import ionutbalosin.training.application.security.practices.client.credentials.handler.IdpTokenFetcher;
32 | import org.springframework.boot.SpringApplication;
33 | import org.springframework.boot.autoconfigure.SpringBootApplication;
34 | import org.springframework.cloud.openfeign.EnableFeignClients;
35 | import org.springframework.context.annotation.ComponentScan;
36 |
37 | @EnableFeignClients
38 | @SpringBootApplication
39 | @ComponentScan(basePackageClasses = {Application.class, IdpTokenFetcher.class})
40 | public class Application {
41 |
42 | public static void main(String[] args) {
43 | SpringApplication.run(Application.class, args);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pizza-cooking-api/src/main/resources/service-api.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | openapi: 3.0.3
3 | info:
4 | title: Pizza Cooking Api
5 | version: "1.0"
6 | paths:
7 | /pizza/cooking/orders:
8 | post:
9 | tags:
10 | - "Pizza Cooking Api"
11 | summary: Create a new order
12 | parameters:
13 | - $ref: '#/components/parameters/Authorization'
14 | requestBody:
15 | description: Partial update content
16 | required: true
17 | content:
18 | application/json:
19 | schema:
20 | $ref: '#/components/schemas/PizzaCookingOrderDto'
21 | responses:
22 | "201":
23 | description: Created
24 | "400":
25 | description: Bad request
26 | "403":
27 | description: Forbidden
28 | "500":
29 | description: Internal server error
30 | components:
31 | schemas:
32 | PizzaCookingOrderDto:
33 | type: object
34 | properties:
35 | orderId:
36 | type: string
37 | description: Order unique id
38 | format: uuid
39 | orders:
40 | type: array
41 | minItems: 1
42 | maxItems: 32
43 | description: Pizza order content
44 | items:
45 | $ref: '#/components/schemas/PizzaCookingOrderItemDto'
46 | required:
47 | - orderId
48 | - orders
49 | PizzaCookingOrderItemDto:
50 | type: object
51 | properties:
52 | name:
53 | type: string
54 | description: Name of the pizza being ordered.
55 | maxLength: 128
56 | example: "Margherita Pizza"
57 | quantity:
58 | type: integer
59 | description: Number of pizza being ordered.
60 | example: 3
61 | minimum: 1
62 | maximum: 12
63 | required:
64 | - name
65 | - quantity
66 | parameters:
67 | Authorization:
68 | name: Authorization
69 | in: header
70 | required: true
71 | description: JWT Authorizing token to allow access to the endpoint
72 | schema:
73 | type: string
--------------------------------------------------------------------------------
/pizza-delivery-api/src/main/resources/service-api.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | openapi: 3.0.3
3 | info:
4 | title: Pizza Delivery Api
5 | version: "1.0"
6 | paths:
7 | /pizza/delivery/orders:
8 | post:
9 | tags:
10 | - "Pizza Delivery Api"
11 | summary: Deliver a new order
12 | parameters:
13 | - $ref: '#/components/parameters/Authorization'
14 | requestBody:
15 | description: Partial update content
16 | required: true
17 | content:
18 | application/json:
19 | schema:
20 | $ref: '#/components/schemas/PizzaDeliveryOrderDto'
21 | responses:
22 | "201":
23 | description: Created
24 | "400":
25 | description: Bad request
26 | "403":
27 | description: Forbidden
28 | "500":
29 | description: Internal server error
30 | components:
31 | schemas:
32 | PizzaDeliveryOrderDto:
33 | type: object
34 | properties:
35 | orderId:
36 | type: string
37 | description: Order unique id
38 | format: uuid
39 | orders:
40 | type: array
41 | minItems: 1
42 | maxItems: 32
43 | description: Pizza order content
44 | items:
45 | $ref: '#/components/schemas/PizzaDeliveryOrderItemDto'
46 | required:
47 | - orderId
48 | - orders
49 | PizzaDeliveryOrderItemDto:
50 | type: object
51 | properties:
52 | name:
53 | type: string
54 | description: Name of the pizza being ordered.
55 | maxLength: 128
56 | example: "Margherita Pizza"
57 | quantity:
58 | type: integer
59 | description: Number of pizza being ordered.
60 | example: 3
61 | minimum: 1
62 | maximum: 12
63 | required:
64 | - name
65 | - quantity
66 | parameters:
67 | Authorization:
68 | name: Authorization
69 | in: header
70 | required: true
71 | description: JWT Authorizing token to allow access to the endpoint
72 | schema:
73 | type: string
--------------------------------------------------------------------------------
/security-token-client-credentials-fetcher/src/main/java/ionutbalosin/training/application/security/practices/client/credentials/handler/IdpToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.client.credentials.handler;
30 |
31 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
32 |
33 | @JsonIgnoreProperties(ignoreUnknown = true)
34 | public class IdpToken {
35 |
36 | private String access_token;
37 | private String token_type;
38 | private String expires_in;
39 |
40 | public String getAccess_token() {
41 | return access_token;
42 | }
43 |
44 | public void setAccess_token(String access_token) {
45 | this.access_token = access_token;
46 | }
47 |
48 | public String getToken_type() {
49 | return token_type;
50 | }
51 |
52 | public void setToken_type(String token_type) {
53 | this.token_type = token_type;
54 | }
55 |
56 | public String getExpires_in() {
57 | return expires_in;
58 | }
59 |
60 | public void setExpires_in(String expires_in) {
61 | this.expires_in = expires_in;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/security-feign-logger-enricher/src/main/java/ionutbalosin/training/application/security/practices/feign/logger/enricher/CorrelationIdInterceptor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.feign.logger.enricher;
30 |
31 | import feign.RequestInterceptor;
32 | import feign.RequestTemplate;
33 | import org.slf4j.MDC;
34 | import org.springframework.stereotype.Component;
35 |
36 | /**
37 | * This class adds the CorrelationId from the MDC (Mapped Diagnostic Context) to the headers of
38 | * outgoing Feign requests.
39 | *
40 | * This ensures that the CorrelationId is propagated across other microservice calls, allowing
41 | * for better tracing and debugging of requests throughout the application.
42 | */
43 | @Component
44 | public class CorrelationIdInterceptor implements RequestInterceptor {
45 |
46 | private static final String CORRELATION_ID = "CorrelationId";
47 |
48 | @Override
49 | public void apply(RequestTemplate template) {
50 | final String correlationId = MDC.get(CORRELATION_ID);
51 | if (correlationId != null) {
52 | template.header(CORRELATION_ID, correlationId);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/03-PRACTICE-API-and-Microservices-Security.md:
--------------------------------------------------------------------------------
1 | # Application Security for Java Developers
2 |
3 | Copyright (C) 2025 Ionut Balosin
4 |
5 | This project is licensed under the Apache License, Version 2.0.
6 | Please see the [LICENSE](license/LICENSE) file for full license.
7 |
8 | ---
9 |
10 | ## API and Microservices Security
11 |
12 | > ⏰ 40 minutes
13 |
14 | > 👨💼 Conducted By Trainer
15 |
16 | ### 📖 Informational: Software Architecture Diagram
17 |
18 | This software architecture diagram for the `Pizza` distributed application highlights key security aspects, including OAuth 2.0 flows (e.g., Token introspection, JWKS) and endpoint roles checks.
19 |
20 |
21 |
22 | ---
23 |
24 | ### 🏋️ Hands-On Demo
25 |
26 | **Note:** Please ensure that the Docker daemon is running; otherwise, the commands will not execute successfully.
27 |
28 | 1. Open a terminal and start the `Pizza` application, which includes multiple microservices running in Docker, by using the following command:
29 |
30 | ```bash
31 | ./bootstrap-pizza-application.sh
32 | ```
33 |
34 | 2. Next, open `Postman` and import the [Postman collections](postman).
35 |
36 | 3. From the provided `Postman` collections, choose one of the following OAuth 2.0 flows to obtain a proper JWT token:
37 | - `Password Flow`
38 | - `Client Credentials Flow`
39 | - `Authorization Code Flow with PKCE` (using the credentials `demo_user:Test1234!`) *(recommended)*
40 |
41 | 4. Finally, initiate a pizza order request using the endpoint `POST /pizza/orders`. If the command succeeds, the response should be `201 Created`.
42 |
43 | 5. To view further request processing details, open the console logs of each Docker container by running:
44 |
45 | ```bash
46 | docker logs -f
47 | ```
48 |
49 | where `` can be retrieved by running:
50 |
51 | ```bash
52 | docker ps -a
53 | ```
54 |
55 | 6. Additionally, to better understand the `Token Introspection`, `JSON Web Key Set`, and `roles-based access control` implementations, please check out the following modules:
56 | - [security-token-introspection](security-token-introspection)
57 | - [security-token-jwks](security-token-jwks)
58 | - [security-token-client-credentials-fetcher](security-token-client-credentials-fetcher)
59 |
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/cache/PizzaCookingOrderCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service.cache;
30 |
31 | import com.github.benmanes.caffeine.cache.Cache;
32 | import com.github.benmanes.caffeine.cache.Caffeine;
33 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderStatusDto;
34 | import java.util.UUID;
35 | import java.util.concurrent.TimeUnit;
36 |
37 | public enum PizzaCookingOrderCache {
38 | CACHE_INSTANCE;
39 |
40 | private Cache pizzaCookingOrderCache;
41 |
42 | PizzaCookingOrderCache() {
43 | this.pizzaCookingOrderCache =
44 | Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(3, TimeUnit.HOURS).build();
45 | }
46 |
47 | public PizzaOrderStatusDto getProduct(UUID pizzaOrderId) {
48 | return pizzaCookingOrderCache.getIfPresent(pizzaOrderId);
49 | }
50 |
51 | public void addProduct(UUID pizzaOrderId, PizzaOrderStatusDto pizzaOrderStatusDto) {
52 | pizzaCookingOrderCache.put(pizzaOrderId, pizzaOrderStatusDto);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/01-PRACTICE-Security-Design-Principles.md:
--------------------------------------------------------------------------------
1 | # Application Security for Java Developers
2 |
3 | Copyright (C) 2025 Ionut Balosin
4 |
5 | This project is licensed under the Apache License, Version 2.0.
6 | Please see the [LICENSE](license/LICENSE) file for full license.
7 |
8 | ---
9 |
10 | ## Security Design Principles
11 |
12 | > ⏰ 20 minutes
13 |
14 | > 👨🎓 Attendees' Exercise
15 |
16 | A company, `SecureBank`, recently launched an online banking platform. However, users have reported various security issues. Here’s what happened in some key events.
17 |
18 | ### 1. User Accounts Compromised
19 |
20 | `SecureBank`’s employees use shared administrator accounts to access customer data. Due to this, one employee accidentally deleted critical customer information.
21 |
22 | ❓ **Question**: Which principle is lacking here, and what would you recommend to address it?
23 |
24 | 1. Defense in depth
25 | 2. Least privilege
26 | 3. Fail securely
27 | 4. Compartmentalization
28 |
29 | ---
30 |
31 | ### 2. System Breach Through a Single Firewall
32 |
33 | Hackers breached `SecureBank`'s platform by exploiting a vulnerability in its single-layer firewall. With no additional protective layers, attackers gained direct access to sensitive data.
34 |
35 | ❓ **Question**: Which security principle could have mitigated this risk?
36 |
37 | 1. Compartmentalization
38 | 2. Defense in depth
39 | 3. Least privilege
40 | 4. Fail securely
41 |
42 | ---
43 |
44 | ### 3. Sensitive Data Exposure During System Error
45 |
46 | During a system crash, users noticed that sensitive data, such as account balances, were briefly exposed on error pages.
47 |
48 | ❓ **Question**: Which principle is missing, and how would applying it prevent this issue?
49 |
50 | 1. Least privilege
51 | 2. Compartmentalization
52 | 3. Fail securely
53 | 4. Defense in depth
54 |
55 | ---
56 |
57 | ### 4. Cross-System Data Leakage
58 |
59 | The `SecureBank` platform stores credit card information alongside other non-sensitive data in the same database. When an unrelated feature failed, developers inadvertently accessed credit card data during troubleshooting.
60 |
61 | ❓ **Question**: Which principle should `SecureBank` implement to prevent this kind of issue?
62 |
63 | 1. Compartmentalization
64 | 2. Defense in depth
65 | 3. Fail securely
66 | 4. Least privilege
--------------------------------------------------------------------------------
/pizza-menu.json:
--------------------------------------------------------------------------------
1 | {
2 | "pizza_menu": [
3 | {
4 | "name": "Margherita",
5 | "price": {
6 | "amount": 8.5,
7 | "currency": "EUR"
8 | },
9 | "description": "Classic tomato sauce, mozzarella, and fresh basil."
10 | },
11 | {
12 | "name": "Pepperoni",
13 | "price": {
14 | "amount": 10,
15 | "currency": "EUR"
16 | },
17 | "description": "Tomato sauce, mozzarella, and spicy pepperoni slices."
18 | },
19 | {
20 | "name": "Quattro Stagioni",
21 | "price": {
22 | "amount": 11.5,
23 | "currency": "EUR"
24 | },
25 | "description": "Tomato sauce, mozzarella, mushrooms, ham, artichokes, and olives."
26 | },
27 | {
28 | "name": "Vegetariana",
29 | "price": {
30 | "amount": 9.5,
31 | "currency": "EUR"
32 | },
33 | "description": "Tomato sauce, mozzarella, grilled vegetables, and olives."
34 | },
35 | {
36 | "name": "Hawaiian",
37 | "price": {
38 | "amount": 10.5,
39 | "currency": "EUR"
40 | },
41 | "description": "Tomato sauce, mozzarella, ham, and pineapple."
42 | },
43 | {
44 | "name": "Diavola",
45 | "price": {
46 | "amount": 11,
47 | "currency": "EUR"
48 | },
49 | "description": "Tomato sauce, mozzarella, spicy salami, chili peppers, and olives."
50 | },
51 | {
52 | "name": "Capricciosa",
53 | "price": {
54 | "amount": 12,
55 | "currency": "EUR"
56 | },
57 | "description": "Tomato sauce, mozzarella, ham, mushrooms, olives, and artichokes."
58 | },
59 | {
60 | "name": "Quattro Formaggi",
61 | "price": {
62 | "amount": 12.5,
63 | "currency": "EUR"
64 | },
65 | "description": "Tomato sauce, mozzarella, gorgonzola, parmesan, and ricotta."
66 | },
67 | {
68 | "name": "Frutti di Mare",
69 | "price": {
70 | "amount": 13.5,
71 | "currency": "EUR"
72 | },
73 | "description": "Tomato sauce, mozzarella, shrimp, mussels, squid, and garlic."
74 | },
75 | {
76 | "name": "Boscaiola",
77 | "price": {
78 | "amount": 11,
79 | "currency": "EUR"
80 | },
81 | "description": "Tomato sauce, mozzarella, mushrooms, sausage, and truffle oil."
82 | }
83 | ]
84 | }
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/service/OrderService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service.service;
30 |
31 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderDto;
32 | import ionutbalosin.training.application.security.practices.pizza.order.service.client.CookingClient;
33 | import org.slf4j.Logger;
34 | import org.slf4j.LoggerFactory;
35 | import org.springframework.stereotype.Service;
36 |
37 | @Service
38 | public class OrderService {
39 |
40 | private static final Logger LOGGER = LoggerFactory.getLogger(OrderService.class);
41 |
42 | private final CookingClient cookingClient;
43 |
44 | public OrderService(CookingClient cookingClient) {
45 | this.cookingClient = cookingClient;
46 | }
47 |
48 | public void pizzaOrdersPost(String authorization, PizzaCookingOrderDto pizzaCookingOrderDto) {
49 | cookingClient.pizzaCookingOrdersPost(authorization, pizzaCookingOrderDto);
50 | LOGGER.info(
51 | "Pizza order '{}' has been successfully sent for cooking.",
52 | pizzaCookingOrderDto.getOrderId());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/pizza-cooking-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/cooking/service/mapper/PizzaDeliveryOrderDtoMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.cooking.service.mapper;
30 |
31 | import static java.util.stream.Collectors.toList;
32 |
33 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderDto;
34 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.model.PizzaDeliveryOrderDto;
35 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.model.PizzaDeliveryOrderItemDto;
36 |
37 | public class PizzaDeliveryOrderDtoMapper {
38 |
39 | public PizzaDeliveryOrderDto map(PizzaCookingOrderDto pizzaCookingOrderDto) {
40 | return new PizzaDeliveryOrderDto()
41 | .orderId(pizzaCookingOrderDto.getOrderId())
42 | .orders(
43 | pizzaCookingOrderDto.getOrders().stream()
44 | .map(
45 | cookingOrderDto ->
46 | new PizzaDeliveryOrderItemDto()
47 | .name(cookingOrderDto.getName())
48 | .quantity(cookingOrderDto.getQuantity()))
49 | .collect(toList()));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/sanitizer/OrderSanitizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service.sanitizer;
30 |
31 | import static java.util.Optional.ofNullable;
32 |
33 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderCustomerDto;
34 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderDto;
35 | import org.springframework.stereotype.Service;
36 | import org.springframework.web.util.HtmlUtils;
37 |
38 | @Service
39 | public class OrderSanitizer {
40 |
41 | /**
42 | * This method sanitizes the special request free text field to prevent cross-site scripting (XSS)
43 | * attacks. It uses HTML escaping to ensure that any potentially malicious content in the free
44 | * text field is neutralized before processing.
45 | */
46 | public void sanitizeSpecialRequest(PizzaOrderDto pizzaOrderDto) {
47 | ofNullable(pizzaOrderDto.getCustomer())
48 | .map(PizzaOrderCustomerDto::getSpecialRequest)
49 | .map(HtmlUtils::htmlEscape)
50 | .ifPresent(
51 | sanitizedSpecialRequest ->
52 | pizzaOrderDto.getCustomer().setSpecialRequest(sanitizedSpecialRequest));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/bootstrap-pizza-application.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Application Security for Java Developers
4 | #
5 | # Copyright (C) 2025 Ionut Balosin
6 | # Website: www.ionutbalosin.com
7 | # Social Media:
8 | # LinkedIn: ionutbalosin
9 | # Bluesky: @ionutbalosin.bsky.social
10 | # X: @ionutbalosin
11 | # Mastodon: ionutbalosin@mastodon.social
12 | #
13 | # Licensed to the Apache Software Foundation (ASF) under one
14 | # or more contributor license agreements. See the NOTICE file
15 | # distributed with this work for additional information
16 | # regarding copyright ownership. The ASF licenses this file
17 | # to you under the Apache License, Version 2.0 (the
18 | # "License"); you may not use this file except in compliance
19 | # with the License. You may obtain a copy of the License at
20 | #
21 | # http://www.apache.org/licenses/LICENSE-2.0
22 | #
23 | # Unless required by applicable law or agreed to in writing,
24 | # software distributed under the License is distributed on an
25 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26 | # KIND, either express or implied. See the License for the
27 | # specific language governing permissions and limitations
28 | # under the License.
29 | #
30 |
31 | echo ""
32 | echo "******************************************************"
33 | echo "* [1/3] Compile and package all Spring Boot services *"
34 | echo "******************************************************"
35 | echo ""
36 |
37 | BASE_DIR="${PWD}"
38 |
39 | if ! ./mvnw package -DskipTests; then
40 | echo ""
41 | echo "ERROR: Maven encountered errors and cannot continue."
42 | exit 1
43 | fi
44 |
45 | echo ""
46 | echo "**********************************************************"
47 | echo "* [2/3] Build Docker images for all Spring Boot services *"
48 | echo "**********************************************************"
49 | echo ""
50 |
51 | cd ./pizza-order-service
52 | ./build-docker.sh || exit 1
53 | cd "${BASE_DIR}"
54 |
55 | cd ./pizza-cooking-service
56 | ./build-docker.sh || exit 1
57 | cd "${BASE_DIR}"
58 |
59 | cd ./pizza-delivery-service
60 | ./build-docker.sh || exit 1
61 | cd "${BASE_DIR}"
62 |
63 | echo ""
64 | echo "***************************************************"
65 | echo "* [3/3] Start all Spring Boot services with Docker *"
66 | echo "***************************************************"
67 | echo ""
68 |
69 | docker compose -f ./docker-compose-pizza-application.yml \
70 | up -d
71 | if [ $? -ne 0 ]; then
72 | echo "Error: Docker services failed to start."
73 | exit 1
74 | fi
75 |
76 | echo ""
77 | echo "🎉 Congratulations! Everything was successful."
--------------------------------------------------------------------------------
/security-feign-logger-enricher/src/main/java/ionutbalosin/training/application/security/practices/feign/logger/enricher/CustomSlf4jLogger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.feign.logger.enricher;
30 |
31 | import feign.Request;
32 | import org.slf4j.Logger;
33 | import org.slf4j.LoggerFactory;
34 |
35 | /**
36 | * This class provides enhanced logging capabilities using SLF4J, allowing for customized logging of
37 | * Feign requests and responses while maintaining a consistent format.
38 | *
39 | * This logger captures requests and logs them at the INFO level, enabling effective tracking and
40 | * analysis of interactions with external services.
41 | */
42 | public class CustomSlf4jLogger extends feign.Logger {
43 |
44 | private final Logger logger;
45 |
46 | public CustomSlf4jLogger(Class> clazz) {
47 | this(LoggerFactory.getLogger(clazz));
48 | }
49 |
50 | public CustomSlf4jLogger() {
51 | this(feign.Logger.class);
52 | }
53 |
54 | private CustomSlf4jLogger(Logger logger) {
55 | this.logger = logger;
56 | }
57 |
58 | @Override
59 | protected void logRequest(String configKey, Level logLevel, Request request) {
60 | super.logRequest(configKey, logLevel, request);
61 | }
62 |
63 | @Override
64 | protected void log(String configKey, String format, Object... args) {
65 | logger.info(String.format(methodTag(configKey) + format, args));
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/pizza-cooking-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/cooking/service/config/MapperConfig.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.cooking.service.config;
30 |
31 | import ionutbalosin.training.application.security.practices.pizza.cooking.service.mapper.PizzaDeliveryOrderDtoMapper;
32 | import java.util.concurrent.Executor;
33 | import org.springframework.beans.factory.annotation.Qualifier;
34 | import org.springframework.context.annotation.Bean;
35 | import org.springframework.context.annotation.Configuration;
36 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
37 |
38 | @Configuration
39 | public class MapperConfig {
40 |
41 | @Bean
42 | public PizzaDeliveryOrderDtoMapper pizzaDeliveryOrderDtoMapper() {
43 | return new PizzaDeliveryOrderDtoMapper();
44 | }
45 |
46 | @Bean
47 | @Qualifier("PizzaCookingExecutor")
48 | public Executor executor() {
49 | // Configure a thread pool with a core pool size of 2, a maximum pool size of 4,
50 | // and leave the queue capacity at its default value (i.e., unlimited).
51 | // Note: Adjust these values if latency issues arise.
52 | final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
53 | executor.setCorePoolSize(2);
54 | executor.setMaxPoolSize(4);
55 | executor.setThreadNamePrefix("PizzaCookingThreadPoolExecutor");
56 | executor.initialize();
57 | return executor;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/java/ionutbalosin/training/application/security/practices/encryption/decryption/hashing/MessageHashing.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.encryption.decryption.hashing;
30 |
31 | import java.security.MessageDigest;
32 | import java.security.NoSuchAlgorithmException;
33 |
34 | /**
35 | * This class demonstrates how to use SHA-256 to verify data integrity by comparing hash values.
36 | *
37 | *
Note: SHA-256 ensures data integrity but does not provide authenticity (unlike HMAC), meaning
38 | * an attacker can still modify data and recompute the hash.
39 | */
40 | public class MessageHashing {
41 |
42 | public static void main(String[] args) throws Exception {
43 | final String originalData = "This is the original data.";
44 | byte[] originalHash = hashData(originalData.getBytes());
45 |
46 | // Simulate data transmission or storage (assume data might be modified)
47 | final String receivedData = "This is the original data.";
48 | byte[] receivedHash = hashData(receivedData.getBytes());
49 |
50 | // Verify data integrity by comparing hashes
51 | boolean isDataIntact = MessageDigest.isEqual(originalHash, receivedHash);
52 | System.out.printf("Is data intact: [%s]%n", isDataIntact);
53 | }
54 |
55 | // Computes the SHA-256 hash of the given data.
56 | public static byte[] hashData(byte[] data) throws NoSuchAlgorithmException {
57 | final MessageDigest digest = MessageDigest.getInstance("SHA-256");
58 | return digest.digest(data);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/mapper/PizzaCookingOrderDtoMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service.mapper;
30 |
31 | import static java.util.stream.Collectors.toList;
32 |
33 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderDto;
34 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderItemDto;
35 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderDto;
36 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderProcessingStatusDto;
37 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderStatusDto;
38 | import java.util.UUID;
39 |
40 | public class PizzaCookingOrderDtoMapper {
41 |
42 | public PizzaCookingOrderDto map(PizzaOrderDto pizzaOrderDto) {
43 | return new PizzaCookingOrderDto()
44 | .orderId(UUID.randomUUID())
45 | .orders(
46 | pizzaOrderDto.getOrders().stream()
47 | .map(
48 | orderDto ->
49 | new PizzaCookingOrderItemDto()
50 | .name(orderDto.getName())
51 | .quantity(orderDto.getQuantity()))
52 | .collect(toList()));
53 | }
54 |
55 | public PizzaOrderStatusDto map(
56 | PizzaOrderDto pizzaOrderDto, PizzaOrderProcessingStatusDto orderStatus) {
57 | return new PizzaOrderStatusDto().pizzaOrder(pizzaOrderDto).orderStatus(orderStatus);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pizza-delivery-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/delivery/service/service/DeliveryService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.delivery.service.service;
30 |
31 | import static ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderProcessingStatusDto.*;
32 |
33 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.model.PizzaDeliveryOrderDto;
34 | import ionutbalosin.training.application.security.practices.pizza.delivery.service.client.OrderClient;
35 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderUpdatedStatusDto;
36 | import org.slf4j.Logger;
37 | import org.slf4j.LoggerFactory;
38 | import org.springframework.stereotype.Service;
39 |
40 | @Service
41 | public class DeliveryService {
42 |
43 | private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryService.class);
44 |
45 | private OrderClient orderClient;
46 |
47 | public DeliveryService(OrderClient orderClient) {
48 | this.orderClient = orderClient;
49 | }
50 |
51 | public void pizzaDeliveryOrdersPost(
52 | String authorization, PizzaDeliveryOrderDto pizzaDeliveryOrderDto) {
53 | // TODO: Implement actual delivery process (e.g., logistics, tracking)
54 | LOGGER.info(
55 | "Pizza order '{}' has been successfully delivered.", pizzaDeliveryOrderDto.getOrderId());
56 |
57 | // Update the order's processing status to 'DELIVERED' in the order service
58 | orderClient.pizzaOrdersOrderIdPut(
59 | authorization,
60 | pizzaDeliveryOrderDto.getOrderId(),
61 | new PizzaOrderUpdatedStatusDto().orderStatus(DELIVERED));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/security-feign-logger-enricher/src/main/java/ionutbalosin/training/application/security/practices/feign/logger/enricher/FeignConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.feign.logger.enricher;
30 |
31 | import feign.Logger;
32 | import feign.RequestInterceptor;
33 | import org.springframework.beans.factory.annotation.Value;
34 | import org.springframework.context.annotation.Bean;
35 | import org.springframework.context.annotation.Configuration;
36 |
37 | /**
38 | * The FeignConfiguration class is responsible for configuring the logging behavior of Feign
39 | * clients.
40 | *
41 | *
By default, Feign does not provide logging, so this class enables and customizes it.
42 | *
43 | *
Feign supports four logging levels:
44 | *
45 | *
46 | * - NONE: no logging (default)
47 | *
- BASIC: logs the request method, URL, response status code, and execution time
48 | *
- HEADERS: logs the request method, URL, headers, response status code, and execution time
49 | *
- FULL: logs the headers, body, and metadata for both requests and responses
50 | *
51 | *
52 | * This configuration sets the default logging level to BASIC instead of the default NONE.
53 | * Additionally, it returns an instance of a custom logger bean, enabling SLF4J-based logging for
54 | * all Feign requests and responses.
55 | */
56 | @Configuration
57 | public class FeignConfiguration {
58 |
59 | @Bean
60 | public Logger.Level feignLoggerLevel(
61 | @Value("${logging.feignLevel:BASIC}") Logger.Level feignLoggingLevel) {
62 | return feignLoggingLevel;
63 | }
64 |
65 | @Bean
66 | public Logger feignLogger() {
67 | return new CustomSlf4jLogger();
68 | }
69 |
70 | @Bean
71 | public RequestInterceptor correlationIdInterceptor() {
72 | return new CorrelationIdInterceptor();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/zap-scan.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Application Security for Java Developers
4 | #
5 | # Copyright (C) 2025 Ionut Balosin
6 | # Website: www.ionutbalosin.com
7 | # Social Media:
8 | # LinkedIn: ionutbalosin
9 | # Bluesky: @ionutbalosin.bsky.social
10 | # X: @ionutbalosin
11 | # Mastodon: ionutbalosin@mastodon.social
12 | #
13 | # Licensed to the Apache Software Foundation (ASF) under one
14 | # or more contributor license agreements. See the NOTICE file
15 | # distributed with this work for additional information
16 | # regarding copyright ownership. The ASF licenses this file
17 | # to you under the Apache License, Version 2.0 (the
18 | # "License"); you may not use this file except in compliance
19 | # with the License. You may obtain a copy of the License at
20 | #
21 | # http://www.apache.org/licenses/LICENSE-2.0
22 | #
23 | # Unless required by applicable law or agreed to in writing,
24 | # software distributed under the License is distributed on an
25 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26 | # KIND, either express or implied. See the License for the
27 | # specific language governing permissions and limitations
28 | # under the License.
29 | #
30 |
31 | # Script properties used across all script commands
32 | DOCKER_NETWORK="security-practices"
33 | ZAPROXY_CONTAINER="zaproxy-zap.local"
34 | ZAPROXY_IMAGE="zaproxy-zap:local"
35 |
36 | echo ""
37 | echo "**********************************"
38 | echo "* Start ZAP scanning with Docker *"
39 | echo "**********************************"
40 | echo ""
41 |
42 | # Check if Docker network exists
43 | echo "Checking if Docker network [$DOCKER_NETWORK] exists ..."
44 | docker network inspect $DOCKER_NETWORK > /dev/null 2>&1
45 | if [ $? -ne 0 ]; then
46 | echo "Docker network [$DOCKER_NETWORK] does not exist."
47 | echo "Please ensure the other services have started (e.g., $ ./bootstrap.sh)."
48 | exit 1
49 | fi
50 |
51 | # Build the ZAP Docker image
52 | echo "Building the ZAP Docker image [$ZAPROXY_IMAGE] ..."
53 | docker build -t $ZAPROXY_IMAGE -f Dockerfile-zap .
54 | if [ $? -ne 0 ]; then
55 | echo "Building the ZAP Docker image [$ZAPROXY_IMAGE] failed."
56 | exit 1
57 | fi
58 |
59 | # Check if the ZAP container already exists and remove it if necessary
60 | docker ps -a --filter "name=$ZAPROXY_CONTAINER" --format "{{.Names}}" | grep -w $ZAPROXY_CONTAINER > /dev/null 2>&1
61 | if [ $? -eq 0 ]; then
62 | echo "Removing existing ZAP container [$ZAPROXY_CONTAINER] ..."
63 | docker rm -f $ZAPROXY_CONTAINER
64 | fi
65 |
66 | # Run the ZAP container
67 | echo "Running ZAP container [$ZAPROXY_CONTAINER] ..."
68 | docker run -d \
69 | --name $ZAPROXY_CONTAINER \
70 | -p 17070:7070 -p 17080:7080 \
71 | --network $DOCKER_NETWORK \
72 | -v $(pwd)/zap/reports:/zap/reports \
73 | $ZAPROXY_IMAGE
74 | if [ $? -ne 0 ]; then
75 | echo "Failed to run the ZAP container [$ZAPROXY_CONTAINER]."
76 | exit 1
77 | fi
78 |
79 | echo ""
80 | echo "🎉 Congratulations! Everything was successful."
81 | echo "Please check the '$(pwd)/zap/reports' folder 📂 for the HTML report."
82 |
--------------------------------------------------------------------------------
/security-token-client-credentials-fetcher/src/main/java/ionutbalosin/training/application/security/practices/client/credentials/handler/util/JsonObjectMapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.client.credentials.handler.util;
30 |
31 | import static java.util.Optional.ofNullable;
32 |
33 | import com.fasterxml.jackson.annotation.JsonInclude;
34 | import com.fasterxml.jackson.databind.DeserializationFeature;
35 | import com.fasterxml.jackson.databind.ObjectMapper;
36 | import com.fasterxml.jackson.databind.ObjectReader;
37 | import com.fasterxml.jackson.databind.SerializationFeature;
38 | import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
39 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
40 | import java.io.IOException;
41 |
42 | public class JsonObjectMapper {
43 |
44 | private static final ObjectMapper OBJECT_MAPPER =
45 | new ObjectMapper()
46 | .registerModule(new Jdk8Module())
47 | .registerModule(new JavaTimeModule())
48 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
49 | .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
50 | .setSerializationInclusion(JsonInclude.Include.NON_NULL);
51 |
52 | private JsonObjectMapper() {}
53 |
54 | public static T deserialize(String source, Class target) {
55 | if (target == null) {
56 | throw new IllegalArgumentException("Target class cannot be null.");
57 | }
58 |
59 | return ofNullable(source)
60 | .map(
61 | src -> {
62 | try {
63 | return getObjectReader().readValue(source, target);
64 | } catch (IOException e) {
65 | throw new RuntimeException("Could not deserialize Json string", e);
66 | }
67 | })
68 | .orElse(null);
69 | }
70 |
71 | private static ObjectReader getObjectReader() {
72 | return OBJECT_MAPPER.reader();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/pizza-delivery-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/delivery/service/controller/DeliveryController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.delivery.service.controller;
30 |
31 | import static org.springframework.http.HttpStatus.CREATED;
32 |
33 | import io.swagger.v3.oas.annotations.Parameter;
34 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.PizzaApi;
35 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.model.PizzaDeliveryOrderDto;
36 | import ionutbalosin.training.application.security.practices.pizza.delivery.service.service.DeliveryService;
37 | import org.slf4j.Logger;
38 | import org.slf4j.LoggerFactory;
39 | import org.springframework.http.ResponseEntity;
40 | import org.springframework.security.access.prepost.PreAuthorize;
41 | import org.springframework.stereotype.Controller;
42 | import org.springframework.web.bind.annotation.RequestBody;
43 | import org.springframework.web.bind.annotation.RequestHeader;
44 |
45 | @Controller
46 | public class DeliveryController implements PizzaApi {
47 |
48 | private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryController.class);
49 |
50 | private final DeliveryService deliveryService;
51 |
52 | public DeliveryController(DeliveryService deliveryService) {
53 | this.deliveryService = deliveryService;
54 | }
55 |
56 | @Override
57 | @PreAuthorize("hasAuthority('demo_private_client_role')")
58 | public ResponseEntity pizzaDeliveryOrdersPost(
59 | @Parameter(name = "Authorization") @RequestHeader String authorization,
60 | @RequestBody PizzaDeliveryOrderDto pizzaDeliveryOrderDto) {
61 | LOGGER.info(
62 | "pizzaDeliveryOrdersPost(pizzaDeliveryOrder = '{}')", pizzaDeliveryOrderDto.getOrderId());
63 |
64 | deliveryService.pizzaDeliveryOrdersPost(authorization, pizzaDeliveryOrderDto);
65 | return new ResponseEntity<>(CREATED);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/pizza-order-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/order/service/client/CookingClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.order.service.client;
30 |
31 | import io.swagger.v3.oas.annotations.Parameter;
32 | import io.swagger.v3.oas.annotations.enums.ParameterIn;
33 | import ionutbalosin.training.application.security.practices.feign.logger.enricher.FeignConfiguration;
34 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderDto;
35 | import jakarta.validation.Valid;
36 | import jakarta.validation.constraints.NotNull;
37 | import org.springframework.cloud.openfeign.FeignClient;
38 | import org.springframework.http.ResponseEntity;
39 | import org.springframework.web.bind.annotation.RequestBody;
40 | import org.springframework.web.bind.annotation.RequestHeader;
41 | import org.springframework.web.bind.annotation.RequestMapping;
42 | import org.springframework.web.bind.annotation.RequestMethod;
43 |
44 | @FeignClient(
45 | name = "${pizza-cooking-service.name}",
46 | url = "${pizza-cooking-service-endpoint.url}",
47 | configuration = FeignConfiguration.class)
48 | public interface CookingClient {
49 |
50 | @RequestMapping(
51 | method = RequestMethod.POST,
52 | value = "/pizza/cooking/orders",
53 | consumes = {"application/json"})
54 | ResponseEntity pizzaCookingOrdersPost(
55 | @Parameter(
56 | name = "Authorization",
57 | description = "JWT Authorizing token to allow access to endpoint",
58 | required = true,
59 | in = ParameterIn.HEADER)
60 | @RequestHeader(value = "Authorization", required = true)
61 | @NotNull
62 | String authorization,
63 | @Parameter(
64 | name = "PizzaCookingOrderDto",
65 | description = "Partial update content",
66 | required = true)
67 | @RequestBody
68 | @Valid
69 | PizzaCookingOrderDto pizzaCookingOrderDto);
70 | }
71 |
--------------------------------------------------------------------------------
/pizza-cooking-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/cooking/service/client/DeliveryClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.cooking.service.client;
30 |
31 | import io.swagger.v3.oas.annotations.Parameter;
32 | import io.swagger.v3.oas.annotations.enums.ParameterIn;
33 | import ionutbalosin.training.application.security.practices.feign.logger.enricher.FeignConfiguration;
34 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.model.PizzaDeliveryOrderDto;
35 | import jakarta.validation.Valid;
36 | import jakarta.validation.constraints.NotNull;
37 | import org.springframework.cloud.openfeign.FeignClient;
38 | import org.springframework.http.ResponseEntity;
39 | import org.springframework.web.bind.annotation.RequestBody;
40 | import org.springframework.web.bind.annotation.RequestHeader;
41 | import org.springframework.web.bind.annotation.RequestMapping;
42 | import org.springframework.web.bind.annotation.RequestMethod;
43 |
44 | @FeignClient(
45 | name = "${pizza-delivery-service.name}",
46 | url = "${pizza-delivery-service-endpoint.url}",
47 | configuration = FeignConfiguration.class)
48 | public interface DeliveryClient {
49 |
50 | @RequestMapping(
51 | method = RequestMethod.POST,
52 | value = "/pizza/delivery/orders",
53 | consumes = {"application/json"})
54 | ResponseEntity pizzaDeliveryOrdersPost(
55 | @NotNull
56 | @Parameter(
57 | name = "Authorization",
58 | description = "JWT Authorizing token to allow access to endpoint",
59 | required = true,
60 | in = ParameterIn.HEADER)
61 | @RequestHeader(value = "Authorization", required = true)
62 | String authorization,
63 | @Parameter(
64 | name = "PizzaDeliveryOrderDto",
65 | description = "Partial update content",
66 | required = true)
67 | @Valid
68 | @RequestBody
69 | PizzaDeliveryOrderDto pizzaDeliveryOrderDto);
70 | }
71 |
--------------------------------------------------------------------------------
/pizza-order-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | ionutbalosin.training.application.security.practices
8 | application-security-practices
9 | 0.0.1-SNAPSHOT
10 |
11 |
12 | Pizza Order Service
13 | pizza-order-service
14 |
15 |
16 |
17 | ionutbalosin.training.application.security.practices
18 | pizza-order-api
19 | 0.0.1-SNAPSHOT
20 |
21 |
22 | ionutbalosin.training.application.security.practices
23 | pizza-cooking-api
24 | 0.0.1-SNAPSHOT
25 |
26 |
27 | ionutbalosin.training.application.security.practices
28 | security-slf4j-logger-enricher
29 | 0.0.1-SNAPSHOT
30 |
31 |
32 | ionutbalosin.training.application.security.practices
33 | security-feign-logger-enricher
34 | 0.0.1-SNAPSHOT
35 |
36 |
37 | ionutbalosin.training.application.security.practices
38 | security-token-introspection
39 | 0.0.1-SNAPSHOT
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-web
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-oauth2-resource-server
48 |
49 |
50 | org.springframework.cloud
51 | spring-cloud-starter-openfeign
52 |
53 |
54 | com.github.ben-manes.caffeine
55 | caffeine
56 |
57 |
58 | org.apache.tika
59 | tika-core
60 |
61 |
62 | org.springdoc
63 | springdoc-openapi-starter-webmvc-ui
64 |
65 |
66 |
67 |
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-maven-plugin
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/pizza-delivery-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | ionutbalosin.training.application.security.practices
8 | application-security-practices
9 | 0.0.1-SNAPSHOT
10 |
11 |
12 | Pizza Delivery Service
13 | pizza-delivery-service
14 |
15 |
16 |
17 | ionutbalosin.training.application.security.practices
18 | pizza-delivery-api
19 | 0.0.1-SNAPSHOT
20 |
21 |
22 | ionutbalosin.training.application.security.practices
23 | pizza-order-api
24 | 0.0.1-SNAPSHOT
25 |
26 |
27 | ionutbalosin.training.application.security.practices
28 | security-slf4j-logger-enricher
29 | 0.0.1-SNAPSHOT
30 |
31 |
32 | ionutbalosin.training.application.security.practices
33 | security-feign-logger-enricher
34 | 0.0.1-SNAPSHOT
35 |
36 |
37 | ionutbalosin.training.application.security.practices
38 | security-token-jwks
39 | 0.0.1-SNAPSHOT
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-web
44 |
45 |
46 | org.springframework.boot
47 | spring-boot-starter-oauth2-resource-server
48 |
49 |
50 | org.springframework.cloud
51 | spring-cloud-starter-openfeign
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-starter-cache
56 |
57 |
58 | org.springdoc
59 | springdoc-openapi-starter-webmvc-ui
60 |
61 |
62 | com.squareup.okhttp3
63 | okhttp
64 |
65 |
66 |
67 |
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-maven-plugin
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/java/ionutbalosin/training/application/security/practices/encryption/decryption/hashing/HmacMessageAuthenticator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.encryption.decryption.hashing;
30 |
31 | import java.util.Arrays;
32 | import javax.crypto.KeyGenerator;
33 | import javax.crypto.Mac;
34 | import javax.crypto.SecretKey;
35 |
36 | /**
37 | * Demonstrates HMAC-SHA256 for data integrity and authenticity using a secure key.
38 | *
39 | * This approach is commonly used in scenarios such as verifying JSON Web Tokens (JWT). When an
40 | * Identity Provider (IdP) issues a JWT, it signs the token using HMAC-SHA256. Later, when the token
41 | * is received (either for introspection or via JWKS), the IdP checks its integrity and authenticity
42 | * by verifying the signature using the same secret key that was initially used to sign it.
43 | */
44 | public class HmacMessageAuthenticator {
45 |
46 | public static void main(String[] args) throws Exception {
47 | final SecretKey secretKey = generateHmacKey();
48 | final String originalData = "This is the original data.";
49 |
50 | // Generate HMAC for the original data
51 | final byte[] originalHmac = generateHMAC(originalData.getBytes(), secretKey);
52 |
53 | // Simulate data transmission or storage (assume data might be modified)
54 | final String receivedData = "This is the original data.";
55 | final byte[] receivedHmac = generateHMAC(receivedData.getBytes(), secretKey);
56 |
57 | // Verify data authenticity
58 | final boolean isAuthentic = Arrays.equals(originalHmac, receivedHmac);
59 | System.out.printf("Is data authentic: [%s]%n", isAuthentic);
60 | }
61 |
62 | // Generates a secure random key for HMAC-SHA256.
63 | public static SecretKey generateHmacKey() throws Exception {
64 | final KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA256");
65 | return keyGen.generateKey();
66 | }
67 |
68 | // Computes the HMAC-SHA256 of the given data using a secure secret key.
69 | public static byte[] generateHMAC(byte[] data, SecretKey key) throws Exception {
70 | final Mac mac = Mac.getInstance("HmacSHA256");
71 | mac.init(key);
72 | return mac.doFinal(data);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/serialization-deserialization/src/main/java/ionutbalosin/training/application/security/practices/serialization/deserialization/yaml/YamlBombDeserializer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.serialization.deserialization.yaml;
30 |
31 | import java.io.BufferedInputStream;
32 | import java.io.FileInputStream;
33 | import java.io.IOException;
34 | import java.io.InputStream;
35 | import java.util.List;
36 | import java.util.Map;
37 | import org.yaml.snakeyaml.Yaml;
38 |
39 | /**
40 | * This class reads and parses a YAML file containing a deeply recursive structure, referred to as a
41 | * "YAML bomb", using the SnakeYAML library. Parsing such a YAML structure may consume excessive CPU
42 | * or memory, potentially causing the application to crash or become unavailable, making it a
43 | * potential vector for denial-of-service (DoS) attacks.
44 | *
45 | *
This vulnerability can occur when the application receives an external YAML file as input and
46 | * attempts to load it.
47 | *
48 | *
Note: Versions of SnakeYAML prior to 1.26 are susceptible to this vulnerability. Starting from
49 | * version 1.26, this type of attack is prevented, as the library imposes a limit on the depth of
50 | * nested structures.
51 | *
52 | *
References:
53 | *
54 | *
59 | */
60 | public class YamlBombDeserializer {
61 |
62 | private static final String CURRENT_DIR = System.getProperty("user.dir", ".");
63 | private static final String CLASS_FILENAME =
64 | CURRENT_DIR + "/serialization-deserialization/src/main/resources/yaml_bomb.yaml";
65 |
66 | public static void main(String[] args) throws IOException {
67 | System.out.printf("*** Deserialization ***%n");
68 | try (InputStream inputStream = new BufferedInputStream(new FileInputStream(CLASS_FILENAME))) {
69 | final Yaml yaml = new Yaml();
70 | final Map data = yaml.load(inputStream);
71 | final List users = (List) data.get("user");
72 | System.out.printf("Successfully deserialized from [%s]%n", CLASS_FILENAME);
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/pizza-cooking-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/cooking/service/controller/CookingController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.cooking.service.controller;
30 |
31 | import static org.springframework.http.HttpStatus.CREATED;
32 |
33 | import io.swagger.v3.oas.annotations.Parameter;
34 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.PizzaApi;
35 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderDto;
36 | import ionutbalosin.training.application.security.practices.pizza.cooking.service.service.CookingService;
37 | import java.util.stream.Collectors;
38 | import org.slf4j.Logger;
39 | import org.slf4j.LoggerFactory;
40 | import org.springframework.http.ResponseEntity;
41 | import org.springframework.security.access.prepost.PreAuthorize;
42 | import org.springframework.stereotype.Controller;
43 | import org.springframework.web.bind.annotation.RequestBody;
44 | import org.springframework.web.bind.annotation.RequestHeader;
45 |
46 | @Controller
47 | public class CookingController implements PizzaApi {
48 |
49 | private static final Logger LOGGER = LoggerFactory.getLogger(CookingController.class);
50 |
51 | private final CookingService cookingService;
52 |
53 | public CookingController(CookingService cookingService) {
54 | this.cookingService = cookingService;
55 | }
56 |
57 | @Override
58 | @PreAuthorize("hasAuthority('demo_user_role')")
59 | public ResponseEntity pizzaCookingOrdersPost(
60 | @Parameter(name = "Authorization") @RequestHeader String authorization,
61 | @RequestBody PizzaCookingOrderDto pizzaCookingOrderDto) {
62 | LOGGER.info(
63 | "pizzaCookingOrdersPost(pizzaCookingOrder = '{}')",
64 | formatPizzaCookingOrderDto(pizzaCookingOrderDto));
65 |
66 | cookingService.pizzaCookingOrdersPost(pizzaCookingOrderDto);
67 | return new ResponseEntity<>(CREATED);
68 | }
69 |
70 | private String formatPizzaCookingOrderDto(PizzaCookingOrderDto pizzaCookingOrderDto) {
71 | return pizzaCookingOrderDto.getOrders().stream()
72 | .map(
73 | cookingOrderDto ->
74 | String.format("%s: %d", cookingOrderDto.getName(), cookingOrderDto.getQuantity()))
75 | .collect(Collectors.joining(", "));
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/security-token-jwks/src/main/java/ionutbalosin/training/application/security/practices/jwks/JwksSecurityConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.jwks;
30 |
31 | import org.springframework.context.annotation.Bean;
32 | import org.springframework.context.annotation.Configuration;
33 | import org.springframework.http.HttpMethod;
34 | import org.springframework.security.config.Customizer;
35 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
36 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
37 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
38 | import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
39 | import org.springframework.security.web.SecurityFilterChain;
40 |
41 | @Configuration
42 | @EnableMethodSecurity
43 | @EnableWebSecurity
44 | public class JwksSecurityConfiguration {
45 |
46 | private static final String PERMIT_PUBLIC_URL_PATTERN = "/public/**";
47 |
48 | @Bean
49 | public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
50 | http.csrf(csrf -> csrf.ignoringRequestMatchers(PERMIT_PUBLIC_URL_PATTERN));
51 | http.authorizeHttpRequests(
52 | authorize ->
53 | authorize
54 | // Allow preflight requests (OPTIONS) without authentication
55 | .requestMatchers(HttpMethod.OPTIONS)
56 | .permitAll()
57 | // Allow public endpoints without being authorized
58 | .requestMatchers(PERMIT_PUBLIC_URL_PATTERN)
59 | .permitAll()
60 | // Require authentication for all other requests
61 | .anyRequest()
62 | .authenticated())
63 | .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
64 |
65 | return http.build();
66 | }
67 |
68 | @Bean
69 | public JwtAuthenticationConverter jwtAuthenticationConverter() {
70 | final JwtConverter grantedAuthoritiesConverter = new JwtConverter();
71 |
72 | final JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
73 | jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
74 | return jwtAuthenticationConverter;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/pizza-delivery-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/delivery/service/client/OrderClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.delivery.service.client;
30 |
31 | import io.swagger.v3.oas.annotations.Parameter;
32 | import io.swagger.v3.oas.annotations.enums.ParameterIn;
33 | import ionutbalosin.training.application.security.practices.feign.logger.enricher.FeignConfiguration;
34 | import ionutbalosin.training.application.security.practices.pizza.order.api.model.PizzaOrderUpdatedStatusDto;
35 | import jakarta.validation.Valid;
36 | import jakarta.validation.constraints.NotNull;
37 | import java.util.UUID;
38 | import org.springframework.cloud.openfeign.FeignClient;
39 | import org.springframework.http.ResponseEntity;
40 | import org.springframework.web.bind.annotation.PathVariable;
41 | import org.springframework.web.bind.annotation.RequestBody;
42 | import org.springframework.web.bind.annotation.RequestHeader;
43 | import org.springframework.web.bind.annotation.RequestMapping;
44 | import org.springframework.web.bind.annotation.RequestMethod;
45 |
46 | @FeignClient(
47 | name = "${pizza-order-service.name}",
48 | url = "${pizza-order-service-endpoint.url}",
49 | configuration = FeignConfiguration.class)
50 | public interface OrderClient {
51 |
52 | @RequestMapping(
53 | method = RequestMethod.PUT,
54 | value = "/pizza/orders/{orderId}",
55 | consumes = {"application/json"})
56 | ResponseEntity pizzaOrdersOrderIdPut(
57 | @NotNull
58 | @Parameter(
59 | name = "Authorization",
60 | description = "JWT Authorizing token to allow access to the endpoint",
61 | required = true,
62 | in = ParameterIn.HEADER)
63 | @RequestHeader(value = "Authorization", required = true)
64 | String authorization,
65 | @Parameter(
66 | name = "orderId",
67 | description = "The order identifier",
68 | required = true,
69 | in = ParameterIn.PATH)
70 | @PathVariable("orderId")
71 | UUID orderId,
72 | @Parameter(
73 | name = "PizzaOrderUpdatedStatusDto",
74 | description = "Pizza order processing status",
75 | required = true)
76 | @Valid
77 | @RequestBody
78 | PizzaOrderUpdatedStatusDto pizzaOrderUpdatedStatusDto);
79 | }
80 |
--------------------------------------------------------------------------------
/pizza-cooking-service/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | ionutbalosin.training.application.security.practices
8 | application-security-practices
9 | 0.0.1-SNAPSHOT
10 |
11 |
12 | Pizza Cooking Service
13 | pizza-cooking-service
14 |
15 |
16 |
17 | ionutbalosin.training.application.security.practices
18 | pizza-cooking-api
19 | 0.0.1-SNAPSHOT
20 |
21 |
22 | ionutbalosin.training.application.security.practices
23 | pizza-delivery-api
24 | 0.0.1-SNAPSHOT
25 |
26 |
27 | ionutbalosin.training.application.security.practices
28 | security-slf4j-logger-enricher
29 | 0.0.1-SNAPSHOT
30 |
31 |
32 | ionutbalosin.training.application.security.practices
33 | security-feign-logger-enricher
34 | 0.0.1-SNAPSHOT
35 |
36 |
37 | ionutbalosin.training.application.security.practices
38 | security-token-jwks
39 | 0.0.1-SNAPSHOT
40 |
41 |
42 | ionutbalosin.training.application.security.practices
43 | security-token-client-credentials-fetcher
44 | 0.0.1-SNAPSHOT
45 |
46 |
47 | org.springframework.boot
48 | spring-boot-starter-web
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-starter-oauth2-resource-server
53 |
54 |
55 | org.springframework.cloud
56 | spring-cloud-starter-openfeign
57 |
58 |
59 | org.springframework.boot
60 | spring-boot-starter-cache
61 |
62 |
63 | org.springdoc
64 | springdoc-openapi-starter-webmvc-ui
65 |
66 |
67 | com.squareup.okhttp3
68 | okhttp
69 |
70 |
71 | com.github.spotbugs
72 | spotbugs-annotations
73 |
74 |
75 |
76 |
77 |
78 |
79 | org.springframework.boot
80 | spring-boot-maven-plugin
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/pizza-order-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | ionutbalosin.training.application.security.practices
8 | application-security-practices
9 | 0.0.1-SNAPSHOT
10 |
11 |
12 | Pizza Order Api
13 | pizza-order-api
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-validation
23 |
24 |
25 | io.swagger.core.v3
26 | swagger-annotations
27 |
28 |
29 | io.swagger.core.v3
30 | swagger-models
31 |
32 |
33 | org.openapitools
34 | jackson-databind-nullable
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | org.openapitools
43 | openapi-generator-maven-plugin
44 | ${openapi.maven.plugin.version}
45 |
46 |
47 |
48 | generate
49 |
50 |
51 | ${project.basedir}/src/main/resources/service-api.yaml
52 | spring
53 | spring-boot
54 | true
55 | false
56 | false
57 | true
58 | false
59 | false
60 | true
61 |
62 | true
63 | true
64 | true
65 | true
66 | true
67 | ionutbalosin.training.application.security.practices.pizza.order.api
68 | ionutbalosin.training.application.security.practices.pizza.order.api.model
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/pizza-cooking-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | ionutbalosin.training.application.security.practices
8 | application-security-practices
9 | 0.0.1-SNAPSHOT
10 |
11 |
12 | Pizza Cooking Api
13 | pizza-cooking-api
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-validation
23 |
24 |
25 | io.swagger.core.v3
26 | swagger-annotations
27 |
28 |
29 | io.swagger.core.v3
30 | swagger-models
31 |
32 |
33 | org.openapitools
34 | jackson-databind-nullable
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | org.openapitools
43 | openapi-generator-maven-plugin
44 | ${openapi.maven.plugin.version}
45 |
46 |
47 |
48 | generate
49 |
50 |
51 | ${project.basedir}/src/main/resources/service-api.yaml
52 | spring
53 | spring-boot
54 | true
55 | false
56 | false
57 | true
58 | false
59 | false
60 | true
61 |
62 | true
63 | true
64 | true
65 | true
66 | true
67 | ionutbalosin.training.application.security.practices.pizza.cooking.api
68 | ionutbalosin.training.application.security.practices.pizza.cooking.api.model
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/pizza-delivery-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | ionutbalosin.training.application.security.practices
8 | application-security-practices
9 | 0.0.1-SNAPSHOT
10 |
11 |
12 | Pizza Delivery Api
13 | pizza-delivery-api
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-validation
23 |
24 |
25 | io.swagger.core.v3
26 | swagger-annotations
27 |
28 |
29 | io.swagger.core.v3
30 | swagger-models
31 |
32 |
33 | org.openapitools
34 | jackson-databind-nullable
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | org.openapitools
43 | openapi-generator-maven-plugin
44 | ${openapi.maven.plugin.version}
45 |
46 |
47 |
48 | generate
49 |
50 |
51 | ${project.basedir}/src/main/resources/service-api.yaml
52 | spring
53 | spring-boot
54 | true
55 | false
56 | false
57 | true
58 | false
59 | false
60 | true
61 |
62 | true
63 | true
64 | true
65 | true
66 | true
67 | ionutbalosin.training.application.security.practices.pizza.delivery.api
68 | ionutbalosin.training.application.security.practices.pizza.delivery.api.model
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/java/ionutbalosin/training/application/security/practices/encryption/decryption/asymetric/MessageEncryptDecrypt.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.encryption.decryption.asymetric;
30 |
31 | import java.security.KeyPair;
32 | import java.security.KeyPairGenerator;
33 | import java.security.PrivateKey;
34 | import java.security.PublicKey;
35 | import java.util.Base64;
36 | import javax.crypto.Cipher;
37 |
38 | /**
39 | * This class, demonstrates encrypting and decrypting a short message using RSA encryption. It
40 | * generates an RSA key pair, encrypts a message with the public key, and decrypts it with the
41 | * private key. The encrypted message is encoded in Base64 for readability.
42 | */
43 | public class MessageEncryptDecrypt {
44 |
45 | private static final byte[] SECRET_MESSAGE =
46 | "Top secret: The universe’s best coffee recipe is hidden here... but first, decrypt me!"
47 | .getBytes();
48 |
49 | public static void main(String[] args) throws Exception {
50 | // Generate a key pair for encryption
51 | final KeyPair keyPair = generateKeyPair();
52 | final PrivateKey privateKey = keyPair.getPrivate();
53 | final PublicKey publicKey = keyPair.getPublic();
54 |
55 | // Encrypt the message using the generated public key
56 | final byte[] encryptedMessage = encryptMessage(SECRET_MESSAGE, publicKey);
57 | System.out.printf(
58 | "Generated base64 encoded encrypted message [%s]%n",
59 | Base64.getEncoder().encodeToString(encryptedMessage));
60 |
61 | // Decrypt the message using the generated private key
62 | final byte[] decryptedMessage = decryptMessage(encryptedMessage, privateKey);
63 | System.out.printf("Decrypted message: [%s]%n", new String(decryptedMessage));
64 | }
65 |
66 | private static KeyPair generateKeyPair() throws Exception {
67 | // Generate RSA key pair with 2048-bit key size
68 | final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
69 | keyPairGenerator.initialize(2048);
70 | return keyPairGenerator.generateKeyPair();
71 | }
72 |
73 | private static byte[] encryptMessage(byte[] message, PublicKey publicKey) throws Exception {
74 | // Note: RSA can only encrypt data smaller than the key size minus padding overhead.
75 | // For a 2048-bit key and PKCS1 padding, this is generally less than 254 bytes.
76 | final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
77 | cipher.init(Cipher.ENCRYPT_MODE, publicKey);
78 |
79 | return cipher.doFinal(message);
80 | }
81 |
82 | private static byte[] decryptMessage(byte[] encryptedMessage, PrivateKey privateKey)
83 | throws Exception {
84 | final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
85 | cipher.init(Cipher.DECRYPT_MODE, privateKey);
86 |
87 | return cipher.doFinal(encryptedMessage);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/serialization-deserialization/src/main/java/ionutbalosin/training/application/security/practices/serialization/deserialization/clazz/MaliciousClazz.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.serialization.deserialization.clazz;
30 |
31 | import java.io.IOException;
32 | import java.io.ObjectInputStream;
33 |
34 | /**
35 | * This malicious class (typically is created by the attacker) demonstrates a deserialization
36 | * vulnerability by launching the system calculator upon deserialization. The consequences could be
37 | * more severe on a real system.
38 | *
39 | * This class overrides the readObject method, which plays a key role in Java deserialization,
40 | * making it a common target for deserialization attacks.
41 | *
42 | *
Why the readObject method can be dangerous:
43 | *
44 | *
45 | * - Custom Deserialization Logic: The readObject method allows you to provide custom
46 | * logic during deserialization. If this method is overridden, it can execute any code,
47 | * including unsafe operations like invoking system commands.
48 | *
- Implicit Invocation: When deserializing an object via
49 | * ObjectInputStream.readObject(), the readObject method in the target class is called
50 | * automatically, without explicit invocation. If an attacker can control the serialized data,
51 | * they can trigger arbitrary behavior inside this method during deserialization.
52 | *
- No Input Validation: If the input data comes from an untrusted source, malicious
53 | * serialized data can force the readObject method to run arbitrary code, leading to serious
54 | * security issues like Remote Code Execution (RCE).
55 | *
56 | */
57 | public class MaliciousClazz extends TrustedClazz {
58 |
59 | private static final long serialVersionUID = 1L;
60 |
61 | public MaliciousClazz() {
62 | System.out.printf("A malicious class constructor has been invoked.%n");
63 | }
64 |
65 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
66 | ois.defaultReadObject();
67 |
68 | final String os = System.getProperty("os.name").toLowerCase();
69 | final String[] cmd;
70 |
71 | // Determine the appropriate command to launch the calculator based on the OS
72 | if (os.contains("win")) {
73 | cmd = new String[] {"cmd.exe", "/c", "calc"};
74 | } else if (os.contains("mac")) {
75 | cmd = new String[] {"/bin/sh", "-c", "open -a Calculator"};
76 | } else if (os.contains("nix") || os.contains("nux")) {
77 | cmd = new String[] {"/bin/sh", "-c", "gnome-calculator"};
78 | } else {
79 | throw new UnsupportedOperationException("Unsupported operating system: " + os);
80 | }
81 |
82 | System.out.printf("Malicious class launches the Calculator application on [%s]%n", os);
83 | Runtime.getRuntime().exec(cmd);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/java/ionutbalosin/training/application/security/practices/encryption/decryption/asymetric/DigitalSignatureVerifier.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.encryption.decryption.asymetric;
30 |
31 | import java.security.KeyPair;
32 | import java.security.KeyPairGenerator;
33 | import java.security.PrivateKey;
34 | import java.security.PublicKey;
35 | import java.security.Signature;
36 | import java.util.Base64;
37 |
38 | /**
39 | * This class demonstrates the use of digital signatures to ensure non-repudiation, message
40 | * integrity, and authenticity. By generating an RSA key pair, signing a secret message with the
41 | * private key, and verifying the signature with the corresponding public key, this class highlights
42 | * how cryptographic techniques can prevent denial of the message's origin and guarantee that the
43 | * message has not been altered.
44 | */
45 | public class DigitalSignatureVerifier {
46 |
47 | private static final byte[] SECRET_MESSAGE =
48 | "Top secret: The universe’s best coffee recipe is hidden here... but first, decrypt me!"
49 | .getBytes();
50 |
51 | public static void main(String[] args) throws Exception {
52 | // Generate a key pair for encryption
53 | final KeyPair keyPair = generateKeyPair();
54 | final PrivateKey privateKey = keyPair.getPrivate();
55 | final PublicKey publicKey = keyPair.getPublic();
56 |
57 | // Sign the data using the private key
58 | final byte[] digitalSignature = signData(SECRET_MESSAGE, privateKey);
59 | System.out.printf(
60 | "Generated digital signature [%s]%n", Base64.getEncoder().encodeToString(digitalSignature));
61 |
62 | // Verify the digital signature using the public key
63 | final boolean isVerified = verifySignature(SECRET_MESSAGE, digitalSignature, publicKey);
64 | System.out.printf("Is signature verified: [%s]%n", isVerified);
65 | }
66 |
67 | // Method to generate an RSA key pair
68 | private static KeyPair generateKeyPair() throws Exception {
69 | // Generate RSA key pair with 2048-bit key size
70 | final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
71 | keyGen.initialize(2048);
72 |
73 | return keyGen.generateKeyPair();
74 | }
75 |
76 | // Method to sign data using a private key
77 | private static byte[] signData(byte[] data, PrivateKey privateKey) throws Exception {
78 | final Signature signature = Signature.getInstance("SHA256withRSA");
79 | signature.initSign(privateKey);
80 | signature.update(data);
81 |
82 | return signature.sign();
83 | }
84 |
85 | // Method to verify a digital signature using a public key
86 | private static boolean verifySignature(byte[] data, byte[] digitalSignature, PublicKey publicKey)
87 | throws Exception {
88 | final Signature signatureVerify = Signature.getInstance("SHA256withRSA");
89 | signatureVerify.initVerify(publicKey);
90 | signatureVerify.update(data);
91 |
92 | return signatureVerify.verify(digitalSignature);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/java/ionutbalosin/training/application/security/practices/encryption/decryption/symetric/FileDecryption.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.encryption.decryption.symetric;
30 |
31 | import java.io.*;
32 | import javax.crypto.Cipher;
33 | import javax.crypto.CipherInputStream;
34 | import javax.crypto.SecretKey;
35 | import javax.crypto.spec.GCMParameterSpec;
36 |
37 | /**
38 | * This class handles the decryption of an encrypted file using AES-256 encryption with GCM mode. It
39 | * loads a saved secret key and initialization vector (IV), decrypts the file, and stores the
40 | * decrypted content.
41 | */
42 | public class FileDecryption {
43 |
44 | private static final String CURRENT_DIR = System.getProperty("user.dir", ".");
45 | private static final String ENCRYPTED_FILENAME =
46 | CURRENT_DIR + "/encryption-decryption/target/encrypted_file_aes.txt";
47 | private static final String DECRYPTED_FILENAME =
48 | CURRENT_DIR + "/encryption-decryption/target/decrypted_file_aes.txt";
49 | private static final String SECRET_KEY_FILENAME =
50 | CURRENT_DIR + "/encryption-decryption/target/secret_aes.key";
51 | private static final String IV_FILENAME =
52 | CURRENT_DIR + "/encryption-decryption/target/iv_aes.key";
53 |
54 | public static void main(String[] args) throws Exception {
55 | // Load the secret key and IV from the files for decryption
56 | final SecretKey secretKey = loadKey();
57 | final byte[] iv = loadIv();
58 |
59 | // Decrypt the encrypted file using the loaded secret key and IV
60 | decryptFile(ENCRYPTED_FILENAME, DECRYPTED_FILENAME, secretKey, iv);
61 | System.out.printf("File successfully decrypted to [%s]%n", DECRYPTED_FILENAME);
62 | }
63 |
64 | private static SecretKey loadKey() throws Exception {
65 | try (final ObjectInputStream keyIn =
66 | new ObjectInputStream(new FileInputStream(SECRET_KEY_FILENAME))) {
67 | return (SecretKey) keyIn.readObject();
68 | }
69 | }
70 |
71 | private static byte[] loadIv() throws Exception {
72 | try (FileInputStream ivIn = new FileInputStream(IV_FILENAME)) {
73 | return ivIn.readAllBytes();
74 | }
75 | }
76 |
77 | private static void decryptFile(
78 | String encryptedFilePath, String decryptedFilePath, SecretKey secretKey, byte[] iv)
79 | throws Exception {
80 | final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
81 | cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
82 |
83 | try (final FileInputStream fileInputStream = new FileInputStream(encryptedFilePath);
84 | final FileOutputStream fileOutputStream = new FileOutputStream(decryptedFilePath);
85 | final CipherInputStream cipherInputStream =
86 | new CipherInputStream(fileInputStream, cipher)) {
87 |
88 | final byte[] buffer = new byte[1024];
89 | int bytesRead;
90 | while ((bytesRead = cipherInputStream.read(buffer)) != -1) {
91 | fileOutputStream.write(buffer, 0, bytesRead);
92 | }
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/encryption-decryption/src/main/java/ionutbalosin/training/application/security/practices/encryption/decryption/hashing/PasswordHashing.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.encryption.decryption.hashing;
30 |
31 | import java.security.SecureRandom;
32 | import java.util.Base64;
33 | import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
34 | import org.bouncycastle.crypto.params.Argon2Parameters;
35 |
36 | /**
37 | * This class, Argon2Hashing, demonstrates how to generate a secure hash using the Argon2 algorithm.
38 | * It includes methods to generate a random salt and to hash a password with specified parameters
39 | * such as iterations, memory cost, parallelism, and hash length. The main method prints the
40 | * generated salt and hash in base64 encoding.
41 | */
42 | public class PasswordHashing {
43 |
44 | private static final String PASSWORD = "My-Super-Secret-Password";
45 | private static final int SALT_LENGTH = 16; // Length of the salt in bytes
46 | private static final int ITERATIONS = 3; // Number of iterations
47 | private static final int MEMORY = 65536; // Memory cost (in KB)
48 | private static final int PARALLELISM = 1; // Degree of parallelism
49 | private static final int HASH_LENGTH = 64; // Length of the hash in bytes
50 |
51 | public static void main(String[] args) {
52 | final byte[] salt = generateSalt(SALT_LENGTH);
53 | final byte[] hash = hashPassword(PASSWORD, salt, ITERATIONS, MEMORY, PARALLELISM, HASH_LENGTH);
54 |
55 | System.out.printf(
56 | "Generated base64 encoded salt [%s]%n", Base64.getEncoder().encodeToString(salt));
57 | System.out.printf(
58 | "Generated base64 encoded hash [%s]%n", Base64.getEncoder().encodeToString(hash));
59 | }
60 |
61 | /**
62 | * Generates a random salt of the specified length.
63 | *
64 | * The salt is used in hashing functions to ensure that even if two users have the same
65 | * password, their hashes will be unique. This helps protect against precomputed attacks such as
66 | * rainbow table attacks.
67 | */
68 | private static byte[] generateSalt(int length) {
69 | final SecureRandom random = new SecureRandom();
70 | final byte[] salt = new byte[length];
71 | random.nextBytes(salt);
72 | return salt;
73 | }
74 |
75 | private static byte[] hashPassword(
76 | String password, byte[] salt, int iterations, int memory, int parallelism, int hashLength) {
77 | final Argon2Parameters.Builder builder =
78 | new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
79 | .withSalt(salt) // Salt ensures unique hashes for identical passwords
80 | .withIterations(iterations) // Iterations increase computational cost for added security
81 | .withMemoryAsKB(memory) // Memory cost helps resist GPU-based attacks
82 | .withParallelism(parallelism);
83 |
84 | final Argon2BytesGenerator generator = new Argon2BytesGenerator();
85 | generator.init(builder.build());
86 |
87 | final byte[] hash = new byte[hashLength];
88 | generator.generateBytes(password.toCharArray(), hash);
89 |
90 | return hash;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/zap/zap-api-scan-rules.conf:
--------------------------------------------------------------------------------
1 | # zap-api-scan rule configuration file
2 | # Change WARN to IGNORE to ignore rule or FAIL to fail if rule matches
3 | # Active scan rules set to IGNORE will not be run which will speed up the scan
4 | # Only the rule identifiers are used - the names are just for info
5 | # You can add your own messages to each rule by appending them after a tab on each line.
6 | # See https://www.zaproxy.org/docs/docker/api-scan/
7 | 0 WARN (Directory Browsing - Active/release)
8 | 10010 WARN (Cookie No HttpOnly Flag - Passive/release)
9 | 10011 WARN (Cookie Without Secure Flag - Passive/release)
10 | 10012 WARN (Password Autocomplete in Browser - Passive/release)
11 | 10015 WARN (Incomplete or No Cache-control and Pragma HTTP Header Set - Passive/release)
12 | 10016 WARN (Web Browser XSS Protection Not Enabled - Passive/release)
13 | 10017 WARN (Cross-Domain JavaScript Source File Inclusion - Passive/release)
14 | 10019 WARN (Content-Type Header Missing - Passive/release)
15 | 10020 WARN (X-Frame-Options Header Scanner - Passive/release)
16 | 10021 WARN (X-Content-Type-Options Header Missing - Passive/release)
17 | 10023 WARN (Information Disclosure - Debug Error Messages - Passive/beta)
18 | 10024 WARN (Information Disclosure - Sensitive Informations in URL - Passive/beta)
19 | 10025 WARN (Information Disclosure - Sensitive Information in HTTP Referrer Header - Passive/beta)
20 | 10026 WARN (HTTP Parameter Override - Passive/beta)
21 | 10027 WARN (Information Disclosure - Suspicious Comments - Passive/beta)
22 | 10032 WARN (Viewstate Scanner - Passive/beta)
23 | 10040 WARN (Secure Pages Include Mixed Content - Passive/release)
24 | 10045 WARN (Source Code Disclosure - /WEB-INF folder - Active/beta)
25 | 10048 WARN (Remote Code Execution - Shell Shock - Active/beta)
26 | 10095 WARN (Backup File Disclosure - Active/beta)
27 | 10105 WARN (Weak Authentication Method - Passive/beta)
28 | 10202 WARN (Absence of Anti-CSRF Tokens - Passive/beta)
29 | 2 WARN (Private IP Disclosure - Passive/release)
30 | 20012 WARN (Anti CSRF Tokens Scanner - Active/beta)
31 | 20014 WARN (HTTP Parameter Pollution scanner - Active/beta)
32 | 20015 WARN (Heartbleed OpenSSL Vulnerability - Active/beta)
33 | 20016 WARN (Cross-Domain Misconfiguration - Active/beta)
34 | 20017 WARN (Source Code Disclosure - CVE-2012-1823 - Active/beta)
35 | 20018 WARN (Remote Code Execution - CVE-2012-1823 - Active/beta)
36 | 20019 WARN (External Redirect - Active/release)
37 | 3 WARN (Session ID in URL Rewrite - Passive/release)
38 | 30001 WARN (Buffer Overflow - Active/release)
39 | 30002 WARN (Format String Error - Active/release)
40 | 30003 WARN (Integer Overflow Error - Active/beta)
41 | 40003 WARN (CRLF Injection - Active/release)
42 | 40008 WARN (Parameter Tampering - Active/release)
43 | 40009 WARN (Server Side Include - Active/release)
44 | 40012 WARN (Cross Site Scripting (Reflected) - Active/release)
45 | 40013 WARN (Session Fixation - Active/beta)
46 | 40014 WARN (Cross Site Scripting (Persistent) - Active/release)
47 | 40016 WARN (Cross Site Scripting (Persistent) - Prime - Active/release)
48 | 40017 WARN (Cross Site Scripting (Persistent) - Spider - Active/release)
49 | 40018 WARN (SQL Injection - Active/release)
50 | 40019 WARN (SQL Injection - MySQL - Active/beta)
51 | 40020 WARN (SQL Injection - Hypersonic SQL - Active/beta)
52 | 40021 WARN (SQL Injection - Oracle - Active/beta)
53 | 40022 WARN (SQL Injection - PostgreSQL - Active/beta)
54 | 40023 WARN (Possible Username Enumeration - Active/beta)
55 | 42 WARN (Source Code Disclosure - SVN - Active/beta)
56 | 50000 WARN (Script Active Scan Rules - Active/release)
57 | 50001 WARN (Script Passive Scan Rules - Passive/release)
58 | 6 WARN (Path Traversal - Active/release)
59 | 7 WARN (Remote File Inclusion - Active/release)
60 | 90001 WARN (Insecure JSF ViewState - Passive/beta)
61 | 90011 WARN (Charset Mismatch - Passive/beta)
62 | 90019 WARN (Server Side Code Injection - Active/release)
63 | 90020 WARN (Remote OS Command Injection - Active/release)
64 | 90021 WARN (XPath Injection - Active/beta)
65 | 90022 WARN (Application Error Disclosure - Passive/release)
66 | 90023 WARN (XML External Entity Attack - Active/beta)
67 | 90024 WARN (Generic Padding Oracle - Active/beta)
68 | 90025 WARN (Expression Language Injection - Active/beta)
69 | 90026 WARN (SOAP Action Spoofing - Active/alpha)
70 | 90028 WARN (Insecure HTTP Method - Active/beta)
71 | 90029 WARN (SOAP XML Injection - Active/alpha)
72 | 90030 WARN (WSDL File Passive Scanner - Passive/alpha)
73 | 90033 WARN (Loosely Scoped Cookie - Passive/beta)
--------------------------------------------------------------------------------
/99-PRACTICE-Security-General-Quiz.md:
--------------------------------------------------------------------------------
1 | # Application Security for Java Developers
2 |
3 | Copyright (C) 2025 Ionut Balosin
4 |
5 | This project is licensed under the Apache License, Version 2.0.
6 | Please see the [LICENSE](license/LICENSE) file for full license.
7 |
8 | ---
9 |
10 | ### 1. Email Account Verification
11 |
12 | You receive the following email from your bank, called Penny Bank and registered at [penny-bank.com](http://penny-bank.com), where you hold an account:
13 |
14 | ```html
15 | FROM: support@penny-bank.com
16 | SUBJECT: URGENT: Action Required to Avoid Account Suspension
17 |
18 | Dear Valued Customer,
19 |
20 | We have detected suspicious activity on your account and have temporarily restricted access. To restore full access, you must verify your identity immediately.
21 |
22 | Click the link below to confirm your email and restore your account:
23 | [Verify Your Account](https://www.penny-bank-customer-verification.com)
24 |
25 | Failure to act within 24 hours will result in permanent suspension.
26 |
27 | Thank you for your prompt attention.
28 |
29 | Sincerely,
30 | Support Team
31 | Penny Bank
32 | ```
33 |
34 | ❓ **Question**: How should you behave?
35 |
36 | 1. Simply mark the email as spam.
37 | 2. Do not act on this email but rather call the bank directly to ask what is happening.
38 | 3. Click the link and proceed further.
39 |
40 | ---
41 |
42 | ### 2. Suspicious Transaction Phone Call
43 |
44 | You receive a phone call from a lady claiming to be from the payment supervision department of Penny Bank, where you hold both a credit card and an account.
45 | The caller ID on your smartphone displays Penny Bank's name, so it looks legitimate.
46 |
47 | The lady informs you that she noticed an unusual transaction from your account, and as a result, your account has been temporarily locked.
48 |
49 | She then asks you to confirm your identity, informs you that you will shortly receive an email, and requests that you click the activation link in the email to reactivate your account.
50 |
51 | ❓ **Question**: What should you do?
52 |
53 | 1. Confirm your identity and click the link in the email to reactivate your account, since the call appears to be from Penny Bank.
54 | 2. Hang up and call Penny Bank directly using the number from the official website or the back of your credit card.
55 | 3. Tell them you will visit a local branch to resolve the issue.
56 |
57 | ---
58 |
59 | ### 3. Failed Delivery Notification Text Message
60 |
61 | You receive a text message saying:
62 |
63 | ```html
64 | Your delivery package number #1234 failed. Click this link to change the delivery contact information:
65 | https://www.amazon-package-delivery.com/update-contact/1234.
66 |
67 | If you do not provide an update within 24 hours, the delivery will be rejected.
68 | ```
69 |
70 | The sender's number is `+43664111111111`, and it appears to be from Amazon.
71 |
72 | ❓ **Question**: What is the best action to take?
73 |
74 | 1. Click the link to update the data and resolve the issue.
75 | 2. Call the number +43664111111111 from the text message to ask for more details.
76 | 3. Check the Amazon website for the official customer service phone number and call that to ask for more details.
77 | 4. Ignore the message and delete it, since you are not expecting any package.
78 |
79 | ---
80 |
81 | ### 4. Legitimate URL
82 |
83 | ❓ **Question**: Which of the following URLs is legitimate?
84 |
85 | 1. https://secure.apple-id.com/login
86 | 2. https://login.authenticate.paypal-user.com
87 | 3. https://support.amazon-center.com/help
88 | 4. https://help.netflix-security.com
89 | 5. https://support.apple.com/account
90 |
91 | ---
92 |
93 | ### 5. A Friend is Asking for Money
94 |
95 | You receive a message in a social media chat from your friend, Martin Mustermann, asking you to send him money. He tells you he is on a private holiday, but his wallet with his credit card and phone were stolen. He says he is using a computer in the hotel lobby and urgently needs around 5,000 EUR to check out, pay the hotel fees, and return home. He promises to return the money once he’s back in a few days. He urges you to help him, saying he’s desperate, and asks that you not tell his parents.
96 |
97 | ❓ **Question**: What is the best course of action to take?
98 |
99 | 1. Click the transfer link from the chat and send him the money.
100 | 2. Contact your friend via a different method (e.g., a video chat) to verify his identity and confirm the situation.
101 | 3. Ignore the message, as it is likely a joke or scam. If not, your friend should ask someone else for help.
--------------------------------------------------------------------------------
/02-PRACTICE-Authentication-and-Authorization.md:
--------------------------------------------------------------------------------
1 | # Application Security for Java Developers
2 |
3 | Copyright (C) 2025 Ionut Balosin
4 |
5 | This project is licensed under the Apache License, Version 2.0.
6 | Please see the [LICENSE](license/LICENSE) file for full license.
7 |
8 | ---
9 |
10 | ## Authentication and Authorization
11 |
12 | ### 🕵️♂️ Identify the Appropriate Flows
13 |
14 | > ⏰ 60 minutes
15 |
16 | > 👨🎓 Attendees' Exercise
17 |
18 | Consider the software architecture diagram below, explicitly designed to be agnostic to any specific business application domain. However, since security is mission-critical, each component must be protected, and all communication between components must be both authenticated and authorized.
19 |
20 |
21 |
22 |
23 |
24 | The system is accessed by different types of clients or systems:
25 |
26 | - `External OIDC Users`: Public users authenticated through an external OIDC provider, such as Google or Facebook.
27 | - `Internal OIDC Users`: Public users authenticated via the organization’s internal Identity Provider (IdP).
28 | - `Employees`: Internal company users authenticated via the organization’s internal IdP.
29 | - `External/Internal Services`: Public external systems and internal services from other departments, authenticated via the organization’s internal IdP.
30 |
31 | The system is divided into three distinct layers:
32 |
33 | - `Public Clients Layer`: Publicly exposed APIs accessed by users or external systems, involving both the internal IdP and an external (i.e., third-party) IdP for authentication.
34 | - `Internal Core Services Layer`: Internal organizational services usually within the same company department, requiring secure service-to-service authentication and authorization.
35 | - `Internal Non-core Services Layer`: Auxiliary, non-core systems usually belonging to other departments within the organization, also requiring service-to-service authentication and authorization.
36 |
37 | Communication between clients and services is primarily based on synchronous HTTP RESTful API calls; however, in some cases, asynchronous communication is used, with events placed into queues.
38 |
39 | **Task:** Each red arrow, labeled with an index from `1` to `12`, links a specific client or service to the IdP.
40 | For each of these arrows, identify the most suitable OAuth 2.0 or OpenID Connect (OIDC) flow based on the interaction type and the client or service involved.
41 | Options may include, but are not limited to:
42 |
43 | - `OpenID Connect`
44 | - `Authorization Code Flow with PKCE`
45 | - `Client Credentials Flow`
46 | - `Password Flow`
47 | - `Implicit Flow`
48 | - `Token Introspection`
49 | - `JSON Web Key Set`
50 | - etc.
51 |
52 | **Note:** Other flows may be suitable too, so don't limit your choice to only the options listed above.
53 |
54 | ---
55 |
56 | ### 🏋️ Hands-On Demo
57 |
58 | > ⏰ 60 minutes
59 |
60 | > 👨💼 Conducted By Trainer
61 |
62 | **Note:** Please ensure that the Docker daemon is running; otherwise, the commands will not execute successfully.
63 |
64 | 1. Open a terminal and run the following command to bootstrap the `Keycloak` service:
65 |
66 | ```bash
67 | ./bootstrap-keycloak.sh
68 | ```
69 |
70 | 2. From another terminal, trigger the `Keycloak` initialization setup using the following command:
71 |
72 | ```bash
73 | ./keycloak-init.sh
74 | ```
75 |
76 | 3. Once everything has been started and properly initialized, open a browser and navigate to [http://localhost:9090](http://localhost:9090) to access the **Keycloak UI** (using the credentials `admin:admin`) and review the configuration.
77 |
78 | 4. As the next and final step, open `Postman`, import the [Postman collections](postman) and trigger the following IdP endpoints and OAuth 2.0 flows:
79 | - OpenID Connect configuration
80 | - `Client Credentials Flow`
81 | - `Password Flow`
82 | - `Implicit Flow` (using the credentials `demo_user:Test1234!`)
83 | - `Authorization Code Flow with PKCE` (using the credentials `demo_user:Test1234!`)
84 | - `Token Introspection`
85 | - `User Info`
86 |
87 |
88 | **Notes:**
89 | - Before requesting `Token Introspection` and `User Info`, ensure that the `{{token}}` variable is set in the environment variables.
90 | - Depending on the flow, not all types of tokens (e.g., identity, access, and refresh tokens) are returned.
91 | - To understand the structure of a JWT token, copy and paste it into [jwt.io](https://jwt.io) and examine its structure (e.g., header, payload, signature) and the specific claims like `exp`, `iat`, `iss`, `sub`, `typ`, `azp`, `roles`, `client_id`, etc.
92 |
--------------------------------------------------------------------------------
/pizza-cooking-service/src/main/java/ionutbalosin/training/application/security/practices/pizza/cooking/service/service/CookingService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.pizza.cooking.service.service;
30 |
31 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
32 | import ionutbalosin.training.application.security.practices.client.credentials.handler.IdpToken;
33 | import ionutbalosin.training.application.security.practices.client.credentials.handler.IdpTokenFetcher;
34 | import ionutbalosin.training.application.security.practices.pizza.cooking.api.model.PizzaCookingOrderDto;
35 | import ionutbalosin.training.application.security.practices.pizza.cooking.service.client.DeliveryClient;
36 | import ionutbalosin.training.application.security.practices.pizza.cooking.service.mapper.PizzaDeliveryOrderDtoMapper;
37 | import ionutbalosin.training.application.security.practices.pizza.delivery.api.model.PizzaDeliveryOrderDto;
38 | import java.time.Duration;
39 | import java.util.Random;
40 | import java.util.concurrent.Executor;
41 | import java.util.concurrent.ThreadLocalRandom;
42 | import org.slf4j.Logger;
43 | import org.slf4j.LoggerFactory;
44 | import org.springframework.beans.factory.annotation.Qualifier;
45 | import org.springframework.stereotype.Service;
46 |
47 | @Service
48 | public class CookingService {
49 |
50 | private static final Logger LOGGER = LoggerFactory.getLogger(CookingService.class);
51 |
52 | @SuppressFBWarnings("PREDICTABLE_RANDOM")
53 | private static final Random RANDOM = ThreadLocalRandom.current();
54 |
55 | private final IdpTokenFetcher tokenFetcher;
56 | private final PizzaDeliveryOrderDtoMapper dtoMapper;
57 | private final DeliveryClient deliveryClient;
58 | private final Executor taskExecutor;
59 |
60 | public CookingService(
61 | IdpTokenFetcher tokenFetcher,
62 | PizzaDeliveryOrderDtoMapper dtoMapper,
63 | DeliveryClient deliveryClient,
64 | @Qualifier("PizzaCookingExecutor") Executor taskExecutor) {
65 | this.tokenFetcher = tokenFetcher;
66 | this.dtoMapper = dtoMapper;
67 | this.deliveryClient = deliveryClient;
68 | this.taskExecutor = taskExecutor;
69 | }
70 |
71 | public void pizzaCookingOrdersPost(PizzaCookingOrderDto pizzaCookingOrderDto) {
72 | taskExecutor.execute(() -> schedulePizzaCooking(pizzaCookingOrderDto));
73 | LOGGER.info(
74 | "Pizza order '{}' has been successfully scheduled for cooking.",
75 | pizzaCookingOrderDto.getOrderId());
76 | }
77 |
78 | private void schedulePizzaCooking(PizzaCookingOrderDto pizzaCookingOrderDto) {
79 | try {
80 | // Simulate some cooking activity between 5 and 15 seconds
81 | Thread.sleep(Duration.ofSeconds(5 + RANDOM.nextInt(11)).toMillis());
82 | } catch (InterruptedException e) {
83 | // Swallow exception
84 | }
85 |
86 | LOGGER.info(
87 | "Pizza order '{}' has been successfully cooked.", pizzaCookingOrderDto.getOrderId());
88 | final IdpToken idpToken = fetchToken();
89 |
90 | // Notify delivery service with the order details once the pizza cooking is done
91 | final PizzaDeliveryOrderDto deliveryOrderDto = dtoMapper.map(pizzaCookingOrderDto);
92 | deliveryClient.pizzaDeliveryOrdersPost(
93 | "Bearer " + idpToken.getAccess_token(), deliveryOrderDto);
94 |
95 | LOGGER.info(
96 | "Pizza order '{}' has been successfully sent for delivery.", deliveryOrderDto.getOrderId());
97 | }
98 |
99 | private IdpToken fetchToken() {
100 | return tokenFetcher
101 | .fetchToken()
102 | .orElseThrow(() -> new RuntimeException("Unable to fetch the IdP token"));
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/security-token-client-credentials-fetcher/src/main/java/ionutbalosin/training/application/security/practices/client/credentials/handler/IdpTokenFetcher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.client.credentials.handler;
30 |
31 | import static ionutbalosin.training.application.security.practices.client.credentials.handler.util.JsonObjectMapper.deserialize;
32 | import static java.lang.String.format;
33 | import static java.util.Optional.empty;
34 | import static java.util.Optional.ofNullable;
35 |
36 | import java.io.IOException;
37 | import java.util.Base64;
38 | import java.util.Optional;
39 | import okhttp3.MediaType;
40 | import okhttp3.OkHttpClient;
41 | import okhttp3.Request;
42 | import okhttp3.RequestBody;
43 | import okhttp3.Response;
44 | import okhttp3.ResponseBody;
45 | import org.slf4j.Logger;
46 | import org.slf4j.LoggerFactory;
47 | import org.springframework.beans.factory.annotation.Value;
48 | import org.springframework.stereotype.Service;
49 |
50 | /**
51 | * This class fetches a new access token from the Identity Provider (IdP) using the client
52 | * credentials flow. The client credentials flow is typically used for machine-to-machine
53 | * communication. In this flow, it is recommended that a refresh token SHOULD NOT be issued.
54 | * Therefore, whenever a new token is required, a fresh authorization request must be made to the
55 | * IdP. Since the backend stores the client secret locally, it can easily request a new access token
56 | * without needing to rely on a refresh token.
57 | *
58 | * References:
59 | *
60 | *
63 | */
64 | @Service
65 | public class IdpTokenFetcher {
66 |
67 | private static final Logger LOG = LoggerFactory.getLogger(IdpTokenFetcher.class);
68 |
69 | private static final String AUTH = "Authorization";
70 | private static final String CONTENT_TYPE = "Content-Type";
71 | private static final String CONTENT_TYPE_JSON = "application/json";
72 | private static final String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded";
73 |
74 | @Value("${oidc.url}")
75 | private String idpUrl;
76 |
77 | @Value("${oidc.clientId}")
78 | private String clientId;
79 |
80 | @Value("${oidc.clientSecret}")
81 | private String clientSecret;
82 |
83 | public Optional fetchToken() {
84 | final String encodedHeader =
85 | new String(Base64.getEncoder().encode(format("%s:%s", clientId, clientSecret).getBytes()));
86 | final RequestBody body =
87 | RequestBody.create(
88 | format("grant_type=client_credentials&client_id=%s", clientId),
89 | MediaType.parse(CONTENT_TYPE_URLENCODED));
90 | final Request request =
91 | new Request.Builder()
92 | .url(idpUrl)
93 | .post(body)
94 | .addHeader(CONTENT_TYPE, CONTENT_TYPE_JSON)
95 | .addHeader(AUTH, format("Basic %s", encodedHeader))
96 | .build();
97 |
98 | try (Response response = new OkHttpClient().newCall(request).execute()) {
99 | final ResponseBody responseBody = response.body();
100 | if (responseBody == null) {
101 | throw new RuntimeException(
102 | "Identity Provider responded with an empty body. Unable to retrieve token.");
103 | }
104 | final IdpToken idpToken = deserialize(responseBody.string(), IdpToken.class);
105 | return ofNullable(idpToken);
106 | } catch (IOException exception) {
107 | LOG.error("IOException while retrieving authentication token: '{}'", exception.getMessage());
108 | return empty();
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/security-token-introspection/src/main/java/ionutbalosin/training/application/security/practices/token/introspection/OpaqueJwtIntrospector.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.token.introspection;
30 |
31 | import static java.util.Collections.emptySet;
32 | import static java.util.Optional.ofNullable;
33 | import static java.util.stream.Collectors.toList;
34 | import static java.util.stream.Collectors.toSet;
35 |
36 | import java.util.ArrayList;
37 | import java.util.Collection;
38 | import java.util.List;
39 | import java.util.Map;
40 | import org.slf4j.Logger;
41 | import org.slf4j.LoggerFactory;
42 | import org.springframework.security.core.GrantedAuthority;
43 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
44 | import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
45 | import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
46 | import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
47 | import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
48 |
49 | public class OpaqueJwtIntrospector implements OpaqueTokenIntrospector {
50 |
51 | private static final Logger LOG = LoggerFactory.getLogger(OpaqueJwtIntrospector.class);
52 |
53 | private static final String RESOURCE_ACCESS_CLAIM = "realm_access";
54 | private static final String ROLES_CLAIM = "roles";
55 | private final OpaqueTokenIntrospector delegate;
56 |
57 | public OpaqueJwtIntrospector(String introspectionUri, String clientId, String clientSecret) {
58 | delegate = new SpringOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
59 | }
60 |
61 | @Override
62 | public OAuth2AuthenticatedPrincipal introspect(String token) {
63 | final OAuth2AuthenticatedPrincipal principal = this.delegate.introspect(token);
64 | return new DefaultOAuth2AuthenticatedPrincipal(
65 | principal.getName(), principal.getAttributes(), extractAuthorities(principal));
66 | }
67 |
68 | private Collection extractAuthorities(OAuth2AuthenticatedPrincipal principal) {
69 | final List rolesClaim = getRolesClaim(principal);
70 | if (rolesClaim.isEmpty()) {
71 | LOG.warn(
72 | "No roles found in the JWT claim structure '{} -> {}'. Principal: {}",
73 | RESOURCE_ACCESS_CLAIM,
74 | ROLES_CLAIM,
75 | principal.getName());
76 | return emptySet();
77 | }
78 |
79 | return ((Collection>) rolesClaim)
80 | .stream().map(role -> (String) role).map(SimpleGrantedAuthority::new).collect(toSet());
81 | }
82 |
83 | /*
84 | * This method extracts the roles claim from a JWT token using the default Keycloak JWT token format.
85 | * Example structure:
86 | * "realm_access": {
87 | * "roles": [
88 | * "role-1",
89 | * "role-2",
90 | * "..."
91 | * ]
92 | * }
93 | * The returned list will contain ["role-1", "role-2", ...].
94 | */
95 | private List getRolesClaim(OAuth2AuthenticatedPrincipal principal) {
96 | return ofNullable(principal.getAttributes().get(RESOURCE_ACCESS_CLAIM))
97 | .filter(resourceAccessClaim -> resourceAccessClaim instanceof Map)
98 | .map(resourceAccessClaim -> (Map, ?>) resourceAccessClaim)
99 | .map(resourceAccess -> resourceAccess.get(ROLES_CLAIM))
100 | .filter(roles -> roles instanceof List)
101 | .map(roles -> (List>) roles)
102 | .map(
103 | roles ->
104 | roles.stream()
105 | .filter(role -> role instanceof String)
106 | .map(role -> (String) role)
107 | .collect(toList()))
108 | .orElseGet(ArrayList::new);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/04-PRACTICE-Topmost-Common-Attacks.md:
--------------------------------------------------------------------------------
1 | # Application Security for Java Developers
2 |
3 | Copyright (C) 2025 Ionut Balosin
4 |
5 | This project is licensed under the Apache License, Version 2.0.
6 | Please see the [LICENSE](license/LICENSE) file for full license.
7 |
8 | ---
9 |
10 | ## Topmost Common Attacks
11 |
12 | > ⏰ 30 minutes
13 |
14 | > 👨💼 Conducted By Trainer
15 |
16 | ### 📖 Informational: XSS, CSRF, OWASP WebGoat
17 |
18 | **Cross-Site Scripting (XSS)** is a security vulnerability that allows attackers to inject malicious scripts into web pages, enabling them to execute in users' browsers and potentially steal sensitive information or perform unauthorized actions.
19 |
20 | **XSS** can be categorized into two main types:
21 | - `Reflected XSS`: This type involves malicious scripts that are immediately executed in a user's browser when they click a crafted link or submit a form. It typically targets the same user and is not stored on the server.
22 | - `Stored XSS`: The malicious scripts are stored on the server (e.g., in a database) and executed whenever the affected content is accessed by any user, allowing the attacker to target multiple victims over time.
23 |
24 | **Cross-Site Request Forgery (CSRF)** is a security vulnerability that tricks users into unconsciously submitting unauthorized requests to a web application in which they are authenticated, potentially allowing attackers to perform actions on behalf of the user without their consent.
25 |
26 | **XSS** vs **CSRF**:
27 | - **XSS** involves injecting malicious scripts into web pages that are executed in the user's browser, while **CSRF** involves tricking the user into submitting unauthorized requests (often through an external image, link, or email received from the attacker) to a server while the user is authenticated.
28 | - **XSS** targets the client (the user's browser) by executing scripts directly in it, while **CSRF** targets the server by sending forged requests that the server processes as legitimate actions while the user remains authenticated.
29 |
30 | [OWASP WebGoat](https://owasp.org/www-project-webgoat) is a deliberately insecure application that contains lessons for almost all [OWASP Top 10](https://owasp.org/www-project-top-ten/) vulnerabilities (including **XSS**, **CSRF**, etc.). It allows developers to test vulnerabilities commonly found in Java-based applications that use popular open-source components.
31 |
32 | Some solutions to the challenges in the `OWASP WebGoat` application can be found in the [WebGoat Solutions](https://github.com/WebGoat/WebGoat/wiki/Main-Exploits) wiki.
33 |
34 | ---
35 |
36 | ### 🏋️ Hands-On Demo
37 |
38 | **Note:** Please ensure that the Docker daemon is running; otherwise, the commands will not execute successfully.
39 |
40 | 1. Open a terminal and start the `OWASP WebGoat` application in Docker using the following command:
41 |
42 | ```bash
43 | ./bootstrap-webgoat.sh
44 | ```
45 |
46 | 2. Next, open a browser and navigate to http://localhost:48080/WebGoat/login to access the **OWASP WebGoat UI**.
47 |
48 | 3. Create a user account to log in:
49 | - Username: `administrator`
50 | - Password: `Test1234!`
51 |
52 | #### 🕵️♂️ Cross-Site Scripting Attack
53 |
54 | To demonstrate a simple reflected **XSS** attack, navigate to the `OWASP WebGoat` [Lesson 7 Exercise](http://localhost:48080/WebGoat/start.mvc?username=administrator#lesson/CrossSiteScripting.lesson/6).
55 |
56 | You can find the solution to this exercise in the `OWASP WebGoat` [Solutions - Lesson 7 Exercise](https://github.com/WebGoat/WebGoat/wiki/Main-Exploits#cross-site-scripting-lesson-7-exercise) or simply inject and submit the following value for the `credit card number`:
57 |
58 | ```html
59 |
60 | ```
61 |
62 | #### 🕵️♂️ Cross-Site Request Forgery Attack
63 |
64 | To demonstrate a simple reflected **CSRF** attack, navigate to the `OWASP WebGoat` [Lesson 4 Exercise](http://localhost:48080/WebGoat/start.mvc?username=administrator#lesson/CSRF.lesson/3).
65 |
66 | You can find the solution to this exercise in the `OWASP WebGoat` [Solutions - Lesson 4 Exercise](https://github.com/WebGoat/WebGoat/wiki/Main-Exploits#cross-site-request-forgeries-lesson-4-exercise) or simply save and open the `html` file below while logged into the application:
67 |
68 | ```html
69 |
70 |
71 |
76 |
77 |
78 |
79 | ```
80 |
81 | **Notes:**
82 | - The `validateReq` field is a (hard-coded) token used by the server to authenticate requests. To inspect it dynamically, post a normal comment in the application and check the browser's network tab.
83 | - The CSRF attack will not work if the user is not logged into the application when opening this `html` file.
--------------------------------------------------------------------------------
/security-slf4j-logger-enricher/src/main/java/ionutbalosin/training/application/security/practices/slf4j/logger/enricher/LoggerInterceptor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Application Security for Java Developers
3 | *
4 | * Copyright (C) 2025 Ionut Balosin
5 | * Website: www.ionutbalosin.com
6 | * Social Media:
7 | * LinkedIn: ionutbalosin
8 | * Bluesky: @ionutbalosin.bsky.social
9 | * X: @ionutbalosin
10 | * Mastodon: ionutbalosin@mastodon.social
11 | *
12 | * Licensed to the Apache Software Foundation (ASF) under one
13 | * or more contributor license agreements. See the NOTICE file
14 | * distributed with this work for additional information
15 | * regarding copyright ownership. The ASF licenses this file
16 | * to you under the Apache License, Version 2.0 (the
17 | * "License"); you may not use this file except in compliance
18 | * with the License. You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing,
23 | * software distributed under the License is distributed on an
24 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25 | * KIND, either express or implied. See the License for the
26 | * specific language governing permissions and limitations
27 | * under the License.
28 | */
29 | package ionutbalosin.training.application.security.practices.slf4j.logger.enricher;
30 |
31 | import jakarta.servlet.http.HttpServletRequest;
32 | import jakarta.servlet.http.HttpServletResponse;
33 | import java.util.UUID;
34 | import org.slf4j.MDC;
35 | import org.springframework.stereotype.Component;
36 | import org.springframework.web.servlet.HandlerInterceptor;
37 |
38 | /**
39 | * This class effectively captures critical information such as the remote host, user ID,
40 | * correlation ID, HTTP request method, HTTP request URI, user agent, and response status. This
41 | * information is essential for tracking user actions, identifying potential security issues, and
42 | * troubleshooting. It stores this data in the Mapped Diagnostic Context (MDC), providing context in
43 | * log outputs, facilitating traceability, and enhancing the overall observability of the
44 | * application.
45 | */
46 | @Component
47 | public class LoggerInterceptor implements HandlerInterceptor {
48 |
49 | private static final String REMOTE_HOST = "RemoteHost";
50 | private static final String REMOTE_PORT = "RemotePort";
51 | private static final String USER_ID = "UserId";
52 | private static final String CORRELATION_ID = "CorrelationId";
53 | private static final String REQUEST_METHOD = "RequestMethod";
54 | private static final String REQUEST_URI = "RequestURI";
55 | private static final String USER_AGENT = "UserAgent";
56 | private static final String RESPONSE_STATUS = "ResponseStatus";
57 |
58 | @Override
59 | public boolean preHandle(
60 | HttpServletRequest request, HttpServletResponse response, Object handler) {
61 | // Add the remote host
62 | final String remoteHost = request.getRemoteAddr();
63 | MDC.put(REMOTE_HOST, remoteHost);
64 |
65 | // Add the remote port
66 | final String remotePort = String.valueOf(request.getRemotePort());
67 | MDC.put(REMOTE_PORT, remotePort);
68 |
69 | // Add the user ID
70 | final String userId =
71 | (request.getUserPrincipal() != null) ? request.getUserPrincipal().getName() : "anonymous";
72 | MDC.put(USER_ID, userId);
73 |
74 | // Add the correlation ID
75 | String correlationId = request.getHeader(CORRELATION_ID);
76 | if (correlationId == null || correlationId.isEmpty()) {
77 | correlationId = UUID.randomUUID().toString();
78 | }
79 | MDC.put(CORRELATION_ID, correlationId);
80 |
81 | // Add the request method
82 | String requestMethod = request.getMethod();
83 | MDC.put(REQUEST_METHOD, requestMethod);
84 |
85 | // Add the HTTP request URI
86 | String requestURI = request.getRequestURI();
87 | MDC.put(REQUEST_URI, requestURI);
88 |
89 | // Add the user agent
90 | String userAgent = request.getHeader("User-Agent");
91 | MDC.put(USER_AGENT, userAgent != null ? userAgent : "unknown");
92 |
93 | return true;
94 | }
95 |
96 | @Override
97 | public void postHandle(
98 | HttpServletRequest request,
99 | HttpServletResponse response,
100 | Object handler,
101 | org.springframework.web.servlet.ModelAndView modelAndView) {
102 | // Add the HTTP response status code
103 | // Note: the status is available after handling
104 | int status = response.getStatus();
105 | MDC.put(RESPONSE_STATUS, String.valueOf(status));
106 |
107 | // Clean up the other MDC properties, except for response status
108 | MDC.remove(REMOTE_HOST);
109 | MDC.remove(REMOTE_PORT);
110 | MDC.remove(USER_ID);
111 | MDC.remove(CORRELATION_ID);
112 | MDC.remove(REQUEST_METHOD);
113 | MDC.remove(REQUEST_URI);
114 | }
115 |
116 | @Override
117 | public void afterCompletion(
118 | HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
119 | // Remove response status from MDC
120 | MDC.remove(RESPONSE_STATUS);
121 |
122 | // Optionally clear the entire MDC context, but it's not strictly necessary
123 | // MDC.clear();
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/06-PRACTICE-Security-Testing.md:
--------------------------------------------------------------------------------
1 | # Application Security for Java Developers
2 |
3 | Copyright (C) 2025 Ionut Balosin
4 |
5 | This project is licensed under the Apache License, Version 2.0.
6 | Please see the [LICENSE](license/LICENSE) file for full license.
7 |
8 | ---
9 |
10 | ## Security Testing
11 |
12 | > ⏰ 30 minutes
13 |
14 | > 👨🎓 Attendees' Exercise
15 |
16 | ### 📖 Informational: SCA, SAST and DAST
17 |
18 | **Software Composition Analysis (SCA)** identifies vulnerabilities in project dependencies by using [Common Platform Enumeration (CPE)](https://nvd.nist.gov/products/cpe) identifiers, a standardized naming scheme that maps software components to known vulnerabilities documented as [Common Vulnerability and Exposure (CVE)](https://cve.mitre.org/) entries.
19 |
20 | Each identified vulnerability is assigned a [Common Vulnerability Scoring System (CVSS)](https://en.wikipedia.org/wiki/Common_Vulnerability_Scoring_System) score, which ranges from `1` to `10`, with `10` representing the most severe vulnerabilities.
21 |
22 | The [National Vulnerability Database (NVD)](https://nvd.nist.gov/products/cpe) is a U.S. government-managed repository that maintains comprehensive records of publicly known vulnerabilities.
23 | It includes the CPE dictionary, which organizes information about affected software products.
24 | This database is widely used by SCA code-scanning tools to identify vulnerable dependencies and assess known security risks associated with them.
25 |
26 | **Static Application Security Testing (SAST)** is a method used to secure software by analyzing its source code, or bytecode code to identify potential vulnerabilities early in the development process.
27 | SAST tools run at compile time and require access to the codebase to detect security weaknesses based on specific patterns, such as hardcoded secrets, input validation issues, or coding flaws that could lead to vulnerabilities.
28 |
29 | Main Drawbacks:
30 | - `False Positives`: SAST tools often report potential vulnerabilities that are not actual security issues, leading to unnecessary alerts and potentially overwhelming developers with false alarms.
31 | - `Dependency on Code Quality`: The accuracy of SAST tools can be impacted by poorly written, obfuscated, or highly complex code, which may result in missed vulnerabilities or excessive false positives.
32 | - `Limited Context Awareness`: SAST tools analyze code statically and cannot fully simulate runtime behaviors or interactions, sometimes missing issues that would only appear during execution."
33 |
34 | **Dynamic Application Security Testing (DAST)** is a security testing method that analyzes running applications to identify potential vulnerabilities without requiring access to the source code. DAST tools simulate external attacks on live applications, making them particularly effective for uncovering vulnerabilities in web applications, APIs, and services in real time.
35 |
36 | Key Features:
37 | - `Black-Box Testing`: DAST operates as an external attacker would, focusing on the application's exposed interfaces and user interactions, which helps uncover issues like authentication flaws, injection vulnerabilities, and configuration errors.
38 | - `Runtime Context`: By analyzing applications in a live environment, DAST tools can detect vulnerabilities that static methods may miss, such as input handling issues and misconfigurations.
39 |
40 | ---
41 |
42 | ### 🕵️♂️ Software Composition Analysis
43 |
44 | [OWASP Dependency-Check](https://owasp.org/www-project-dependency-check) is an open-source SCA tool that identifies vulnerabilities in project dependencies, helping reveal and address known security risks.
45 |
46 | Open a terminal and execute the following command to check for any known dependency vulnerabilities:
47 |
48 | ```bash
49 | ./mvnw clean compile org.owasp:dependency-check-maven:check
50 | ```
51 |
52 | **Note:** The first run of this command might take a significant amount of time (e.g., from a couple of minutes to even tens of minutes, depending on the internet connection) to initially download the [NVD Data Feeds](https://nvd.nist.gov/vuln/data-feeds) hosted by NIST.
53 |
54 | The detailed report containing all libraries and their vulnerabilities, including links to the sources where they were reported, can also be found in `/target/dependency-check-report.html`.
55 |
56 | ---
57 |
58 | ### 🕵️♂️ Static Application Security Testing
59 |
60 | [Spotbugs](https://spotbugs.github.io/) is an open-source static analysis tool that detects bugs in Java programs by analyzing bytecode.
61 |
62 | With the help of the [FindSecBugs plugin](https://find-sec-bugs.github.io/) plugin, it can be used as a SAST tool to identify security vulnerabilities in Java applications.
63 |
64 | To check for potential code vulnerabilities, execute the following command:
65 |
66 | ```bash
67 | ./mvnw clean compile spotbugs:check
68 | ```
69 |
70 | ---
71 |
72 | ### 🕵️♂️ Dynamic Application Security Testing
73 |
74 | [The Zed Attack Proxy (ZAP)](https://github.com/zaproxy/zaproxy) is an open-source DAST tool specifically designed for identifying vulnerabilities in applications during runtime.
75 |
76 | To check for API security vulnerabilities, execute the following command:
77 |
78 | ```bash
79 | ./zap-scan.sh
80 | ```
81 |
82 | **Note:** Please ensure the `Pizza` application is already started; otherwise, the command will not execute successfully.
83 |
84 | The command starts ZAP in Docker, launches an API scan using the [zap-api-scan rules](zap/zap-api-scan-rules.conf) against one of the services, and saves the scan report in the [./zap/reports](zap/reports) folder.
85 |
--------------------------------------------------------------------------------