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:
- Logged in/out indicators
- How HawkScan checks it is logged in throughout the scan.
- Auth(N)
- Using
authentication.external
to tell HawkScan you will be injecting a valid session
- Using
- Auth(Z)
- How you maintain the session. Either a Cookie or Token. Token recommended for injecting session credentials.
- 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:
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 StackHawk 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: COOKIE
value: mycookie=${COOKIE_VALUE} # Set the combination of injected cookie name and value
# Auth(Z) Cookie
cookieAuthorization:
cookieNames:
- mycookie # injected cookie
# - othercookie # if non-injected cookies exist also, add them to the same cookieNames list
# 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.
Please only add one .value
and .type
combination. HawkScan will only use the last .value
and .type
combination if there are multiple.
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!