Skip to content

Commit

Permalink
library updates
Browse files Browse the repository at this point in the history
okta auth scripts
  • Loading branch information
kcberg committed Dec 19, 2023
1 parent 7610f70 commit 1893df3
Show file tree
Hide file tree
Showing 15 changed files with 261 additions and 30 deletions.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
1 change: 0 additions & 1 deletion hawkscripts/authentication/form-auth-multi.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import com.fasterxml.jackson.databind.ObjectMapper
import com.stackhawk.Script
import com.stackhawk.zap.extension.talon.HawkConfExtensions
import com.stackhawk.zap.extension.talon.cleanHost
import com.stackhawk.zap.extension.talon.hawkscan.ExtensionTalonHawkscan
Expand Down
85 changes: 85 additions & 0 deletions hawkscripts/authentication/okta-client-credentials-basic.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import java.util.Base64
import java.util.TreeSet
import org.apache.commons.httpclient.URI
import org.apache.hc.client5.http.auth.AuthenticationException
import org.apache.hc.core5.http.ContentType
import org.apache.hc.core5.http.HttpHeaders
import org.apache.hc.core5.http.Method
import org.apache.log4j.LogManager
import org.parosproxy.paros.network.HtmlParameter
import org.parosproxy.paros.network.HttpHeader
import org.parosproxy.paros.network.HttpMessage
import org.parosproxy.paros.network.HttpRequestHeader
import org.zaproxy.zap.authentication.AuthenticationHelper
import org.zaproxy.zap.authentication.GenericAuthenticationCredentials

val logger = LogManager.getLogger("okta-auth")
val mapper = ObjectMapper()

fun authenticate(
helper: AuthenticationHelper,
paramsValues: Map<String, String>,
credentials: GenericAuthenticationCredentials,
): HttpMessage {
logger.info("auth hook")

val oktaDomain = paramsValues["okta_domain"]
val scope = paramsValues["scope"]
val clientId = credentials.getParam("client_id")
val clientSecret = credentials.getParam("client_secret")
val base64Creds = Base64.getEncoder().encodeToString("$clientId:$clientSecret".toByteArray())


val msg = helper.prepareMessage()
msg.requestHeader = HttpRequestHeader(
Method.POST.name,
URI("https://$oktaDomain/oauth2/default/v1/token", true),
HttpHeader.HTTP11
)
msg.requestHeader.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString())
msg.requestHeader.addHeader(HttpHeaders.AUTHORIZATION, "Basic $base64Creds")

val formTree = TreeSet<HtmlParameter>()
formTree.add(HtmlParameter(HtmlParameter.Type.form, "grant_type", "client_credentials"))
formTree.add(HtmlParameter(HtmlParameter.Type.form, "scope", scope))
msg.requestBody.setFormParams(formTree)
msg.requestHeader.contentLength = msg.requestBody.length()

logger.info("::::::auth request:::::\n${msg.requestHeader}${msg.requestBody}")

helper.sendAndReceive(msg)

logger.info("::::::auth response:::::\n${msg.responseHeader}${msg.responseBody}")

// Throw an authentication exception if the status code is not 2xx
if (!(200..299).contains(msg.responseHeader.statusCode)) {
val jsonObject = mapper.readValue(msg.responseBody.bytes, ObjectNode::class.java)
val err = jsonObject.get("error").asText()
val errDesc = jsonObject.get("error_description").asText()
throw AuthenticationException("$err $errDesc")
}

return msg
}

fun getRequiredParamsNames(): Array<String> {
return arrayOf("okta_domain", "scope")
}

fun getCredentialsParamsNames(): Array<String> {
return arrayOf("client_id", "client_secret")
}

fun getOptionalParamsNames(): Array<String> {
return arrayOf()
}

fun getLoggedInIndicator(): String {
return ""
}

