One-Line Python Decorator to Auto‑Encrypt, Sign, and Secure API Requests

Learn how to replace repetitive manual signing, encryption, timestamp, and nonce handling in API tests with a single Python @secure_request decorator that automatically encrypts specified fields, adds security parameters, generates signatures, and sends the request, improving maintainability and reducing errors.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
One-Line Python Decorator to Auto‑Encrypt, Sign, and Secure API Requests

Background

Modern API automation testing often requires digital signatures, encryption of sensitive parameters, and inclusion of timestamp and nonce to prevent replay attacks. Manually constructing these elements for each test case leads to duplicated code, high error risk, and difficult maintenance.

Typical Pain Points

Repeated logic for signing, encrypting, and adding security fields in every test.

Algorithm changes require modifications across all test cases.

Easy to miss fields such as nonce or timestamp.

Solution: @secure_request Decorator

The decorator abstracts all security concerns. By decorating a function that returns a plain‑text parameter dictionary, the decorator automatically encrypts configured fields, injects timestamp and nonce, generates a signature, and sends the HTTP request.

@secure_request(
    encrypt_fields=["phone", "id_card"],
    secret_key="my_secret_2026",
    sign_algorithm="md5"
)
def create_user(name: str, phone: str):
    return {"name": name, "phone": phone, "action": "register"}

# Test case
resp = create_user("Alice", "13800138000")  # Encryption, signing, and request are automatic
assert resp.json()["code"] == 200

Core Implementation

import os, time, random, hashlib, json, functools
from typing import List, Dict, Any, Optional
from Crypto.Cipher import AES  # requires pycryptodome
from Crypto.Util.Padding import pad
import base64, requests

def random_str(length: int = 8) -> str:
    chars = "abcdefghijklmnopqrstuvwxyz0123456789"
    return ''.join(random.choice(chars) for _ in range(length))

def aes_encrypt(data: str, key: str) -> str:
    key_bytes = key.encode('utf-8')[:16].ljust(16, b'\0')
    cipher = AES.new(key_bytes, AES.MODE_ECB)
    padded_data = pad(data.encode('utf-8'), AES.block_size)
    encrypted = cipher.encrypt(padded_data)
    return base64.b64encode(encrypted).decode('utf-8')

def secure_request(
    url: str,
    method: str = "POST",
    encrypt_fields: Optional[List[str]] = None,
    secret_key: str = os.getenv("API_SECRET_KEY", ""),
    sign_algorithm: str = "md5",
    timeout: int = 10
):
    """Decorator that adds encryption, signature, timestamp, and nonce to API requests.
    :param url: Full endpoint URL
    :param method: HTTP method
    :param encrypt_fields: List of field names to encrypt
    :param secret_key: Signing key
    :param sign_algorithm: "md5" or "sha256"
    :param timeout: Request timeout in seconds
    """
    encrypt_fields = encrypt_fields or []
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            raw_params = func(*args, **kwargs)
            if not isinstance(raw_params, dict):
                raise ValueError("Decorated function must return a dict")
            params = raw_params.copy()
            params["timestamp"] = str(int(time.time()))
            params["nonce"] = random_str()
            for field in encrypt_fields:
                if field in params:
                    params[field] = aes_encrypt(str(params[field]), secret_key)
            sign_items = sorted(params.items())
            sign_str = "&".join([f"{k}={v}" for k, v in sign_items]) + f"&key={secret_key}"
            if sign_algorithm == "md5":
                sign = hashlib.md5(sign_str.encode()).hexdigest()
            elif sign_algorithm == "sha256":
                sign = hashlib.sha256(sign_str.encode()).hexdigest()
            else:
                raise ValueError(f"Unsupported sign algorithm: {sign_algorithm}")
            params["sign"] = sign
            if method.upper() == "GET":
                resp = requests.get(url, params=params, timeout=timeout)
            else:
                resp = requests.post(url, json=params, timeout=timeout)
            return resp
        return wrapper
    return decorator

Usage Example

@secure_request(
    url="https://api.example.com/v1/user",
    method="POST",
    encrypt_fields=["phone", "id_card"],
    secret_key="MySuperSecretKey2026!",
    sign_algorithm="sha256"
)
def register_user(name: str, phone: str, id_card: str):
    """Return plain parameters; decorator handles security automatically"""
    return {"name": name, "phone": phone, "id_card": id_card, "channel": "auto_test"}

# Test case
resp = register_user("张三", "13800138000", "11010119900307XXXX")
assert resp.status_code == 200
assert resp.json()["success"] is True

In the actual request body, phone and id_card appear as Base64‑encoded AES ciphertexts, together with timestamp, nonce, and the generated sign.

Security Enhancement Suggestions

Support Chinese national algorithms SM2/SM4 by replacing aes_encrypt with a gmssl implementation.

Use dynamic secret‑key management (e.g., fetch from Vault or KMS) instead of hard‑coding.

Enforce replay‑attack protection by validating timestamp within an acceptable window and ensuring nonce uniqueness.

Require HTTPS URLs; raise a warning if the endpoint does not start with https://.

Integration with Test Frameworks

Provide a pytest fixture to inject secret_key per environment.

Attach sanitized request parameters to Allure reports for auditability.

Make the decorator compatible with existing mock utilities to skip security logic in mock mode.

Why This Decorator Is a Powerful Tool for Secure Testing

Compared with the traditional approach of manually writing encryption and signing logic in each test case, the decorator offers a one‑line configuration that automatically handles all security steps, eliminates code duplication, reduces the risk of missing fields, and isolates business logic from security concerns.

encryptionAPI securitysignatureDecorator
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.