Prototype Pollution in Chi with Api Keys
Prototype Pollution in Chi with Api Keys — how this specific combination creates or exposes the vulnerability
Prototype pollution in Chi applications that expose API keys through query parameters, headers, or logs can allow an attacker to modify inherited object properties, potentially bypassing security controls or escalating impact. When API keys are handled as user-influenced values and merged into configuration or request-processing objects without isolation, an attacker can inject properties such as __proto__, constructor.prototype, or custom key paths that affect runtime behavior across requests. This becomes critical when API keys are used to derive permissions, rate-limit decisions, or routing logic, because a polluted prototype can change how key validation or authorization is evaluated.
In Chi pipelines, if route parameters or JSON payloads containing API keys are merged into maps or structs using unsafe operations, an attacker can supply keys like ?api_key=abc&__proto__[rateLimit]=false or embed malicious fields in JSON that propagate via map merging. Because Chi encourages pattern-based routing and parameter binding, developers might inadvertently pass user-controlled values into shared maps that later affect authorization checks. For example, a key-based access check that relies on a merged options map can be bypassed if an attacker pollutes the prototype to alter key presence checks or default flags.
Another scenario involves logging or error handling where API keys are captured in metadata. If logs are structured using objects that share prototypes across requests, a polluted prototype can cause keys to be omitted, duplicated, or misrouted, leading to inconsistent enforcement or information leakage. In distributed setups where Chi services forward requests with keys in headers, an attacker might manipulate forwarded objects to change how keys are validated downstream, especially when services rely on inherited object behavior for policy evaluation.
The combination of Chi’s flexible data merging and API key handling amplifies risk when input validation is shallow. Attack patterns such as property injection via JSON parsing or route parameter pollution map directly to OWASP API Top 10:2023 Broken Object Property Level Authorization and can intersect with BOLA/IDOR if key-based object references are manipulated. middleBrick’s checks for Input Validation and Property Authorization help surface these weaknesses by correlating spec definitions with runtime behavior, highlighting unsafe merging or missing constraints on key-bearing parameters.
Api Keys-Specific Remediation in Chi — concrete code fixes
To mitigate prototype pollution in Chi when handling API keys, isolate key values from prototype-inheritable objects and validate strictly before use. Prefer immutable data structures and explicit key extraction rather than merging user input into shared maps. Below are concrete Chi code examples that demonstrate safe handling.
Example 1: Strict key extraction and validation
Extract the API key from query parameters and validate its format before using it in authorization logic. This prevents attacker-controlled fields from reaching shared objects.
import "dart:core";
import "package:chi/chi.dart";
import "package:http_parser/http_parser.dart";
Future<void> main() async {
final app = Chi();
app.get("/resource", (req, res) {
// Explicitly read the key
final apiKey = req.queryParams["api_key"] as String? ?? "";
// Validate format (e.g., expected length and character set)
if (!RegExp(r"^[A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+\.?[A-Za-z0-9\-_=]*$") .hasMatch(apiKey)) {
res.status(400).json({"error": "invalid_api_key"});
return;
}
// Perform key lookup in a clean map that does not share prototypes
final validKeys = {"abc123": "read", "xyz789": "write"};
final scope = validKeys[apiKey];
if (scope == null) {
res.status(403).json({"error": "forbidden"});
return;
}
res.json({"scope": scope});
});
await app.listen("0.0.0.0:8080");
}
Example 2: Safe options merging using copyWith
When building options for downstream clients, avoid merging maps that may contain inherited properties. Use a controlled copy pattern to ensure API keys and related settings remain isolated.
import "dart:core";
import "package:chi/chi.dart";
Map<String, dynamic> safeOptions(Map<String, dynamic> base, Map<String, dynamic> overrides) {
// Create a new map and explicitly copy known keys, ignoring injected prototypes
final result = Map<String, dynamic>.from(base);
overrides.forEach((key, value) {
if (key != "__proto__" && key != "constructor" && key != "prototype") {
result[key] = value;
}
});
return result;
}
void main() {
final base = {"timeout": 30, "api_key": "abc123"};
final userOverrides = {"api_key": "hijacked", "__proto__": {"rateLimit": false}};
final options = safeOptions(base, userOverrides);
// options will not contain __proto__ pollution
print(options); // {timeout: 30, api_key: hijacked}
}
Example 3: Logging without leaking or polluting
When logging API interactions, ensure log builders do not share mutable prototypes and sanitize keys before including them in structured logs.
import "dart:core";
import "package:chi/chi.dart";
void logRequest(String apiKey) {
// Create a fresh map for logging, avoiding shared prototypes
final logEntry = {
"event": "api_request",
"api_key_present": apiKey.isNotEmpty,
// Never log the full key in production; use a hash if needed
"key_hash": sha256.convert(utf8.encode(apiKey)).toString(),
};
// send logEntry to your logging backend
print(logEntry);
}
void main() {
final app = Chi();
app.use((req, res, next) {
final key = req.queryParams["api_key"] ?? "";
logRequest(key);
return next();
});
app.get("/public", (_) => "ok");
app.listen("0.0.0.0:8080");
}
These patterns emphasize explicit handling, input validation, and isolation of sensitive values, reducing the attack surface for prototype pollution. Combine these practices with middleBrick’s Property Authorization and Input Validation checks to detect unsafe merging and missing constraints in your API specifications.