Inject 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 an authorization token with the authentication.external configuration.

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

  1. Logged in/out indicators
    • How HawkScan checks it is logged in throughout the scan.
  2. Auth(N)
    • Using authentication.external to tell HawkScan you will be injecting a valid session
  3. Auth(Z)
    • How you maintain the session. Either a Cookie or Token. Token recommended for injecting session credentials.
  4. Test Path
    • How HawkScan sees if it successfully logged in

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

YAML for Injecting Credentials

Injecting a Token:

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(N) External Injection
    external:
      type: TOKEN
      value: ${AUTH_TOKEN}
    # Auth(Z) Token
    tokenAuthorization:
      type: HEADER
      value: X-APIKey
    # 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: ".*2[0-9]{2}.*"
      requestMethod: GET

Injecting a cookie for maintaining the session is rare. In most cases, it is better to use traditional form authentication form authentication with a cookie.

When injecting an external cookie, Hawkscan needs to “see” the cookie first. We do this by requesting the testPath twice. After the first request, there is a check to see if the cookie has been sent from the server, usually it is, and then make the second request to check the status.

If HawkScan never sees a Set-Cookie for cookieName, it won’t be able to track it through the testPath route, or any other route. HawkScan will error and you will not be able to run an authenticated scan. In these cases you will most likely need to use the Replacer Plugin for HawkScan to get the correct header required. Contact support with questions.

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(N) External Injection
    external:
      type: TOKEN
      value: ${AUTH_TOKEN}
    # Auth(Z) Cookie
    cookieAuthorization:
      cookieNames:
        - sessionid
    # 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 #Change me
      success: ".*2[0-9]{2}.*"
      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 .value will be the string containing the path to the token in the JSON payload authentication response or the name of the response header containing the token.

This will need to be added at run time either in your run command or as a stored secret. 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:
      type: TOKEN
      value: ${AUTH_TOKEN}
    testPath:
      path: /authenticated/path
      success: '.*200.*'

Maintaining the Session

Once HawkScan has logged in with the external JSON payload, add the following YAML snippet depending on if your application is using cookies or tokens.

Cookies:

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"
    cookieAuthorization:
      cookieNames:
        - sessionid
    testPath:
      path: /authenticated/path
      success: '.*200.*'

Tokens:

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"
    tokenAuthorization:
      type: HEADER
      value: X-APIKey
    testPath:
      path: /authenticated/path
      success: '.*200.*'

Custom Script:

Custom authentication and session management scripts can be used to handle complex authentication and authorization scenarios. If a preconfigured authentication and/or authorization style doesn’t meet your needs you can replace either with a custom script. Visit our GitHub repo to get started.

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: /mysettings
      success: ".*2[0-9]{2}.*"
      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.*'

External Token Authentication + Custom Token Authorization

This scenario will use tokenAuthorization, a custom header for the authorization token and pass an externally generated token from our web application’s JWT library. This will allow us to run HawkScan as any user of our application in a programmatic fashion.

Example External Token Authentication + Custom Token Authorization

This scenario will use the same route layout as the previous scenarios.

POST      /login
GET       /home
GET       /comments
GET|POST  /mysettings <- protected route
POST      /comments <- protected route
GET|POST  /privatemessage <- protected routes

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:
      type: TOKEN
      value: ${AUTH_TOKEN}
    tokenAuthorization:
      type: HEADER
      value: X-APIKey
    testPath:
      path: /mysettings
      success: ".*2[0-9]{2}.*"
      requestMethod: GET

In the above configuration we are specifying external.value=${AUTH_TOKEN} to allow the authorization token to be passed in as an environment variable at runtime.
Also we’re specifying that the authorization token should be passed as the custom header X-APIKey.

Now create a script to generate an authorization token. This is an example using jsonwebtoken to create a valid JWT but this can be done using whatever token library or framework you application uses.

const jwt = require('jsonwebtoken');
const privateKey = ''; //<- this should be your real private key.
var user = {
  "id": 1,
  "username": "",
  "email": "user1@example.com",
  "role": "user",
  "isActive": true,
};
var token = jwt.sign(user, privateKey, { expiresIn: 3600 * 5, algorithm: 'RS256' });
console.log(token);

Next create a script to invoke HawkScan with a token generated by gen-auth-token.js.

#!/usr/bin/env bash

token=$(node gen-auth-token.js)
docker run --rm -v $(pwd):/hawk:rw -e API_KEY=hawk.xxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxx -e AUTH_TOKEN=${token} -t stackhawk/hawkscan

In the above script we first call gen-auth-token.js which will output the token. This value is then passed to HawkScan via -e AUTH_TOKEN=${token}. Now you can run hawkscan.sh to kick off a scan. As with the previous scenarios you can use the provided testPath configuration to verify the generated token can be used to validate requests.

Authorized GET Request to /mysettings

GET /mysettings HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Accept: application/json, text/plain, */*
X-APIKey: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6InVzZXIxQGV4YW1wbGUuY29tIiwicm9sZSI6InVzZXIiLCJpc0FjdGl2ZSI6dHJ1ZSwiaWF0IjoxNTczNTgyOTM1LCJleHAiOjE1NzM2MDA5MzV9.bj7bJJ149RcdipxO6B457ZAVaUcFE8zqD6CHEnK2tRH_XlwGTOv_PAXCGHHrBUHVPAJujo_ThMDJd_zv6q6edPX-iYJYH2UfmjA-vYC6KgOk1eUoAs8qzn0x4UwnO4LgLgTVIYilcWiIpdtG404jIKIGxeBAB4TavsOk0m8-ZsY

POST Authorization

A POST may also be configured in the above example to perform the same authentication verification against an endpoint such as /comments. To do this, provide the requestMethod and requestBody options to the testPath configuration.

For example:

  ...
    testPath:
      path: /comments
      success: ".*200.*"
      requestMethod: POST
      requestBody: user=hawk&comment=KAKAAAW!

Authorized POST Request to /comments

As with the previous example, the authorization verification request will appear as follows.

POST /comments HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Accept: application/json, text/plain, */*
X-APIKey: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6InVzZXIxQGV4YW1wbGUuY29tIiwicm9sZSI6InVzZXIiLCJpc0FjdGl2ZSI6dHJ1ZSwiaWF0IjoxNTczNTgyOTM1LCJleHAiOjE1NzM2MDA5MzV9.bj7bJJ149RcdipxO6B457ZAVaUcFE8zqD6CHEnK2tRH_XlwGTOv_PAXCGHHrBUHVPAJujo_ThMDJd_zv6q6edPX-iYJYH2UfmjA-vYC6KgOk1eUoAs8qzn0x4UwnO4LgLgTVIYilcWiIpdtG404jIKIGxeBAB4TavsOk0m8-ZsY
user=hawk&comment=KAKAAAW!