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 []