API Key

Prev Next

An authentication mechanism based on an API Key is a security method used in software development and web services. It involves generating and providing a unique alphanumeric key (API Key) to identify and authenticate the user or application accessing an API. The server verifies the key before granting access.

Configurations

The API Key authentication mechanism has the following configurations:

Configuration

Description

API Key

The unique key is generated for the user or the applications.

Add the API Key as part of the Query parameter

Query Key — The API key to be passed in the URL or query parameters of the API request.

The query key is used for authentication and authorization.

When using a Query Key, the API Key is appended to the URL as a parameter, usually in the following format:

https://api.example.com/resource?api_key=<value>

In this example, api_key is the query parameter name, and <value> is the API Key associated with your application or user account. The API server extracts the API Key from the query parameters and uses it to authenticate and authorize the request.

Query value template (optional) — The template defines how to format the API key in the query parameter of an API request URL. Traceable dynamically substitutes the required authentication value into the request at runtime.

For example, consider an API endpoint that requires an API key in the query string:

https://api.example.com/resource?api_key={{value}}

In this template, you can replace the {{value}} placeholder with the actual API Key.

The Query Value Template keeps the API Key separate from the code logic, allows you to update it easily, and strengthens security because it does not expose the key in plaintext within the codebase.

Add the API Key as part of the Header

Header key — The HTTP header used to pass the API key (commonly Authorization).

The header key is used to secure API endpoints by transmitting authentication credentials in the HTTP headers rather than as part of the URL or request body.

Header value template (optional) — The template defines how to format the API key in the header of an API request URL. Traceable dynamically substitutes the required authentication value into the header at runtime. Using a header value template increases security and flexibility when transmitting authentication credentials.

Following is an example of how you can use a template:

  1. Template in the Header Value — Instead of hard-coding the API Key or authentication token directly into the header, you use a template or placeholder. For example:

    Authorization: Bearer {{value}}

    In this example, {{value}} is a template for the actual API Key.

  2. Dynamic Replacement — When you make an API request, your application or code dynamically replaces {{value}} with the API Key or authentication token before sending the request to the API server. This ensures that the actual authentication credential is used in the header.

Example Request with Header Value Template —

GET /api/resource HTTP/1.1
Host: api.example.com
Authorization: Bearer <your_actual_api_key_here>


The Header Value Template keeps the API Key separate from the code logic, allows you to update it easily, and strengthens security because it does not expose the key in plaintext within the codebase.

Note

The specific syntax and conventions for header value templates may vary depending on the API and its documentation.

Add the API Key as part of the Cookie

Cookie key — The Cookie value used for authentication.

Note

Cookie key is not a standard authentication mechanism in API key authentication.

Cookie value template (optional) — A template to add a cookie value, similar to templates for Query Parameter and Header.

Example

The following are some samples that you can use to configure an API key authentication in the Advanced mode:

Sample 1

def api_key_hook(scanctx: ScanContext, pluginctx: PluginContext, testcase: TestCase, **kwargs) -> list[Assertion]:
    attributes = testcase.get_attributes()

    # Replace these with your actual values
    csrf_token_value = "CSRF_TOKEN_VALUE"
    session_cookie_value = "SESSION_TOKEN_VALUE"

    # Keys for header and cookie
    csrf_token_key = "TOKEN_VALUE"
    session_cookie_key = "TOKEN_VALUE"

    # Set the mutated attribute keys
    csrf_token_attr = f"mutated.http.request.header.{csrf_token_key}"
    session_cookie_attr = f"mutated.http.request.cookie.{session_cookie_key}"

    # Set attributes for CSRF header and session cookie
    attributes.set(csrf_token_attr, csrf_token_value)
    attributes.set(session_cookie_attr, session_cookie_value)

    # Set user roles if needed
    # normal_user = True
    # bola_user = False

    # if normal_user:
    #     attributes.set("mutated.role.user", csrf_token_value)
    # if bola_user:
    #     attributes.set("mutated.role.bolauser", csrf_token_value)

    return []

Sample 2

import re
import requests
import os

login_endpoint = "TOKEN_VALUE"
login_form_endpoint = "TOKEN_VALUE"
token_regex = re.compile(r"authenticity_token\" value=\"(.+)\" autocomplete=")
username = os.environ.get("TOKEN_VALUE")
password = os.environ.get("TOKEN_VALUE")


def get_token():
    result = requests.get(login_form_endpoint)
    if result.status_code != 200:
        raise Exception("Failed to get token")
    token = token_regex.search(result.text).group(1)
    return token, result.cookies.get_dict()
    
def authenticate(username, password):
    token, cookies = get_token()
    result = requests.post(login_endpoint, data={
            "user[email]": username,
            "user[password]": password,
            "brand_id": TOKEN_VALUE,
            "authenticity_token": token
        }, 
        headers={
            "Cookie": "; ".join([f"{k}={v}" for k, v in cookies.items()]),
            "Content-Type": "application/x-www-form-urlencoded",
        }
    )
    if result.status_code != 200:
        raise Exception("Authentication failed with token %s and cookies %s" % (token, cookies), result.text)
    if "is incorrect," in result.text:
        raise Exception("Authentication failed with token %s and cookies %s" % (token, cookies), result.text)
    cookies = result.cookies.get_dict()
    cookies = {k: v for k, v in cookies.items() if k.startswith("_")}
    return cookies


def api_key_hook(scanctx: ScanContext, pluginctx: PluginContext, testcase: TestCase, **kwargs) -> list[Assertion]:
    attributes = testcase.get_attributes()
    cookies = {}
    if scanctx.get("custom_cookies", None): 
        cookies = scanctx.get("custom_cookies")
    else:
        cookies = authenticate("username", "password")
        scanctx.set("custom_cookies", cookies)

    #attributes.set("mutated.auth.attribute",
    #            "mutated.http.request.header.authorization")
    for key, value in cookies.items():
        attributes.set("mutated.http.request.cookie." + key, value)
    return []

Sample 3

import requests
import json
from traceable.config import logger
from traceable.ast.context import ScanContext, PluginContext
from traceable.ast.testsuite.assertion import Assertion
from traceable.ast.testsuite.plugin import TestCase

url = "TOKEN_VALUE"
headers = {"Accept-Encoding": "gzip,deflate", "Connection": "Keep-Alive", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8; text/plain", "Cache-Control": "no-cache"}
data = {"client_id": "TOKEN_VALUE", "client_secret": "TOKEN_VALUE", "token_type": "signed"}

# function to get token
def get_token(url, headers, data):
    try:
        result = requests.post(url, headers=headers, data=data)
        if result.status_code != 200:
            logger.error(f"Check if connected on the DGN: {result.text}")
            return None
            
        if "Bad request," in result.text:
            logger.error(f"Wrong/expired client ID or secret: {result.text}")
            return None
            
        json_response = result.json()
        return json_response.get("access_token")
        
    except Exception as e:
        logger.error(f"Error in get_token: {str(e)}")
        return None

def api_key_hook(scanctx: ScanContext, pluginctx: PluginContext, testcase: TestCase, **kwargs) -> list[Assertion]:
    attributes = testcase.get_attributes()
    
    # Get token from context or fetch new one
    token = scanctx.get("token")
    if token is None:
        token = get_token(url, headers, data)
        if token:
            scanctx.set("token", token)
            logger.info(f"Retrieved new token: {token}")
        else:
            return []
    
    # Set the authorization header with the token
    if token:
        attributes.set("mutated.http.request.header.authorization", f"Authorization: Tut {token}")
        
    return []