├── zap ├── reports │ └── empty └── zap-api-scan-rules.conf ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── spotbugs ├── spotbugs-security-include.xml └── spotbugs-security-exclude.xml ├── assets └── images │ ├── hedgehog_logo_1280.png │ ├── hedgehog_logo_1920.png │ ├── hedgehog_logo_200.png │ ├── hedgehog_logo_3000.png │ ├── hedgehog_logo_320.png │ └── hedgehog_logo_640.png ├── owasp └── project-suppressions.xml ├── serialization-deserialization ├── src │ └── main │ │ ├── resources │ │ ├── zbsm.zip │ │ └── xml_external_entity.xml │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── serialization │ │ └── deserialization │ │ ├── yaml │ │ ├── User.java │ │ └── YamlBombDeserializer.java │ │ └── clazz │ │ ├── TrustedClazz.java │ │ └── MaliciousClazz.java └── pom.xml ├── security-token-jwks ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── jwks │ │ └── JwksSecurityConfiguration.java └── pom.xml ├── pizza-cooking-service ├── .dockerignore ├── Dockerfile.svc ├── src │ └── main │ │ ├── resources │ │ ├── application-dockerlocal.properties │ │ ├── logback.xml │ │ └── application.properties │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── pizza │ │ └── cooking │ │ └── service │ │ ├── Application.java │ │ ├── mapper │ │ └── PizzaDeliveryOrderDtoMapper.java │ │ ├── config │ │ └── MapperConfig.java │ │ ├── client │ │ └── DeliveryClient.java │ │ ├── controller │ │ └── CookingController.java │ │ └── service │ │ └── CookingService.java ├── build-docker.sh └── pom.xml ├── pizza-delivery-service ├── .dockerignore ├── Dockerfile.svc ├── src │ └── main │ │ ├── resources │ │ ├── application-dockerlocal.properties │ │ ├── logback.xml │ │ └── application.properties │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── pizza │ │ └── delivery │ │ └── service │ │ ├── Application.java │ │ ├── service │ │ └── DeliveryService.java │ │ ├── controller │ │ └── DeliveryController.java │ │ └── client │ │ └── OrderClient.java ├── build-docker.sh └── pom.xml ├── pizza-order-service ├── .dockerignore ├── Dockerfile.svc ├── src │ └── main │ │ ├── resources │ │ ├── application-dockerlocal.properties │ │ ├── logback.xml │ │ └── application.properties │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── pizza │ │ └── order │ │ └── service │ │ ├── Application.java │ │ ├── config │ │ └── MapperConfig.java │ │ ├── cache │ │ └── PizzaCookingOrderCache.java │ │ ├── service │ │ └── OrderService.java │ │ ├── sanitizer │ │ └── OrderSanitizer.java │ │ ├── mapper │ │ └── PizzaCookingOrderDtoMapper.java │ │ └── client │ │ └── CookingClient.java ├── build-docker.sh └── pom.xml ├── security-feign-logger-enricher ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── feign │ │ └── logger │ │ └── enricher │ │ ├── CorrelationIdInterceptor.java │ │ ├── CustomSlf4jLogger.java │ │ └── FeignConfiguration.java └── pom.xml ├── security-slf4j-logger-enricher ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── slf4j │ │ └── logger │ │ └── enricher │ │ ├── LoggerInterceptorConfig.java │ │ └── LoggerInterceptor.java └── pom.xml ├── security-token-introspection ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── ionutbalosin │ │ └── training │ │ └── application │ │ └── security │ │ └── practices │ │ └── token │ │ └── introspection │ │ └── OpaqueJwtIntrospector.java └── pom.xml ├── docker-compose-webgoat.yml ├── .gitignore ├── Dockerfile-zap ├── security-token-client-credentials-fetcher ├── pom.xml └── src │ └── main │ └── java │ └── ionutbalosin │ └── training │ └── application │ └── security │ └── practices │ └── client │ └── credentials │ └── handler │ ├── IdpToken.java │ ├── util │ └── JsonObjectMapper.java │ └── IdpTokenFetcher.java ├── license ├── LICENSE-HEADER-SHELL └── LICENSE-HEADER-JAVA ├── docker-compose-keycloak.yml ├── encryption-decryption ├── pom.xml └── src │ └── main │ ├── resources │ └── confidential_file.txt │ └── java │ └── ionutbalosin │ └── training │ └── application │ └── security │ └── practices │ └── encryption │ └── decryption │ ├── hashing │ ├── MessageHashing.java │ ├── HmacMessageAuthenticator.java │ └── PasswordHashing.java │ ├── asymetric │ ├── MessageEncryptDecrypt.java │ └── DigitalSignatureVerifier.java │ └── symetric │ └── FileDecryption.java ├── bootstrap-keycloak.sh ├── bootstrap-webgoat.sh ├── docker-compose-pizza-application.yml ├── zap-entrypoint.sh ├── postman └── Java-Application-Security-Practices.postman_environment.json ├── pizza-cooking-api ├── src │ └── main │ │ └── resources │ │ └── service-api.yaml └── pom.xml ├── pizza-delivery-api ├── src │ └── main │ │ └── resources │ │ └── service-api.yaml └── pom.xml ├── 03-PRACTICE-API-and-Microservices-Security.md ├── 01-PRACTICE-Security-Design-Principles.md ├── pizza-menu.json ├── bootstrap-pizza-application.sh ├── zap-scan.sh ├── pizza-order-api └── pom.xml ├── 99-PRACTICE-Security-General-Quiz.md ├── 02-PRACTICE-Authentication-and-Authorization.md ├── 04-PRACTICE-Topmost-Common-Attacks.md └── 06-PRACTICE-Security-Testing.md /zap/reports/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /spotbugs/spotbugs-security-include.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/images/hedgehog_logo_1280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/assets/images/hedgehog_logo_1280.png -------------------------------------------------------------------------------- /assets/images/hedgehog_logo_1920.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/assets/images/hedgehog_logo_1920.png -------------------------------------------------------------------------------- /assets/images/hedgehog_logo_200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/assets/images/hedgehog_logo_200.png -------------------------------------------------------------------------------- /assets/images/hedgehog_logo_3000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/assets/images/hedgehog_logo_3000.png -------------------------------------------------------------------------------- /assets/images/hedgehog_logo_320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/assets/images/hedgehog_logo_320.png -------------------------------------------------------------------------------- /assets/images/hedgehog_logo_640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/assets/images/hedgehog_logo_640.png -------------------------------------------------------------------------------- /owasp/project-suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /serialization-deserialization/src/main/resources/zbsm.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ionutbalosin/java-application-security-practices/HEAD/serialization-deserialization/src/main/resources/zbsm.zip -------------------------------------------------------------------------------- /spotbugs/spotbugs-security-exclude.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /security-token-jwks/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | ionutbalosin.training.application.security.practices.jwks.JwksSecurityConfiguration 2 | -------------------------------------------------------------------------------- /pizza-cooking-service/.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.iws 4 | .git 5 | .gradle 6 | .dockerignore 7 | .git/**/* 8 | .gradle/**/* 9 | .dockerignore/**/* 10 | Dockerfile 11 | Dockerfile.* 12 | docker-compose.yml -------------------------------------------------------------------------------- /pizza-delivery-service/.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.iws 4 | .git 5 | .gradle 6 | .dockerignore 7 | .git/**/* 8 | .gradle/**/* 9 | .dockerignore/**/* 10 | Dockerfile 11 | Dockerfile.* 12 | docker-compose.yml -------------------------------------------------------------------------------- /pizza-order-service/.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.iws 4 | .git 5 | .gradle 6 | .dockerignore 7 | .git/**/* 8 | .gradle/**/* 9 | .dockerignore/**/* 10 | Dockerfile 11 | Dockerfile.* 12 | docker-compose.yml -------------------------------------------------------------------------------- /security-feign-logger-enricher/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | ionutbalosin.training.application.security.practices.feign.logger.enricher.FeignConfiguration 2 | -------------------------------------------------------------------------------- /security-slf4j-logger-enricher/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | ionutbalosin.training.application.security.practices.slf4j.logger.enricher.LoggerInterceptorConfig 2 | -------------------------------------------------------------------------------- /pizza-order-service/Dockerfile.svc: -------------------------------------------------------------------------------- 1 | FROM azul/zulu-openjdk:21 2 | 3 | ARG JAR_FILE=/target/pizza-order-service-0.0.1-SNAPSHOT.jar 4 | COPY ${JAR_FILE} pizza-order-service.jar 5 | 6 | ENTRYPOINT ["java","-jar","pizza-order-service.jar"] -------------------------------------------------------------------------------- /pizza-cooking-service/Dockerfile.svc: -------------------------------------------------------------------------------- 1 | FROM azul/zulu-openjdk:21 2 | 3 | ARG JAR_FILE=/target/pizza-cooking-service-0.0.1-SNAPSHOT.jar 4 | COPY ${JAR_FILE} pizza-cooking-service.jar 5 | 6 | ENTRYPOINT ["java","-jar","pizza-cooking-service.jar"] -------------------------------------------------------------------------------- /security-token-introspection/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | ionutbalosin.training.application.security.practices.token.introspection.IntrospectionSecurityConfiguration 2 | -------------------------------------------------------------------------------- /pizza-delivery-service/Dockerfile.svc: -------------------------------------------------------------------------------- 1 | FROM azul/zulu-openjdk:21 2 | 3 | ARG JAR_FILE=/target/pizza-delivery-service-0.0.1-SNAPSHOT.jar 4 | COPY ${JAR_FILE} pizza-delivery-service.jar 5 | 6 | ENTRYPOINT ["java","-jar","pizza-delivery-service.jar"] -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /docker-compose-webgoat.yml: -------------------------------------------------------------------------------- 1 | services: 2 | webgoat: 3 | container_name: webgoat.local 4 | image: webgoat/webgoat 5 | ports: 6 | - 48080:8080 7 | - 49090:9090 8 | networks: 9 | - security-practices-network 10 | networks: 11 | security-practices-network: 12 | name: security-practices -------------------------------------------------------------------------------- /pizza-delivery-service/src/main/resources/application-dockerlocal.properties: -------------------------------------------------------------------------------- 1 | # IdP JWKS configuration for validating JWTs 2 | spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://keycloak.local:9090/realms/master/protocol/openid-connect/certs 3 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/realms/master -------------------------------------------------------------------------------- /pizza-order-service/src/main/resources/application-dockerlocal.properties: -------------------------------------------------------------------------------- 1 | # IdP introspection configuration for validating (opaque) tokens 2 | spring.security.oauth2.resourceserver.opaque.introspection-uri=http://keycloak.local:9090/realms/master/protocol/openid-connect/token/introspect 3 | spring.security.oauth2.resourceserver.opaque.introspection-client-id=demo_private_client 4 | spring.security.oauth2.resourceserver.opaque.introspection-client-secret=6EuUNXQzFmxu6xwPHDvvoh56z1uzrBMw 5 | 6 | # pizza-cooking service configuration 7 | pizza-cooking-service.name=pizza-cooking-service.local 8 | pizza-cooking-service-endpoint.url=http://${pizza-cooking-service.name}:8080 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class files 2 | *.class 3 | **/generated-classes/* 4 | **/target/* 5 | **/build/* 6 | **/dependency-reduced-pom.xml 7 | 8 | # macOS files 9 | **/.DS_Store 10 | 11 | # Generated files 12 | **/profiling/* 13 | **/zap/reports/* 14 | 15 | # Log files 16 | *.log 17 | **/logs/* 18 | **/log/* 19 | 20 | # BlueJ files 21 | *.ctxt 22 | 23 | # Package Files 24 | *.war 25 | *.nar 26 | *.ear 27 | *.zip 28 | *.tar.gz 29 | *.rar 30 | 31 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 32 | hs_err_pid* 33 | 34 | # IDE files 35 | .classpath 36 | .project 37 | .settings/ 38 | .iml 39 | .ipr 40 | .iws 41 | .metadata/ 42 | .idea/** 43 | *.iml 44 | *.ipr 45 | *.iws 46 | -------------------------------------------------------------------------------- /pizza-cooking-service/src/main/resources/application-dockerlocal.properties: -------------------------------------------------------------------------------- 1 | # IdP endpoint configuration for fetching OAuth 2.0 tokens 2 | oidc.url=http://keycloak.local:9090/realms/master/protocol/openid-connect/token 3 | oidc.clientId=demo_private_client 4 | oidc.clientSecret=6EuUNXQzFmxu6xwPHDvvoh56z1uzrBMw 5 | 6 | # IdP JWKS configuration for validating JWTs 7 | spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://keycloak.local:9090/realms/master/protocol/openid-connect/certs 8 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/realms/master 9 | 10 | # pizza-delivery service configuration 11 | pizza-delivery-service.name=pizza-delivery-service.local 12 | pizza-delivery-service-endpoint.url=http://${pizza-delivery-service.name}:8080 -------------------------------------------------------------------------------- /security-slf4j-logger-enricher/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 | Security SLF4J Logger Enricher 13 | security-slf4j-logger-enricher 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /pizza-cooking-service/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] [%X] %-5level %logger - %msg%n 7 | 8 | 9 | 10 | 11 | ${LOG_DIR}/server.log 12 | 13 | %d{HH:mm:ss.SSS} [%thread] [%X] %-5level %logger - %msg%n 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /pizza-delivery-service/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] [%X] %-5level %logger - %msg%n 7 | 8 | 9 | 10 | 11 | ${LOG_DIR}/server.log 12 | 13 | %d{HH:mm:ss.SSS} [%thread] [%X] %-5level %logger - %msg%n 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /pizza-order-service/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] [%X] %-5level %logger - %msg%n 7 | 8 | 9 | 10 | 11 | ${LOG_DIR}/server.log 12 | 13 | %d{HH:mm:ss.SSS} [%thread] [%X] %-5level %logger - %msg%n 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /serialization-deserialization/src/main/resources/xml_external_entity.xml: -------------------------------------------------------------------------------- 1 | 2 | ]> 4 | 5 | 6 | Luna 7 | Skywalker 8 | luna_sky99 9 | Aspiring space traveler and coffee lover 10 | 11 | 12 | Finn 13 | Rivers 14 | fantastic_ocean 15 | I surf the web like I surf the waves 16 | 17 | 18 | Nova 19 | Blaze 20 | nova_firestorm 21 | Coding by day, superhero by night 22 | 23 | 24 | Bomb 25 | Hunter 26 | crack_the_heap 27 | &xxe; 28 | 29 | -------------------------------------------------------------------------------- /pizza-delivery-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # spring application configuration 2 | spring.application.name=pizza-cooking-service 3 | 4 | # logging configuration 5 | logging.level.ionutbalosin.training=INFO 6 | logging.level.feign.Logger=DEBUG 7 | logging.level.org.springframework.security=WARN 8 | 9 | # swagger-ui custom path 10 | # Note: The /public endpoint is excluded from authorization to allow access to Swagger UI and API docs 11 | springdoc.swagger-ui.path=/public/swagger-ui.html 12 | springdoc.api-docs.path=/public/v3/api-docs/swagger-config 13 | 14 | # IdP JWKS configuration for validating JWTs 15 | spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9090/realms/master/protocol/openid-connect/certs 16 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/realms/master 17 | 18 | # cors allowed origins 19 | cors.allowed-origins=http://localhost,https://www.ionutbalosin.com 20 | 21 | # pizza-order service configuration 22 | pizza-order-service.name=pizza-order-service.local 23 | pizza-order-service-endpoint.url=http://${pizza-order-service.name}:8080 -------------------------------------------------------------------------------- /Dockerfile-zap: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/zaproxy/zaproxy:stable 2 | 3 | USER root 4 | 5 | # Install necessary packages: ca-certificates-java (for Java app integration) 6 | # Force an update of the CA certificates to handle HTTPS connections properly 7 | RUN apt-get update && apt-get install -y ca-certificates-java && \ 8 | update-ca-certificates -f 9 | 10 | # Create a working directory within the ZAP container for scan results or other files 11 | RUN mkdir /zap/wrk && chmod 755 /zap/wrk 12 | 13 | # Copy custom ZAP API scan rules configuration 14 | # For a full list of supported scanning categories, visit: https://www.zaproxy.org/docs/docker 15 | # The current ZAP API scan rules are defined at: https://www.zaproxy.org/docs/docker/api-scan/#configuration-file 16 | COPY zap/zap-api-scan-rules.conf /zap/wrk/ 17 | 18 | # Copy the custom entrypoint script, which contains the logic to start the ZAP API scan 19 | COPY zap-entrypoint.sh /usr/local/bin/zap-entrypoint.sh 20 | RUN chmod +x /usr/local/bin/zap-entrypoint.sh 21 | 22 | # Set the entrypoint to the custom script so it runs when the container starts 23 | ENTRYPOINT ["/usr/local/bin/zap-entrypoint.sh"] -------------------------------------------------------------------------------- /security-token-jwks/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 | Security Token Jwks 13 | security-token-jwks 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-oauth2-resource-server 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /security-feign-logger-enricher/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 | Security Feign Logger Enricher 13 | security-feign-logger-enricher 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.cloud 22 | spring-cloud-starter-openfeign 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /security-token-client-credentials-fetcher/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 | Security Token Client Credentials Fetcher 13 | security-token-client-credentials-fetcher 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | com.squareup.okhttp3 22 | okhttp 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /security-token-introspection/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 | Security Token Introspection 13 | security-token-introspection 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-oauth2-resource-server 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /license/LICENSE-HEADER-SHELL: -------------------------------------------------------------------------------- 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 | # -------------------------------------------------------------------------------- /docker-compose-keycloak.yml: -------------------------------------------------------------------------------- 1 | services: 2 | keycloak: 3 | container_name: keycloak.local 4 | image: quay.io/keycloak/keycloak:25.0.5 5 | command: ["start-dev", "--import-realm"] 6 | ports: 7 | - 9090:9090 8 | networks: 9 | - security-practices-network 10 | environment: 11 | - KEYCLOAK_ADMIN=admin 12 | - KEYCLOAK_ADMIN_PASSWORD=admin 13 | - KC_METRICS_ENABLED=true 14 | - KC_LOG_LEVEL=DEBUG 15 | - KC_HTTP_PORT=9090 16 | # To ensure Keycloak in the Docker container works correctly with JWT tokens retrieved by Postman, 17 | # the following two properties must be enabled: 18 | # 1. `KC_HOSTNAME`: This sets the hostname for Keycloak, which should match the `iss` (issuer) claim in the JWT tokens. 19 | # 2. `KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true`: This allows dynamic back channel URL resolution, ensuring Keycloak handles internal and external requests properly. 20 | # See: https://stackoverflow.com/questions/72854439/iss-claim-not-valid-keycloak 21 | - KC_HOSTNAME=http://localhost:9090 22 | - KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true 23 | networks: 24 | security-practices-network: 25 | name: security-practices -------------------------------------------------------------------------------- /license/LICENSE-HEADER-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 | */ -------------------------------------------------------------------------------- /encryption-decryption/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 | Encryption/Decryption Examples 13 | encryption-decryption 14 | 15 | 16 | 17 | commons-io 18 | commons-io 19 | 20 | 21 | org.bouncycastle 22 | bcprov-jdk18on 23 | 24 | 25 | 26 | 27 | 28 | 29 | src/main/resources 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /pizza-cooking-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # spring application configuration 2 | spring.application.name=pizza-cooking-service 3 | 4 | # logging configuration 5 | logging.level.ionutbalosin.training=INFO 6 | logging.level.feign.Logger=DEBUG 7 | logging.level.org.springframework.security=WARN 8 | 9 | # swagger-ui custom path 10 | # Note: The /public endpoint is excluded from authorization to allow access to Swagger UI and API docs 11 | springdoc.swagger-ui.path=/public/swagger-ui.html 12 | springdoc.api-docs.path=/public/v3/api-docs/swagger-config 13 | 14 | # IdP endpoint configuration for fetching OAuth 2.0 tokens 15 | oidc.url=http://localhost:9090/realms/master/protocol/openid-connect/token 16 | oidc.clientId=demo_private_client 17 | oidc.clientSecret=6EuUNXQzFmxu6xwPHDvvoh56z1uzrBMw 18 | 19 | # IdP JWKS configuration for validating JWTs 20 | spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:9090/realms/master/protocol/openid-connect/certs 21 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/realms/master 22 | 23 | # cors allowed origins 24 | cors.allowed-origins=http://localhost,https://www.ionutbalosin.com 25 | 26 | # pizza-delivery service configuration 27 | pizza-delivery-service.name=pizza-delivery-service.local 28 | pizza-delivery-service-endpoint.url=http://${pizza-delivery-service.name}:8080 -------------------------------------------------------------------------------- /serialization-deserialization/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 | Serialization/Deserialization Examples 13 | serialization-deserialization 14 | 15 | 16 | 17 | commons-io 18 | commons-io 19 | 20 | 21 | org.yaml 22 | snakeyaml 23 | 24 | 25 | org.bouncycastle 26 | bcprov-jdk18on 27 | 28 | 29 | 30 | 31 | 32 | 33 | src/main/resources 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /pizza-order-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # spring application configuration 2 | spring.application.name=pizza-order-service 3 | 4 | # logging configuration 5 | logging.level.ionutbalosin.training=INFO 6 | logging.level.feign.Logger=DEBUG 7 | logging.level.org.springframework.security=WARN 8 | 9 | # swagger-ui custom path 10 | # Note: The /public endpoint is excluded from authorization to allow access to Swagger UI and API docs 11 | springdoc.swagger-ui.path=/public/swagger-ui.html 12 | springdoc.api-docs.path=/public/v3/api-docs/swagger-config 13 | 14 | # file upload validation settings (using a whitelisting approach) 15 | file.upload.max-size=15728640 16 | file.upload.max-filename-length=255 17 | file.upload.allowed-extensions=txt,json 18 | 19 | # IdP introspection configuration for validating (opaque) tokens 20 | spring.security.oauth2.resourceserver.opaque.introspection-uri=http://localhost:9090/realms/master/protocol/openid-connect/token/introspect 21 | spring.security.oauth2.resourceserver.opaque.introspection-client-id=demo_private_client 22 | spring.security.oauth2.resourceserver.opaque.introspection-client-secret=6EuUNXQzFmxu6xwPHDvvoh56z1uzrBMw 23 | 24 | # cors allowed origins 25 | cors.allowed-origins=http://localhost,https://www.ionutbalosin.com 26 | 27 | # pizza-cooking service configuration 28 | pizza-cooking-service.name=pizza-cooking-service.local 29 | pizza-cooking-service-endpoint.url=http://${pizza-cooking-service.name}:8080 -------------------------------------------------------------------------------- /pizza-order-service/build-docker.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 "| Pizza Order Service - build Docker image |" 34 | echo "+------------------------------------------+" 35 | echo "" 36 | docker build -t pizza-order-service:local -f Dockerfile.svc . -------------------------------------------------------------------------------- /pizza-cooking-service/build-docker.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 "| Pizza Cooking Service - build Docker image |" 34 | echo "+--------------------------------------------+" 35 | echo "" 36 | docker build -t pizza-cooking-service:local -f Dockerfile.svc . -------------------------------------------------------------------------------- /pizza-delivery-service/build-docker.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 "| Pizza Delivery Service - build Docker image |" 34 | echo "+--------------------------------------------+" 35 | echo "" 36 | docker build -t pizza-delivery-service:local -f Dockerfile.svc . -------------------------------------------------------------------------------- /serialization-deserialization/src/main/java/ionutbalosin/training/application/security/practices/serialization/deserialization/yaml/User.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.util.Map; 32 | 33 | public record User( 34 | String firstname, String lastname, String username, Map 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 | *

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 | eCommerce 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 |
72 | 73 | 74 | 75 |
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 | --------------------------------------------------------------------------------