fun getLoggedOutIndicator(): String {
return ""
}
4 changes: 2 additions & 2 deletions hawkscripts/hawkscripts.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import org.jetbrains.kotlin.konan.file.File.Companion.userHome

plugins {
kotlin("jvm") version "1.7.20"
kotlin("jvm") version "1.8.22"
}

val kotlinVersion = "1.7.20"
val hawkScriptSdkVersion = "3.1.12"
val hawkScriptSdkVersion = "3.4.2"

kotlin {
sourceSets {
Expand Down
62 changes: 62 additions & 0 deletions hawkscripts/session/jwt-session.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
import java.time.Instant
import org.apache.log4j.LogManager
import org.zaproxy.zap.session.ScriptBasedSessionManagementMethodType
import org.zaproxy.zap.extension.script.ScriptVars

val logger = LogManager.getLogger("okta-json-to-token")
val mapper = ObjectMapper()

fun extractWebSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {

val tokenField = sessionWrapper.getParam("jwt_token_field")
val tokenType = sessionWrapper.getParam("token_type_field") ?: "Bearer"

logger.info("get token from json: ${sessionWrapper.httpMessage.responseBody}")
val jsonObject = mapper.readValue(sessionWrapper.httpMessage.responseBody.bytes, ObjectNode::class.java)
val accessToken = jsonObject.get(tokenField).asText()
ScriptVars.setGlobalVar("auth_header_value", "$tokenType $accessToken")

sessionWrapper.session.setValue("jwt", accessToken)

val jwt = SignedJWT.parse(accessToken)
logger.info("jwt-expires: ${jwt.jwtClaimsSet.expirationTime}")
sessionWrapper.session.setValue("jwt_claims", jwt.jwtClaimsSet)
}

fun processMessageToMatchSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {

val nowish = Instant.now().minusMillis(15000)
val jwtClaims = sessionWrapper.session.getValue("jwt_claims") as JWTClaimsSet?
val isExpired = jwtClaims?.expirationTime?.toInstant()?.isBefore(nowish)

if (isExpired == true) {
logger.info("session expires @ ${jwtClaims.expirationTime}")
synchronized(this) {
sessionWrapper.httpMessage.requestingUser.authenticate()
}
}

logger.debug("session-jwt: ${sessionWrapper.session.getValue("jwt")}")

val hdrVal = ScriptVars.getGlobalVar("auth_header_value")
logger.debug("auth_header_value: $hdrVal")
if (!hdrVal.isNullOrEmpty()) {
sessionWrapper.httpMessage.requestHeader.setHeader("Authorization", hdrVal)
}

}

fun clearWebSessionIdentifiers(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
}

fun getRequiredParamsNames(): Array<String> {
return arrayOf("jwt_token_field")
}

fun getOptionalParamsNames(): Array<String> {
return arrayOf("token_type_field")
}
21 changes: 20 additions & 1 deletion src/main/java/hawk/MultiHttpSecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ protected void configure(HttpSecurity http) throws Exception {
}

@Configuration
@Order(4)
@Order(5)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
Expand Down Expand Up @@ -163,4 +163,23 @@ public UserDetailsService userDetailsService() {

return new InMemoryUserDetailsManager(user);
}

// "/api/okta/**"

@Configuration
@Order(4)
public static class OktaWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/okta/**")
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/okta/**")
.permitAll();
;
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/hawk/api/jwt/JwtUserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public JwtUserController(UserService userService, UserSearchService userSearchSe
}

@GetMapping("/search/")
public ResponseEntity searchAll() {
public ResponseEntity<List<User>> searchAll() {
Search search = new Search("");
return ResponseEntity.ok(this.userService.findUsersByName(""));
}
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/hawk/api/okta/OktaController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hawk.api.okta;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

@RestController
@RequestMapping("/api/okta")
public class OktaController {

@GetMapping("/me/token")
public ResponseEntity<OktaIdInfo> me(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
System.out.println(name + ": " + value);
}
String authToken = request.getHeader("Authorization");
if (authToken == null || !authToken.startsWith("Bearer ")) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
OktaIdInfo oktaInfo = new OktaIdInfo(authToken);
return ResponseEntity.ok(oktaInfo);
}

}

13 changes: 13 additions & 0 deletions src/main/java/hawk/api/okta/OktaIdInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package hawk.api.okta;

public class OktaIdInfo {
private final String token;

public OktaIdInfo(String token) {
this.token = token;
}

public String getToken() {
return token;
}
}
2 changes: 1 addition & 1 deletion src/main/java/hawk/controller/PayloadController.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class PayloadController {
private static final char[] chars = new char[]{'a','b','c',' ','\n'};
private static Map<Integer, byte[]> payloadCache = new ConcurrentHashMap();

@Value("${payload.startSize:2048}")
@Value("${payload.start-size:2048}")
private int startPayloadSize = 2048;

@Value("${payload.count:10}")
Expand Down
4 changes: 0 additions & 4 deletions src/main/resources/application-postgresql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ springdoc:
api-docs:
path: /openapi

payload:
start-size: 3096
count: 20

logging:
level:
org.hibernate.SQL: debug
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ springdoc:

payload:
start-size: 3096
count: 20
count: 10
37 changes: 37 additions & 0 deletions stackhawk.d/stackhawk-okta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
app:
applicationId: ${APP_ID:dacc7d3e-babc-47d2-b040-ab117ab04526}
env: ${APP_ENV:dev}
host: ${APP_HOST:https://localhost:9000}

authentication:
script:
name: okta-client-credentials-basic.kts
parameters:
okta_domain: '${OKTA_DOMAIN:changeme.okta.com}'
scope: api_key
credentials:
client_id: '${OKTA_CLIENT_ID:changeme}'
client_secret: '${OKTA_CLIENT_SECRET:changeme}'
sessionScript:
name: okta-json-to-token.kts
parameters:
jwt_token_field: access_token
testPath:
path: /api/okta/me/token
success: 'HTTP(.*)200.*'
loggedInIndicator: '.*'
loggedOutIndicator: ''
hawkAddOn:
scripts:
- name: okta-client-credentials-basic.kts
type: authentication
path: hawkscripts
language: KOTLIN
- name: okta-json-to-token.kts
type: session
path: hawkscripts
language: KOTLIN
- name: log-http-payloads.kts
type: httpsender
path: hawkscripts
language: KOTLIN
1 change: 1 addition & 0 deletions stackhawk.d/stackhawk-openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ app:
testPath:
path: /api/jwt/items/search/i
success: "HTTP.*200.*"

openApiConf:
# path: /openapi
filePath: openapi.yaml
Expand Down
22 changes: 4 additions & 18 deletions stackhawk.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
app:
applicationId: ${APP_ID:dacc7d3e-babc-47d2-b040-ab117ab04526}
env: ${APP_ENV:dev}
env: ${APP_ENV:Development}
host: ${APP_HOST:https://localhost:9000}
excludePaths:
- "/logout"
- "/login-form-multi"
# - "/login-code"
- "/login-code"
antiCsrfParam: "_csrf"
authentication:
loggedInIndicator: "\\QSign Out\\E"
Expand All @@ -25,19 +25,5 @@ app:
path: /search
success: "HTTP.*200.*"
waitForAppTarget:
pollDelay: 100
waitTimeoutMillis: 1000

hawk:
scan:
concurrentRequests: 100
throttlePassiveBacklog: 10000000
spider:
maxDurationMinutes: 500
config:
- "database.request.bodysize=30000000"
- "database.response.bodysize=30000000"
- "spider.maxParseSizeBytes=30000000"
# - "scanner.analyser.redirectEqualsNotFound=false"
# - "scanner.analyser.followRedirect=true"

pollDelay: 500
waitTimeoutMillis: 5000

0 comments on commit 1893df3

Please sign in to comment.