Unsafe consumption of APIs occurs when an application trusts data from an external or internal API without adequate validation and sanitization. A compromised provider can supply malicious code or malformed values that enter the application, database, or response flow. Even trusted internal services can return unexpected values that trigger cascading failures. section 3.3
Protect API integrations by applying controls at the boundary:
- Validate every response against an application-owned schema, such as an OpenAPI-derived model. section 5.7
- Sanitize externally supplied values and reject invalid responses instead of passing them through. section 5.7
- Parameterize database queries so poisoned payloads cannot become SQL injection statements. section 5.7
- Handle unavailable or malfunctioning dependencies with useful errors and circuit-breaker behavior to limit cascading failures. section 5.7
- Alert the relevant team when another service returns data that violates the expected model. section 3.3
A poisoned character status
One endpoint returns a character status from an unsafe API. The response contains a SQL injection string instead of the allowed values wizard, witch, or muggle. The consuming endpoint inserts that value into a raw update query, allowing the payload to alter database behavior. Parameterized queries prevent the injection, while an application-owned validation model rejects the status because it is not a permitted enum value. section 5.8
Listing 5.7 Preventing injection from third-party APIs
# file: ch05/unsafe_consumption_apis.py
class StatusEnum(str, enum.Enum): #1
wizard = "wizard"
witch = "witch"
muggle = "muggle"
class CharacterStatus(BaseModel): #2
status: StatusEnum
@server.get("/server-b/safe-consumption")
def safe_consumption():
status = requests.get("http://localhost:8000/unsafe-api").json() #3
status_validated = CharacterStatus(status=status) #4
with session_maker() as session:
session.execute(text(
f"update character set status = '{status_validated.status}' "
f"where name = 'Harry Potter'"
))
session.commit()
Inspect the validation model that constrains an external character-status response to the permitted enum values before database use. section 5.8
