PoP Token Signature

Prev Next

Proof of Possession (PoP) tokens are security tokens that include a cryptographic key the client uses to sign each API request. The server verifies the signature to ensure that the client is in possession of the corresponding private key.

Configurations

PoP's authentication mechanism has the following configurations:

Configuration

Description

Private Key

A private key is a confidential cryptographic key used to generate digital signatures and prove ownership of a corresponding public key. It must remain secret to ensure the integrity and authenticity of signed data.

Header Key

A header key is the name or identifier of a field included in the header of an HTTP request or token. It provides metadata or additional information about the request or the token, such as authentication details, content type, or custom attributes.

Header Value Template

{{value}}


Example

The following are some samples that you can use to configure the POP Token Signature mechanism in the Advanced mode:

Sample 1

import json
import time
import hashlib
import base64
import hmac

def generate_pop_token(private_key, http_method, url, headers=None, body=None):
    timestamp = str(int(time.time()))
    nonce = base64.urlsafe_b64encode(hashlib.sha256(timestamp.encode()).digest()).decode().strip("=")

    if headers is None:
        headers = {}

    if body is None:
        body = ''

    # Step 1: Create the Canonical Request
    canonical_request = '\n'.join([
        http_method,
        url,
        headers.get('Content-Type', ''),
        headers.get('Authorization', ''),
        '',
        body
    ])

    # Step 2: Create the String to Sign
    string_to_sign = '\n'.join([
        "POP-RS256",
        timestamp,
        nonce,
        hashlib.sha256(canonical_request.encode()).hexdigest()
    ])

    # Step 3: Generate the Signature
    signature = base64.urlsafe_b64encode(hmac.new(private_key.encode(), string_to_sign.encode(), hashlib.sha256).digest()).decode().strip("=")

    # Step 4: Create the Authorization Header
    auth_header = f'Authorization: POP-RS256 keyId="*", algorithm="rs256", headers="(request-target) host date", signature="{signature}", nonce="{nonce}", timestamp="{timestamp}"'

    return auth_header


def pop_token_signature(scanctx: ScanContext, pluginctx: PluginContext, testcase: TestCase, **kwargs) -> list[Assertion]:
    attributes = testcase.get_attributes()
    private_key = "your_private_key"
    http_method = attributes.get_one("mutated.http.request.method", default="")
    api_url = attributes.get_one("mutated.http.request.url", default="")

    request_body = attributes.get_one("mutated.http.request.body", default=None)
    body = json.dumps(request_body)

    # Create the POP token signature
    signature = generate_pop_token(private_key, http_method, api_url, headers=headers, body=body)

    header_key = "header_key"
    attributes.set("mutated.auth.attribute", "mutated.http.request.header." + header_key)

    # set user
    normal_user = True
    bola_user = False

    if normal_user:
        attributes.set("mutated.role.user", signature)
        attributes.set("mutated.http.request.header." + header_key, signature)
    if bola_user:
        attributes.set("mutated.role.bolauser", signature)
    return []