Session Management Scripts
Session Script Type Details
Usage Overview
Session management scripting enables you to customize how HawkScan maintains authenticated sessions throughout the scanning process. While authentication scripts handle the initial login process, session scripts manage what happens afterward—extracting session tokens, injecting authentication headers into every request, handling token expiration, and triggering re-authentication when needed.
Session scripts work in tandem with authentication mechanisms to ensure that HawkScan maintains valid, authenticated sessions throughout the entire test cycle. They are particularly valuable for applications using JWT tokens, custom authorization headers, or complex session management patterns that go beyond simple cookie-based authentication.
Key Responsibilities:
- Extract session identifiers (tokens, cookies, headers) immediately after authentication
- Inject authentication data into every subsequent request during scanning
- Monitor token expiration and trigger re-authentication when necessary
- Manage the session lifecycle and cleanup
When to Use Session Scripts vs Built-in Session Management
HawkScan provides built-in session management for common scenarios, including automatic cookie handling and basic JWT token extraction. Session scripts become necessary when you need:
Use Session Scripts When:
- Your application uses JWT tokens in custom JSON response fields
- You need to inject authorization headers into every request (e.g.,
Authorization: Bearer <token>
) - Token expiration requires proactive re-authentication before tokens expire
- Your application uses a combination of cookies and custom headers
- External authentication commands return complex JSON structures requiring parsing
- You need to access tokens from different parts of the authentication response
- Session validation requires custom logic beyond standard patterns
Use Built-in Session Management When:
- Your application uses standard session cookies that are automatically handled by the browser
- Form-based authentication with cookies is sufficient
- OAuth flows with standard token handling patterns
- No custom header injection is required
Usage Scenarios
1. JWT Token Extraction and Header Injection
The most common use case for session scripts is extracting JWT tokens from JSON authentication responses and injecting them as Authorization
headers into every request during scanning.
Example: OAuth 2.0 authentication returns JSON:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
The session script extracts access_token
, monitors expiration, and adds Authorization: Bearer <token>
to every request.
Reference: jwt-session.kts example
2. External Command Authentication with Complex JSON
When using external authentication commands that return both cookies and headers in JSON format, session scripts parse the output and properly configure the session state.
Example: External command returns:
{
"headers": [
{"Authorization": "Bearer token123"},
{"X-API-Key": "api_key_value"}
],
"cookies": [
{"JSESSIONID": "session_id_value"},
{"XSRF-TOKEN": "csrf_token"}
]
}
The session script extracts all headers and cookies, stores them appropriately, and injects them into subsequent requests.
Reference: external-auth-session.kts example
3. Multi-Token Management (Headers + Cookies)
Some applications require both custom authentication headers and cookies to be present on every request. Session scripts can manage both simultaneously.
Example: An application requires:
- A custom
X-Auth-Token
header with a token value - Session cookies (
JSESSIONID
,XSRF-TOKEN
) managed by HttpState
The session script extracts both, stores the token value for header injection, and ensures cookies are properly maintained in the HttpState.
Reference: token-and-cookie.kts example
Simple Script/Template Example and Description
Consider the following basic session management template, session-template.kts
:
import org.apache.log4j.LogManager
import com.stackhawk.hste.session.ScriptBasedSessionManagementMethodType
val logger = LogManager.getLogger("session-template")
/* This function is called after the authentication function to establish a session. */
fun extractWebSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
logger.info("Extracting session data from authentication response")
// Example: Extract data from the authentication response
// val responseBody = sessionWrapper.httpMessage.responseBody.toString()
// Example: Store values in the session for later use
// sessionWrapper.session.setValue("token", extractedToken)
logger.info("Session extraction completed")
}
// This function is called on each request to inject authentication data before sending
fun processMessageToMatchSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
logger.debug("Processing message to match session")
// Example: Inject authentication headers into the request
// val token = sessionWrapper.session.getValue("token") as String
// sessionWrapper.httpMessage.requestHeader.setHeader("Authorization", "Bearer $token")
logger.debug("Message processing completed")
}
// Called internally when a new session is required (cleanup)
fun clearWebSessionIdentifiers(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
logger.info("Clearing session identifiers")
// Example: Clear stored session values
// sessionWrapper.session.setValue("token", null)
// sessionWrapper.session.httpState.clearCookies()
logger.info("Session identifiers cleared")
}
// The required parameter names for your script
fun getRequiredParamsNames(): Array<String> {
return emptyArray()
}
// Optional parameters if desired
fun getOptionalParamsNames(): Array<String> {
return arrayOf("sessionCheckUrl")
}
Required Functions and Signatures
Session scripts must implement five functions to fulfill the session management contract:
1. extractWebSession
- Initial Session Extraction
This is the entry point called immediately after authentication completes. Use it to extract session identifiers from the authentication response.
fun extractWebSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
// Extract session data from authentication response
}
The SessionWrapper Object:
The sessionWrapper
parameter provides access to three key objects:
.httpMessage
(or.getHttpMessage()
) - Contains the final HttpMessage from authentication, including:responseBody
- The authentication response (often JSON)responseHeader
- Response headers (may contain Set-Cookie directives)requestingUser
- User context for triggering re-authentication
.session
(or.getSession()
) - Session storage object providing:setValue(String key, Any value)
- Store values for later retrievalgetValue(String key)
- Retrieve stored valueshttpState
- Cookie jar for automatic cookie managementaddCookie(Cookie cookie)
- Add cookies to the jarclearCookies()
- Remove all cookiescookies
- Array of current cookies
.paramValues[key]
(or.getParam(String key)
) - Access script configuration parameters defined instackhawk.yml
Common Tasks in extractWebSession:
- Parse JSON response bodies to extract tokens
- Extract JWT tokens and parse claims for expiration times
- Store tokens in session values for later use
- Add cookies to the HttpState for automatic cookie handling
- Store header names and values for injection into requests
2. processMessageToMatchSession
- Request Enhancement
This function is called before every single request during scanning. Use it to inject authentication data into outgoing requests.
fun processMessageToMatchSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
// Inject authentication data into each request
}
The SessionWrapper in This Context:
.httpMessage
now represents the current outbound requestrequestHeader
- Populated with default values, ready for modificationrequestBody
- Request body if applicableresponseHeader/responseBody
- Empty (not yet sent)
Common Tasks in processMessageToMatchSession:
- Inject Authorization headers from stored session values
- Check token expiration and trigger re-authentication if needed
- Add custom authentication headers to requests
- Log authentication status for debugging
- Handle token refresh logic
IMPORTANT: Cookies stored in sessionWrapper.session.httpState
are automatically added to requests. You do not need to manually inject cookies.
3. clearWebSessionIdentifiers
- Session Cleanup
Called when a session needs to be cleared before re-authentication.
fun clearWebSessionIdentifiers(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
// Clean up session state
}
Common Tasks:
- Clear stored session values using
setValue(key, null)
- Clear cookies using
httpState.clearCookies()
- Remove JWT expiration data
- Clear any cached authentication data
4. getRequiredParamsNames
- Required Configuration
Returns an array of parameter names that must be provided in the configuration.
fun getRequiredParamsNames(): Array<String> {
return arrayOf("jwt_token_field", "validation_url")
}
HawkScan will throw an error at startup if these parameters are not provided in the sessionScript.parameters
section of stackhawk.yml
.
5. getOptionalParamsNames
- Optional Configuration
Returns an array of optional parameter names.
fun getOptionalParamsNames(): Array<String> {
return arrayOf("token_type_field", "sessionCheckUrl")
}
Your script should check for the existence of optional parameters and adjust behavior accordingly.
Advanced Example: JWT Token Management with Expiration
This example demonstrates a complete JWT session management script with automatic token expiration handling:
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 com.stackhawk.hste.session.ScriptBasedSessionManagementMethodType
import com.stackhawk.hste.extension.script.ScriptVars
val logger = LogManager.getLogger("jwt-session")
val mapper = ObjectMapper()
fun extractWebSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
// Get configuration parameters for token extraction
val tokenField = sessionWrapper.getParam("jwt_token_field")
val tokenType = sessionWrapper.getParam("token_type_field") ?: "Bearer"
logger.info("Extracting JWT token from authentication response")
// Parse JSON response body to extract the JWT token
val jsonObject = mapper.readValue(
sessionWrapper.httpMessage.responseBody.bytes,
ObjectNode::class.java
)
val accessToken = jsonObject.get(tokenField).asText()
// Store the complete Authorization header value as a global variable
// This allows other scripts (like httpsender scripts) to access it
ScriptVars.setGlobalVar("auth_header_value", "$tokenType $accessToken")
// Store the raw JWT in the session for later use
sessionWrapper.session.setValue("jwt", accessToken)
// Parse the JWT to extract claims, particularly the expiration time
val jwt = SignedJWT.parse(accessToken)
logger.info("JWT extracted - expires: ${jwt.jwtClaimsSet.expirationTime}")
// Store the JWT claims for expiration checking
sessionWrapper.session.setValue("jwt_claims", jwt.jwtClaimsSet)
}
fun processMessageToMatchSession(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
// Check if the JWT token is expired or about to expire
val nowish = Instant.now().minusMillis(15000) // 15-second buffer
val jwtClaims = sessionWrapper.session.getValue("jwt_claims") as JWTClaimsSet?
val isExpired = jwtClaims?.expirationTime?.toInstant()?.isBefore(nowish)
// If token is expired, trigger re-authentication
if (isExpired == true) {
logger.info("JWT token expired at ${jwtClaims.expirationTime} - re-authenticating")
synchronized(this) {
sessionWrapper.httpMessage.requestingUser.authenticate()
}
}
logger.debug("Current JWT: ${sessionWrapper.session.getValue("jwt")}")
// Inject the Authorization header into the request
val hdrVal = ScriptVars.getGlobalVar("auth_header_value")
logger.debug("Adding Authorization header: $hdrVal")
if (!hdrVal.isNullOrEmpty()) {
sessionWrapper.httpMessage.requestHeader.setHeader("Authorization", hdrVal)
}
}
fun clearWebSessionIdentifiers(sessionWrapper: ScriptBasedSessionManagementMethodType.SessionWrapper) {
// Clear JWT-related session data
sessionWrapper.session.setValue("jwt", null)
sessionWrapper.session.setValue("jwt_claims", null)
ScriptVars.setGlobalVar("auth_header_value", "")
}
fun getRequiredParamsNames(): Array<String> {
return arrayOf("jwt_token_field")
}
fun getOptionalParamsNames(): Array<String> {
return arrayOf("token_type_field")
}
Script Logic Detailed Breakdown
Imports and Setup
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
Jackson library for JSON parsing - essential for extracting tokens from JSON responses.
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
Nimbus JWT library for parsing and validating JWT tokens, including claim extraction.
import com.stackhawk.hste.extension.script.ScriptVars
Utility for setting and getting global variables accessible across different script types.
Token Extraction Logic
val tokenField = sessionWrapper.getParam("jwt_token_field")
Retrieves the JSON field name from configuration (e.g., "access_token"
, "id_token"
).
val jsonObject = mapper.readValue(
sessionWrapper.httpMessage.responseBody.bytes,
ObjectNode::class.java
)
val accessToken = jsonObject.get(tokenField).asText()
Parses the authentication response as JSON and extracts the token using the configured field name.
ScriptVars.setGlobalVar("auth_header_value", "$tokenType $accessToken")
Stores the complete Authorization
header value as a global variable. This is useful if other scripts (like httpsender scripts) need access to the token.
val jwt = SignedJWT.parse(accessToken)
sessionWrapper.session.setValue("jwt_claims", jwt.jwtClaimsSet)
Parses the JWT to extract claims, particularly the expiration time (exp
claim). Storing the claims allows efficient expiration checking without repeatedly parsing the token.
Expiration Handling
val nowish = Instant.now().minusMillis(15000) // 15-second buffer
Creates a time buffer to trigger re-authentication slightly before actual expiration, preventing race conditions.
val jwtClaims = sessionWrapper.session.getValue("jwt_claims") as JWTClaimsSet?
val isExpired = jwtClaims?.expirationTime?.toInstant()?.isBefore(nowish)
Retrieves stored claims and checks if the expiration time has passed.
synchronized(this) {
sessionWrapper.httpMessage.requestingUser.authenticate()
}
Thread safety is critical! Multiple scanning threads may detect expiration simultaneously. The synchronized
block ensures only one thread triggers re-authentication.
Header Injection
sessionWrapper.httpMessage.requestHeader.setHeader("Authorization", hdrVal)
Injects the Authorization header into every request made during scanning. Without this, authenticated endpoints would return 401 Unauthorized errors.
Required HSTE Configuration
Session scripts require configuration in two places in stackhawk.yml
:
1. App Configuration (if using script-based session management)
app:
host: https://example.com
authentication:
usernamePassword:
usernameField: "email"
passwordField: "password"
loginPath: "/api/v1/login"
loggedInIndicator: "\\Qaccess_token\\E"
loggedOutIndicator: "\\Q401 Unauthorized\\E"
scanUsername: "${USERNAME}"
scanPassword: "${PASSWORD}"
# Link session management to the script
sessionScript: # Nested under authentication (NOT app.sessionManagement)
name: "jwt-session.kts" # Script filename - must match hawkAddOn name
parameters: # Passed via getRequiredParamsNames()/getOptionalParamsNames()
jwt_token_field: "access_token"
token_type_field: "Bearer"
# NO credentials field for session scripts
2. HawkAddOn Script Configuration
hawkAddOn:
scripts:
- name: "jwt-session.kts" # Must match app.authentication.sessionScript.name
type: session
path: "hawkscripts" # Parent directory only (will look in hawkscripts/session/)
language: KOTLIN
Configuration Parameters Explained:
name
: Script filename - must matchapp.authentication.sessionScript.name
type
: Must besession
path
: Parent directory only - type subdirectory added automatically (e.g., “hawkscripts” not “hawkscripts/session”)language
:KOTLIN
- Note: Session scripts do NOT have a
credentials
field (unlike authentication scripts)
Complete Configuration Example: External Command + Session Script
This example shows external command authentication combined with session management:
app:
host: https://example.com
authentication:
external:
type: script
command: "python3"
parameters: ["auth/get-token.py"]
timeout: 30
sessionScript:
name: "external-auth-session.kts"\
parameters:
validation_url: "https://example.com/api/v1/user/profile"
validation_regex: ".*200 OK.*"
hawkAddOn:
scripts:
- name: "external-auth-session.kts"
type: session
path: "hawkscripts" # Parent directory only
language: KOTLIN
How This Works:
- HawkScan runs
python3 auth/get-token.py
to authenticate - The Python script outputs JSON with headers and cookies
- The session script (
external-auth-session.kts
) parses the JSON - Headers and cookies are extracted and stored
- Every request during scanning includes these headers
- The validation endpoint is checked to confirm authentication success
Common Patterns and Best Practices
Pattern 1: JWT Expiration with Buffer Time
// Check expiration with a 5-minute buffer to prevent edge cases
val expirationTime = Instant.ofEpochMilli(jwtExpiresAt)
val currentTime = Instant.now()
val refreshBuffer = 300L // 5 minutes
if (currentTime.isAfter(expirationTime.minusSeconds(refreshBuffer))) {
logger.warn("JWT token expiring soon - re-authenticating")
synchronized(this) {
sessionWrapper.httpMessage.requestingUser.authenticate()
}
}
Pattern 2: Defensive Null Checking
// Always check for null when retrieving session values
val token = sessionWrapper.session.getValue("jwt")?.toString()
if (token.isNullOrEmpty()) {
logger.error("JWT token missing from session - authentication may have failed")
return
}
Pattern 3: Cookie Management with HttpState
// Cookies in HttpState are automatically added to requests
// Just add them once during extractWebSession:
val cookie = Cookie(
domain, // e.g., "example.com"
name, // e.g., "JSESSIONID"
value, // cookie value
"/", // path
null, // expiration (null = session cookie)
false // secure flag
)
sessionWrapper.session.httpState.addCookie(cookie)
// No need to manually add cookies in processMessageToMatchSession!
Pattern 4: Multiple Header Injection
// Store multiple headers during extraction
val headers = mapOf(
"Authorization" to "Bearer token123",
"X-API-Key" to "api_key_value",
"X-Tenant-ID" to "tenant_123"
)
headers.forEach { (name, value) ->
sessionWrapper.session.setValue("header_$name", value)
}
sessionWrapper.session.setValue("header_names", headers.keys.joinToString(","))
// Inject all headers during processMessageToMatchSession
val headerNames = sessionWrapper.session.getValue("header_names")
?.toString()
?.split(",") ?: emptyList()
headerNames.forEach { name ->
val value = sessionWrapper.session.getValue("header_$name")?.toString()
if (!value.isNullOrEmpty()) {
sessionWrapper.httpMessage.requestHeader.setHeader(name, value)
}
}
Troubleshooting
Issue: “Session script not being called”
Symptoms: Your session script’s log messages don’t appear in hawkscan.log
.
Solutions:
- Verify
app.authentication.sessionScript.name
matches the scriptname
inhawkAddOn.scripts
- Check that the script path is correct relative to project root
- Ensure
type: session
is set in the hawkAddOn configuration - Look for script compilation errors at HawkScan startup
Issue: “401 Unauthorized during scanning”
Symptoms: Authentication succeeds, but requests during discovery/scanning fail with 401.
Solutions:
- Verify
processMessageToMatchSession
is injecting headers correctly - Add debug logging to see what headers are being added
- Check if token expiration is triggering too early
- Verify the header name and format match what the server expects
- Use
hawk perch
to test authentication and session management interactively
Issue: “JWT token expiration not being detected”
Symptoms: Scanner continues using expired tokens without re-authenticating.
Solutions:
- Verify JWT parsing succeeded in
extractWebSession
- Check that
jwt_claims
is being stored correctly - Add logging to show current time vs. expiration time
- Ensure the
exp
claim exists in your JWT - Verify time zones are handled correctly (use
Instant
notDate
)
Issue: “Cookies not being sent with requests”
Symptoms: Session cookies extracted during authentication aren’t included in subsequent requests.
Solutions:
- Verify cookies are being added to
sessionWrapper.session.httpState
, not stored as session values - Check cookie domain matches the target host
- Ensure cookie path is
/
or matches request paths - Verify cookies aren’t expired
- Check if
clearWebSessionIdentifiers
is being called unexpectedly
Issue: “Script parameter not found”
Symptoms: sessionWrapper.getParam("param_name")
returns null.
Solutions:
- Verify parameter name spelling matches exactly between script and configuration
- Check if parameter should be in
getRequiredParamsNames()
orgetOptionalParamsNames()
- Ensure parameter is defined in
hawkAddOn.scripts.vars
section - Add null-checking and default values for optional parameters
Testing Session Scripts
Using hawk perch
# Start interactive testing mode
hawk perch --config stackhawk.yml
# In the perch prompt:
auth # Test authentication
session # View session details
request /api/endpoint # Test a request with session data
Manual Testing Workflow
- Test Authentication First:
hawk validate auth --config stackhawk.yml
- Add Debug Logging:
logger.info("Extracted token: ${accessToken.take(20)}...") logger.info("Session values: ${sessionWrapper.session.getValue("jwt")}") logger.debug("Request headers: ${sessionWrapper.httpMessage.requestHeader}")
- Run a Limited Scan:
hawk scan --config stackhawk.yml --spider-max 5
- Review hawkscan.log:
tail -f hawkscan.log | grep -i "jwt-session"
Key Objects Reference
For detailed information on classes and methods used in session scripts, see Key Objects Documentation:
- ScriptBasedSessionManagementMethodType.SessionWrapper - Main session management interface
- HttpMessage - HTTP request/response encapsulation
- HttpRequestHeader / HttpResponseHeader - Header manipulation
- HttpState - Cookie jar for automatic cookie management
- ScriptVars - Global variable storage across script types
Additional Resources
- GitHub Examples Repository
- Authentication Script Documentation
- SDK setup and IntelliJ integration description coming soon