diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..6552b2d --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,55 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: SonarQube Analyze + +on: + push: + branches: [ "main" ] + pull_request: + types: [opened, synchronize, reopened] + workflow_dispatch: + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build with Maven and analyze with SonarQube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=demo:java-security + #run: mvn -B package --file pom.xml + + # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive + #- name: Update dependency graph + # uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 diff --git a/.gitignore b/.gitignore index 6a5b15a..a12c01a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ hs_err_pid* #IDEs .idea/ .vscode/ +java-security.iml diff --git a/README.md b/README.md index c9f5cde..f27ac69 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Demo - Java Security +SonarQube: +[![Quality Gate Status](https://nautilus.sonarqube.org/api/project_badges/measure?project=demo%3Ajava-security&metric=alert_status&token=squ_1e4f3504bdc994f093721895e070abe7c11b1632)](https://nautilus.sonarqube.org/dashboard?id=demo%3Ajava-security) [![Maintainability Rating](https://nautilus.sonarqube.org/api/project_badges/measure?project=demo%3Ajava-security&metric=sqale_rating&token=squ_1e4f3504bdc994f093721895e070abe7c11b1632)](https://nautilus.sonarqube.org/dashboard?id=demo%3Ajava-security) [![Reliability Rating](https://nautilus.sonarqube.org/api/project_badges/measure?project=demo%3Ajava-security&metric=reliability_rating&token=squ_1e4f3504bdc994f093721895e070abe7c11b1632)](https://nautilus.sonarqube.org/dashboard?id=demo%3Ajava-security) [![Security Rating](https://nautilus.sonarqube.org/api/project_badges/measure?project=demo%3Ajava-security&metric=security_rating&token=squ_1e4f3504bdc994f093721895e070abe7c11b1632)](https://nautilus.sonarqube.org/dashboard?id=demo%3Ajava-security) [![Security Hotspots](https://nautilus.sonarqube.org/api/project_badges/measure?project=demo%3Ajava-security&metric=security_hotspots&token=squ_1e4f3504bdc994f093721895e070abe7c11b1632)](https://nautilus.sonarqube.org/dashboard?id=demo%3Ajava-security) + +SonarCloud: [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=SonarCloud-Demos_demo-java-security&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=SonarCloud-Demos_demo-java-security) ## Use case diff --git a/credentials.properties b/credentials.properties new file mode 100644 index 0000000..e86d368 --- /dev/null +++ b/credentials.properties @@ -0,0 +1,2 @@ +keyId=AKIA6HMXNBHDXXNYUMIX +aws_secret_access_key=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 diff --git a/pom.xml b/pom.xml index 6c17931..e90e298 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,11 @@ commons-io 2.11.0 + + commons-codec + commons-codec + 1.16.0 + javax.servlet javax.servlet-api @@ -50,6 +55,24 @@ junit 4.13.2 test + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + maven-plugin + + + org.junit.jupiter + junit-jupiter-api + 5.10.0 + test + + + org.mockito + mockito-all + 1.10.19 + test @@ -78,6 +101,11 @@ + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + @@ -86,6 +114,11 @@ sonar-maven-plugin 3.11.0.3922 + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + diff --git a/src/main/java/demo/security/servlet/UserServlet.java b/src/main/java/demo/security/servlet/UserServlet.java index 9159e35..66317ed 100644 --- a/src/main/java/demo/security/servlet/UserServlet.java +++ b/src/main/java/demo/security/servlet/UserServlet.java @@ -1,11 +1,15 @@ package demo.security.servlet; import demo.security.util.DBUtils; +import demo.security.util.SessionHeader; +import org.apache.commons.codec.binary.Base64; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.ObjectInputStream; import java.io.PrintWriter; import java.util.List; @@ -14,7 +18,6 @@ public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String user = request.getParameter("username"); - String query = "SELECT userid FROM users WHERE username = '" + user + "'"; try { DBUtils db = new DBUtils(); List users = db.findUsers(user); @@ -30,8 +33,36 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } + private SessionHeader getSessionHeader(HttpServletRequest request) { + String sessionAuth = request.getHeader("Session-Auth"); + if (sessionAuth != null) { + try { + byte[] decoded = Base64.decodeBase64(sessionAuth); + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(decoded)); + return (SessionHeader) in.readObject(); + } catch (Exception e) { + return null; + } + } + return null; + } + @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - + SessionHeader sessionHeader = getSessionHeader(request); + if (sessionHeader == null) return; + String user = sessionHeader.getUsername(); + try { + DBUtils db = new DBUtils(); + List users = db.findUsers(user); + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + users.forEach((result) -> { + out.print("

User "+result+ "

"); + }); + out.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/java/demo/security/util/SessionHeader.java b/src/main/java/demo/security/util/SessionHeader.java new file mode 100644 index 0000000..2a18331 --- /dev/null +++ b/src/main/java/demo/security/util/SessionHeader.java @@ -0,0 +1,14 @@ +package demo.security.util; +import java.io.Serializable; +public class SessionHeader implements Serializable { + private String username; + private String sessionId; + public SessionHeader(String username, String sessionId) { + this.username = username; + this.sessionId = sessionId; + } + public String getUsername() { return this.username; } + public void setUsername(String username) { this.username = username; } + public String getSessionId() { return this.sessionId; } + public void setSessionId(String sessionId) { this.sessionId = sessionId; } +} \ No newline at end of file diff --git a/src/main/java/demo/security/util/Utils.java b/src/main/java/demo/security/util/Utils.java index a73c6f5..c68b801 100644 --- a/src/main/java/demo/security/util/Utils.java +++ b/src/main/java/demo/security/util/Utils.java @@ -3,16 +3,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.FileUtils; +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Paths; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; +import java.security.*; public class Utils { @@ -37,4 +39,14 @@ public static void executeJs(String input) throws ScriptException { ScriptEngine engine = manager.getEngineByName("JavaScript"); engine.eval(input); } + + public static void encrypt(byte[] key, byte[] ptxt) throws Exception { + byte[] nonce = "7cVgr5cbdCZV".getBytes("UTF-8"); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonce); + + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); // Noncompliant + } } diff --git a/src/test/java/WebUtilsTest.java b/src/test/java/WebUtilsTest.java new file mode 100644 index 0000000..02b60c2 --- /dev/null +++ b/src/test/java/WebUtilsTest.java @@ -0,0 +1,34 @@ +import demo.security.util.WebUtils; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import javax.servlet.http.HttpServletRequest; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +public class WebUtilsTest { + + @Test + public void getSessionId_withValidRequest() { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + when(request.getRequestedSessionId()).thenReturn("validSessionId"); + + WebUtils.getSessionId(request); + } + + @Test + public void getSessionId_withNullSessionId() { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + when(request.getRequestedSessionId()).thenReturn(null); + + WebUtils.getSessionId(request); + } + + @Test + public void getSessionId_withIOException() { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + when(request.getRequestedSessionId()).thenThrow(new RuntimeException()); + + assertThrows(RuntimeException.class, () -> WebUtils.getSessionId(request)); + } +} \ No newline at end of file