client: allow injecting a requests.Session + stop following redirects
The HTTP Session was hardcoded with no injection point and followed redirects by default, so a server-side caller pointing the client at an attacker-influenced base_url (a tenant's prime_endpoint) had no way to attach an SSRF policy, and a public endpoint could 302-redirect the request to an internal address (e.g. the cloud metadata service). - Client/DragonchainSDK now accept an optional `session` so callers can inject a Session whose transport adapter refuses internal IPs. Default stays unguarded for trusted/CLI use — the guard belongs in the server. - Requests are sent with allow_redirects=False; Prime never legitimately redirects, and a 3xx now surfaces to the caller instead of being followed.
This commit is contained in:
@@ -39,13 +39,20 @@ class Client:
|
||||
auth_key: str,
|
||||
base_url: str,
|
||||
timeout: int = DEFAULT_TIMEOUT,
|
||||
session: Optional[requests.Session] = None,
|
||||
):
|
||||
self.public_id = public_id
|
||||
self.auth_key_id = auth_key_id
|
||||
self.auth_key = auth_key
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.timeout = timeout
|
||||
self._session = requests.Session()
|
||||
# A caller may inject a pre-configured Session — e.g. one with a
|
||||
# transport adapter that refuses to connect to internal IPs — when the
|
||||
# base_url is attacker-influenced (a tenant's prime_endpoint). The
|
||||
# default Session is unguarded, which is fine for trusted/CLI use; the
|
||||
# SSRF policy belongs in the server that points this client at
|
||||
# untrusted endpoints, not baked into the client library.
|
||||
self._session = session if session is not None else requests.Session()
|
||||
|
||||
# -- introspection helpers (mirror the Go accessors) --------------------
|
||||
|
||||
@@ -120,8 +127,17 @@ class Client:
|
||||
headers["Content-Type"] = content_type
|
||||
|
||||
url = self.base_url + path
|
||||
# allow_redirects=False: Prime is a JSON API that never legitimately
|
||||
# 3xx-redirects, and following redirects is an SSRF vector (a public
|
||||
# endpoint that 302s to an internal address). A redirect surfaces as a
|
||||
# 3xx the caller can inspect rather than being silently followed.
|
||||
resp = self._session.request(
|
||||
method, url, data=body_bytes, headers=headers, timeout=self.timeout
|
||||
method,
|
||||
url,
|
||||
data=body_bytes,
|
||||
headers=headers,
|
||||
timeout=self.timeout,
|
||||
allow_redirects=False,
|
||||
)
|
||||
|
||||
resp_text = resp.text
|
||||
|
||||
Reference in New Issue
Block a user