Skip to content

Commit

Permalink
Add docker compose files
Browse files Browse the repository at this point in the history
  • Loading branch information
k-tamura committed Dec 12, 2022
1 parent 329a323 commit 7ff5e0f
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 71 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
FROM maven:3.8-jdk-8 as builder
COPY . /usr/src/easybuggy4sb/
WORKDIR /usr/src/easybuggy4sb/
RUN mvn -B package
RUN mvn -B package -Dmaven.test.skip=true

FROM openjdk:8-slim
RUN apt-get update && apt-get install curl -y
WORKDIR /opt/easybuggy4sb
COPY --from=builder /usr/src/easybuggy4sb/target/ROOT.war .
CMD ["java", "-XX:MaxMetaspaceSize=128m", "-Xloggc:logs/gc_%p_%t.log", "-XX:NativeMemoryTracking=summary", "-Xmx256m", "-XX:MaxDirectMemorySize=90m", "-XX:+UseSerialGC", "-XX:+PrintHeapAtGC", "-XX:+PrintGCDetails", "-XX:+PrintGCDateStamps", "-XX:+UseGCLogFileRotation", "-XX:NumberOfGCLogFiles=5", "-XX:GCLogFileSize=10M", "-XX:GCTimeLimit=15", "-XX:GCHeapFreeLimit=50", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=logs/", "-XX:ErrorFile=logs/hs_err_pid%p.log", "-agentlib:jdwp=transport=dt_socket,server=y,address=9009,suspend=n", "-Dderby.stream.error.file=logs/derby.log", "-Dderby.infolog.append=true", "-Dderby.language.logStatementText=true", "-Dderby.locks.deadlockTrace=true", "-Dderby.locks.monitor=true", "-Dderby.storage.rowLocking=true", "-Dcom.sun.management.jmxremote", "-Dcom.sun.management.jmxremote.port=7900", "-Dcom.sun.management.jmxremote.ssl=false", "-Dcom.sun.management.jmxremote.authenticate=false", "-ea", "-jar", "ROOT.war"]
57 changes: 57 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
version: '3'

volumes:
mysql_data:
driver: local

services:
db:
image: mysql:5.7
container_name: db
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: keycloak
MYSQL_USER: keycloak
MYSQL_PASSWORD: password
ports:
- 3306:3306
keycloak:
image: quay.io/keycloak/keycloak:legacy
environment:
DB_VENDOR: MYSQL
DB_ADDR: db
DB_DATABASE: keycloak
DB_USER: keycloak
DB_PASSWORD: password
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: admin
KEYCLOAK_FRONTEND_URL: http://localhost:8180/auth
# Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the MySQL JDBC driver documentation in order to use it.
#JDBC_PARAMS: "connectTimeout=30000"
ports:
- 8180:8080
healthcheck:
test: "curl -f http://keycloak:8080/auth/realms/master/.well-known/openid-configuration || exit 1"
depends_on:
- db
easybuggy:
build:
context: .
dockerfile: Dockerfile
ports:
- 8080:8080
- 9009:9009
depends_on:
keycloak:
condition: service_healthy
environment:
db_url: jdbc:mysql://db:3306/keycloak?useSSL=false
db_class_name: com.mysql.jdbc.Driver
db_platform: mysql
db_username: keycloak
db_password: password
oidc_op_name: Keycloak
oidc_config_endpoint: http://keycloak:8080/auth/realms/master/.well-known/openid-configuration
oidc_dynamic_client_registration_enabled: 'true'
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ public class StackOverflowErrorController {

@RequestMapping(value = "/sofe")
public void process() {
new S().toString();
}

public class S {
@Override
public String toString() {
return "" + this;
}
process();
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,9 @@
package org.t246osslab.easybuggy4sb.vulnerabilities;

import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.t246osslab.easybuggy4sb.controller.AbstractController;

import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl;
import com.google.api.client.auth.oauth2.AuthorizationCodeTokenRequest;
import com.google.api.client.auth.oauth2.RefreshTokenRequest;
import com.google.api.client.auth.oauth2.TokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.auth.oauth2.TokenResponseException;
import com.google.api.client.auth.openidconnect.IdToken;
Expand All @@ -41,10 +17,28 @@
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.json.JsonHttpContent;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.gson.Gson;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.t246osslab.easybuggy4sb.controller.AbstractController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.*;

@Controller
public class VulnerableOIDCRPController extends AbstractController {
Expand All @@ -59,6 +53,8 @@ public class VulnerableOIDCRPController extends AbstractController {

protected String endSessionEndpoint;

protected String registration_endpoint;

protected String jwksUri;

protected String issuer;
Expand All @@ -69,31 +65,36 @@ public class VulnerableOIDCRPController extends AbstractController {
@Value("${oidc.client.secret}")
protected String clientSecret;

@Value("${oidc.redirect.uri}")
protected String redirectUri;

@Value("${oidc.op.name}")
protected String opName;

@Value("${oidc.dynamic.client.registration.enabled}")
protected boolean clientRegistrationEnabled = false;

@Value("${oidc.configuration.endpoint}")
public void setOPConfig(String configEndpoint) {
log.debug("OP Config Endpoint: " + configEndpoint);
try {
HttpRequestFactory requestFactory = (new NetHttpTransport()).createRequestFactory();
HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(configEndpoint));
HttpResponse response = request.execute();
Map<?, ?> opConfig = new Gson().fromJson(response.parseAsString(), Map.class);
log.debug("OP Config: " + opConfig.toString());
authzEndpoint = (String) opConfig.get("authorization_endpoint");
tokenEndpoint = (String) opConfig.get("token_endpoint");
userinfoEndpoint = (String) opConfig.get("userinfo_endpoint");
endSessionEndpoint = (String) opConfig.get("end_session_endpoint");
registration_endpoint = (String) opConfig.get("registration_endpoint");
issuer = (String) opConfig.get("issuer");
jwksUri = (String) opConfig.get("jwks_uri");
if (!(StringUtils.isEmpty(clientId) || StringUtils.isEmpty(clientSecret) || StringUtils.isEmpty(redirectUri)
|| StringUtils.isEmpty(opName))) {
if (clientRegistrationEnabled) {
tryRegisterClient();
}
if (!(StringUtils.isEmpty(clientId) || StringUtils.isEmpty(clientSecret) || StringUtils.isEmpty(opName))) {
isSettingsReady = true;
}
} catch (IOException e) {
log.debug("OP configuration request failed.", e);
log.error("OP configuration request failed.", e);
}
}

Expand Down Expand Up @@ -151,7 +152,7 @@ public ModelAndView start(ModelAndView mav, HttpServletRequest req, HttpServletR
url.setScopes(Arrays.asList("openid", "profile"));
url.setState(state);
url.set("nonce", nonce);
url.setRedirectUri(new GenericUrl(redirectUri).build());
url.setRedirectUri(new GenericUrl(req.getRequestURL().toString().replace("/start", "/callback")).build());
res.sendRedirect(url.build());
} catch (IOException e) {
log.error("Authorization code request failed.", e);
Expand Down Expand Up @@ -197,12 +198,13 @@ public ModelAndView callback(ModelAndView mav, HttpServletRequest req, HttpSessi
/* Access the token endpoint and get ID and access token */
AuthorizationCodeTokenRequest authzReq = new AuthorizationCodeTokenRequest(new NetHttpTransport(),
new JacksonFactory(), new GenericUrl(tokenEndpoint), code);
authzReq.setRedirectUri(redirectUri)
authzReq.setRedirectUri(req.getRequestURL().toString())
.setClientAuthentication(new BasicAuthentication(clientId, clientSecret));
HttpResponse httpRes = authzReq.executeUnparsed();
IdTokenResponse idTokenRes = httpRes.parseAs(IdTokenResponse.class);
String accessToken = idTokenRes.getAccessToken();
IdToken idToken = IdToken.parse(idTokenRes.getFactory(), idTokenRes.getIdToken());
String idTokenStr = idTokenRes.getIdToken();
IdToken idToken = IdToken.parse(idTokenRes.getFactory(), idTokenStr);

// Verify nonce
if (!nonce.equals(idToken.getPayload().getNonce())) {
Expand Down Expand Up @@ -234,7 +236,9 @@ public ModelAndView callback(ModelAndView mav, HttpServletRequest req, HttpSessi
}

ses.setAttribute("accessToken", accessToken);
ses.setAttribute("idToken", idTokenStr);
ses.setAttribute("refreshToken", idTokenRes.getRefreshToken());
ses.setAttribute("state", req.getParameter("state"));
userInfo = getUserInfo(ses);
changeNextPageToUserInfo(mav, locale, userInfo);
ses.setAttribute("sub", userInfo.get("sub"));
Expand All @@ -249,26 +253,17 @@ public ModelAndView callback(ModelAndView mav, HttpServletRequest req, HttpSessi
}

@RequestMapping(value = "/oidclogout")
public String logout(HttpSession ses) {
public void logout(HttpServletRequest req, HttpServletResponse res, HttpSession ses) {
try {
HttpRequestFactory requestFactory = (new NetHttpTransport()).createRequestFactory();
HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(endSessionEndpoint));
request.setRequestMethod(HttpMethods.POST);
Map<String, String> params = new HashMap<>();
params.put("client_id", clientId);
params.put("client_secret", clientSecret);
params.put("refresh_token", (String) ses.getAttribute("refreshToken"));
HttpContent content = new UrlEncodedContent(params);
request.setContent(content);
HttpResponse response = request.execute();
if (response.isSuccessStatusCode()) {
log.error("Logout request to OP failed. Response: ", response.parseAsString());
}
GenericUrl url = new GenericUrl(endSessionEndpoint);
url.set("id_token_hint", (String) ses.getAttribute("idToken"));
url.set("post_logout_redirect_uri", req.getRequestURL().toString().replace("/oidclogout", "/"));
url.set("state", ses.getAttribute("state"));
res.sendRedirect(url.build());
} catch (IOException e) {
log.error("Logout request to OP failed.", e);
}
ses.invalidate();
return "redirect:/";
}

private void changeNextPageToUserInfo(ModelAndView mav, Locale locale, Map<?, ?> userInfo) {
Expand All @@ -277,7 +272,7 @@ private void changeNextPageToUserInfo(ModelAndView mav, Locale locale, Map<?, ?>
}

private Map<?, ?> getUserInfo(HttpSession ses) {

String accessToken = (String) ses.getAttribute("accessToken");
if (accessToken == null) {
return null;
Expand Down Expand Up @@ -307,6 +302,40 @@ private void changeNextPageToUserInfo(ModelAndView mav, Locale locale, Map<?, ?>
}
return null;
}
private void tryRegisterClient() {
log.info("Try Register Client.");

try {
TokenRequest tokenReq = new TokenRequest(new NetHttpTransport(), new JacksonFactory(),
new GenericUrl(tokenEndpoint), "password");
tokenReq.put("client_id", "admin-cli");
tokenReq.put("username", "admin");
tokenReq.put("password", "admin");
TokenResponse tokenResponse = tokenReq.execute();

HttpRequestFactory requestFactory = (new NetHttpTransport()).createRequestFactory();
HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(registration_endpoint));
request.setRequestMethod(HttpMethods.POST);
HttpHeaders headers = new HttpHeaders();
headers.setAuthorization("bearer " + tokenResponse.getAccessToken());
headers.setContentType("application/json");
headers.setAccept("application/json");
request.setHeaders(headers);
Map<String, Object> params = new HashMap<>();
params.put("redirect_uris", Arrays.asList("*"));
params.put("post_logout_redirect_uris", Arrays.asList("*"));
HttpContent content = new JsonHttpContent(new JacksonFactory(), params);
request.setContent(content);
HttpResponse response = request.execute();
Map map = new Gson().fromJson(response.parseAsString(), Map.class);
clientId = (String) map.get("client_id");
clientSecret = (String) map.get("client_secret");
log.info("Client {} is registered.", clientId);

} catch (IOException e) {
log.error("Registration request to OP failed.", e);
}
}

private TokenResponse refreshTokens(String refreshToken) {
if (refreshToken != null) {
Expand Down
22 changes: 11 additions & 11 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ spring.messages.encoding=UTF-8

spring.thymeleaf.cache=false

spring.datasource.url=jdbc:derby:memory:demo;create=true
spring.datasource.username=demo
spring.datasource.password=demo
spring.datasource.driver-class-name=org.apache.derby.jdbc.EmbeddedDriver
spring.datasource.platform=
spring.profiles.active=docker-compose
spring.datasource.url=${db_url:jdbc:derby:memory:demo;create=true}
spring.datasource.driver-class-name=${db_class_name:org.apache.derby.jdbc.EmbeddedDriver}
spring.datasource.username=${db_username:demo}
spring.datasource.password=${db_password:demo}
spring.datasource.platform=${db_platform:}
spring.datasource.continue-on-error=true

spring.mail.host=localhost
Expand Down Expand Up @@ -49,9 +50,8 @@ account.lock.count=5
mail.admin.address=root@localhost

### OpenID Connect feature
oidc.client.id=clientappname
oidc.client.secret=b179fc27-1de8-4904-bd26-be628816b2c2
oidc.redirect.uri=http://localhost:8080/callback
oidc.op.name=Keyclaok
oidc.configuration.endpoint=http://localhost:8180/auth/realms/master/.well-known/openid-configuration

oidc.client.id=
oidc.client.secret=
oidc.op.name=${oidc_op_name:Keycloak}
oidc.configuration.endpoint=${oidc_config_endpoint:http://localhost:8180/auth/realms/master/.well-known/openid-configuration}
oidc.dynamic.client.registration.enabled=${oidc_dynamic_client_registration_enabled:false}

0 comments on commit 7ff5e0f

Please sign in to comment.