Inject Multiple Cookies and Tokens

Overview

Sometimes authentication is not performed with just a username and password. For example, API key access or third-party authentication services like OAuth require custom tokens.

To support this type of authentication, HawkScan supports externally supplying authorization tokens with the authentication.external configuration.

The authentication section of the stackhawk.yml will have 3 parts:

  1. Logged in/out indicators
    • How HawkScan checks it is logged in throughout the scan.
  2. Auth(Z)
    • Using authentication.external to tell HawkScan you will be injecting valid cookies or tokens for authorization
  3. Test Path
    • How HawkScan sees if it successfully logged in

Make sure your file has all 3 of these parts filled out.

YAML for Injecting Credentials

Injecting Tokens:

stackhawk.yml

app:
  applicationId: xxXXXXXX-xXXX-xxXX-XXxX-xXXxxXXXXxXX
  env: Test
  host: ${APP_HOST:http://localhost:3000}
  authentication:
    # Paths that HawkScan checks to see if it is still logged in during the scan
    loggedInIndicator: "HTTP.*2[0-9][0-9]\\s*O[kK](\\s*)|HTTP.*3[0-9][0-9].*" # Change me
    loggedOutIndicator: "HTTP.*4[0-9][0-9](\\s*)Unauthorized.*" # Change me
    # Auth(Z) External Injection - this example will result in 2 request headers:
    #   Authorization: Bearer ${AUTH_TOKEN}
    #   AnotherToken: ${ANOTHER_TOKEN}
    external:
      values:
          # Indicates whether the injected value is a cookie or token
        - type: TOKEN
          # If specified tokenType will be prepended the header value e.g. "Bearer xxxxxxxxx"
          tokenType: Bearer
          value:
            # Name of the request header
            name: Authorization
            # Value of token
            val: ${AUTH_TOKEN}
        - type: TOKEN
          value:
            name: AnotherToken
            val: ${ANOTHER_TOKEN}
    # A path that can only be seen when successfully logged in. HawkScan will check this path to see if log in was successfull
    testPath:
      path: /mysettings
      success: ".*200.*"
      requestMethod: GET

Injecting Cookies:

stackhawk.yml

app:
  applicationId: xxXXXXXX-xXXX-xxXX-XXxX-xXXxxXXXXxXX
  env: Test
  host: ${APP_HOST:http://localhost:3000}
  authentication:
    # Paths that HawkScan checks to see if it is still logged in during the scan
    loggedInIndicator: "HTTP.*2[0-9][0-9]\\s*O[kK](\\s*)|HTTP.*3[0-9][0-9].*" # Change me
    loggedOutIndicator: "HTTP.*4[0-9][0-9](\\s*)Unauthorized.*" # Change me
     # Auth(Z) External Injection
    external:
      values: 
          # Indicates whether the injected value is a cookie or token
        - type: COOKIE
          value:
            # Name of the cookie
            name: JSESSIONID
            # Value of the cookie
            val: ${AUTH_COOKIE}
        - type: COOKIE
          value:
            name: EXTRAID
            val: ${ANOTHER_COOKIE}
    # A path that can only be seen when successfully logged in. HawkScan will check this path to see if log in was successfull
    testPath:
      path: /login-multi-check #Change me
      success: ".*200.*"
      requestMethod: GET

YAML Sections in Detail

Injecting Authorization with .external

The first half of your authentication section in your stackhawk.yml will be to tell HawkScan you are using an externally supplied authorization.

The .values will be a list off cookies or header tokens or both that will be injected into each request that StackHawk sends to the application.

For the .type of TOKEN, headers are added to each request in this format “.name: .tokenType .val”.

For the .type of COOKIE, cookes are added to wach request in this format “Cookie: .name=.val”.

Values for tokens and cookies will need to be added at run time either in your run command or as stored secrets. We do not recommend adding it directly to your stackhawk.yml file. This is both for security reasons, but also to avoid errors if the token expires.

stackhawk.yml

app:
  applicationId: xxXXXXXX-xXXX-xxXX-XXxX-xXXxxXXXXxXX
  env: Test
  host: ${APP_HOST:http://localhost:3000}
  authentication:
    loggedInIndicator: "\\QLog out\\E"
    loggedOutIndicator: "\\QLog in\\E"
    external:
      values: 
        - type: COOKIE
          value:
            name: JSESSIONID
            val: ${AUTH_COOKIE}
        - type: TOKEN
          tokenType: Bearer
          value:
            name: Authorization
            val: ${AUTH_TOKEN}
    testPath:
      path: /authenticated/path
      success: '.*200.*'

Maintaining the Session

Unlike other authentication types, the session is maintained by injecting the headers and cookies into each request as indicated in the stackhawk.yml.

Test Paths and Logged In/Out Indicators

No matter what type of Authorization/Authentication your app is using, HawkScan requires .testPath, .loggedInIndicator and .loggedOutIndicator.

Test Path:

A testPath configuration needs to be provided to verify HawkScan authenticated its session correctly before scanning the application. The testPath configuration also provides requestMethod and requestBody options to support alternate HTTP request verbs, such as POST or PUT. The default action is GET.

Your testPath should be a path that HawkScan can only access when successfully logged in. This could be a path like “/dashboard” or “/accountdetails”

stackhawk.yml

app:
  applicationId: xxXXXXXX-xXXX-xxXX-XXxX-xXXxxXXXXxXX
  env: Test
  host: ${APP_HOST:http://localhost:3000}
  authentication:
    loggedInIndicator: "\\QLog out\\E"
    loggedOutIndicator: "\\QLog in\\E"
    testPath:
      path: /login-multi-check
      success: ".*200.*"
      requestMethod: GET

Logged In/Out Indicators

Throughout the scan, HawkScan will check to see if it is still logged in by the .loggedInIndicator and .loggedOutIndicator. These are regex strings to match against http responses from requests in the web application.

A .loggedInIndicator could be a “Log Out” or ”Sign Out” button a user would see if logged in. An example of a .loggedOutIndicator would be a “Log In” button on the sign in page. These can also leverage http status codes from the response.

stackhawk.yml

app:
  applicationId: xxXXXXXX-xXXX-xxXX-XXxX-xXXxxXXXXxXX
  env: Test
  host: ${APP_HOST:http://localhost:3000}
  authentication:
    loggedInIndicator: "HTTP.*2[0-9][0-9]\\s*O[kK](\\s*)|HTTP.*3[0-9][0-9].*"
    loggedOutIndicator: "HTTP.*4[0-9][0-9](\\s*)Unauthorized.*"
    testPath:
      path: /authenticated/path
      success: '.*200.*'

This scenario will use the following route layout.

GET       /login-code
GET       /login-form-multi
POST      /login-form-multi
GET       /login-multi-check <- protected route

Detailed Configuration

You would want a stackhawk.yml configuration like this.

stackhawk.yml

app:
  applicationId: xxXXXXXX-xXXX-xxXX-XXxX-xXXxxXXXXxXX
  env: Test
  host: ${APP_HOST:http://localhost:3000}
  authentication:
    loggedInIndicator: "HTTP.*2[0-9][0-9]\\s*O[kK](\\s*)|HTTP.*3[0-9][0-9].*"
    loggedOutIndicator: "HTTP.*4[0-9][0-9](\\s*)Unauthorized.*"
    external:
      values:
      - type: COOKIE
        value:
          name: "XLOGINID"
          val: ${XLOGINID}
      - type: COOKIE
        value:
          name: "JSESSIONID"
          val: ${JSESSIONID}
    testPath:
      path: /login-multi-check
      success: ".*200.*"
      requestMethod: GET

In the above configuration we are specifying .external.values[0].val=${XLOGINID} and .external.values[0].val=${JSESSIONID} to allow the authorization cookies to be passed in as an environment variable at runtime.

Now create a script to generate the authorization cookies. This is an example using a customized route in the javaspringvulny project to generate a session cookie and a login cookie as well as parsing and saving the _csrf token. It logs in the user and then runs hawk scan with those cookie values passed as environment variables.

#!/usr/bin/env bash

# Request login (XLOGINID) and session (JSESSIONID) cookies from server and saves them to the cookie-jar.txt file.
curl -c  cookie-jar.txt https://localhost:9000/login-code
# Parses cookie-jar.txt file to get the JSESSIONID.
JSESSIONID=$(awk 'match($0, /JSESSIONID.*/){print substr($0, RSTART + 11, RLENGTH)}' cookie-jar.txt )
# Parses cookie-jar.txt file to get the XLOGINID.
XLOGINID=$(awk 'match($0, /XLOGINID.*/){print substr($0, RSTART + 9, RLENGTH)}' cookie-jar.txt)
# Request page with cookie-jar.txt cookies and extract the _csrf token from the response
CSRF=$(curl  -b  cookie-jar.txt \
    https://localhost:9000/login-form-multi | awk 'match($0,/_csrf".*/) { print substr($0, RSTART+14, RLENGTH -17)}')

# Log into the mutli cookie endpoint using XLOGINID and JSESSIONID cookies (stored in cookie-jar.txt) and username/password.
# This example also uses the XLONGID as a paremeter passed to the server in the request body.
curl -v -k  \
  -d "_csrf=${CSRF}&loginCode=${XLOGINID}&username=user&password=password&remember=on"  \
  -b  cookie-jar.txt \
  -H  "Content-Type: application/x-www-form-urlencoded" \
  "https://localhost:9000/login-form-multi"

# Run HawkScan injecting local variables as environment variables
hawk scan -e JSESSIONID=${JSESSIONID} -e XLOGINID=${XLOGINID}

Authorized GET Request to /login-multi-check

GET /login-multi-check HTTP/1.1
Host: localhost:9000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/114.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: JSESSIONID=CDDC53E08070E78BF0A8C8F240B2157E; XLOGINID=a30c2167-87c4-4905-9554-66e58a64289c