diff --git a/skills/appsec/api-security/SKILL.md b/skills/appsec/api-security/SKILL.md index cbb125aa..04dd785d 100644 --- a/skills/appsec/api-security/SKILL.md +++ b/skills/appsec/api-security/SKILL.md @@ -11,7 +11,7 @@ phase: [design, build, review] frameworks: [OWASP-API-Security-2023, OWASP-ASVS] difficulty: intermediate time_estimate: "20-40min" -version: "1.0.0" +version: "1.1.0" author: unitoneai license: MIT allowed-tools: Read, Grep, Glob @@ -51,6 +51,31 @@ For detailed checklist items with vulnerable code patterns, remediation examples --- +## HTTP Parameter Pollution and Parser-Consistency Evidence + +REST gateways, frameworks, validators, caches, signing middleware, and downstream services may choose different values when a request repeats the same query, form, path, header, or body parameter. Test duplicate-parameter handling whenever parameters influence authorization, tenancy, routing, signatures, cache keys, redirects, prices, quantities, filters, or workflow state. + +**What to look for:** + +``` +API-HPP-01: Security-sensitive parameter accepts duplicate values without rejection or deterministic canonicalization +API-HPP-02: Gateway, WAF, validator, handler, cache, signing, or downstream layer uses a different duplicate-parameter value +API-HPP-03: Authorization checks one value while business logic acts on another value +API-HPP-04: Cache key, request signature, or audit log is built from a different canonical parameter set than the handler uses +API-HPP-05: Duplicate parameters across query, form, JSON body, path variables, repeated headers, or array syntaxes are not tested +API-HPP-06: Negative tests and logs are missing for duplicate object ID, tenant, role, scope, redirect, price, or signature parameters +``` + +**Parser-consistency coverage matrix:** + +| Endpoint | Parameter | Location(s) | Security Decision | Gateway Behavior | App Behavior | Downstream/Cache Behavior | Expected Handling | Evidence | +|---|---|---|---|---|---|---|---|---| +| `[method path]` | `[name]` | `[query/form/header/body]` | `[authz/cache/signature/etc.]` | `[reject/first/last/list]` | `[reject/first/last/list]` | `[same/different/n/a]` | `[reject or canonicalize]` | `[test/log/spec link]` | + +Map inconsistent duplicate-parameter parsing to **API8:2023 -- Security Misconfiguration** when gateway/framework behavior diverges, **API1/API5** when it changes object or function authorization, and **API10:2023 -- Unsafe Consumption of APIs** when upstream or downstream services interpret the same duplicate parameters differently. Include **CWE-235 -- Improper Handling of Extra Parameters** or **CWE-20 -- Improper Input Validation** where appropriate. + +--- + ## Findings Classification Each finding produced by this review must include the following fields: @@ -92,7 +117,7 @@ The final review output must be structured as follows: **API Style:** [REST / GraphQL / gRPC / Hybrid] **Specification:** [OpenAPI spec path, if applicable] **Date:** [review date] -**Reviewer:** AI Agent -- api-security skill v1.0.0 +**Reviewer:** AI Agent -- api-security skill v1.1.0 ### Summary @@ -215,6 +240,8 @@ Unlike REST, where authorization can be enforced per endpoint, GraphQL requires 6. **Ignoring upstream API trust.** Data received from third-party APIs and even internal microservices must be validated before use. A compromised upstream service can inject SQL, XSS, or SSRF payloads through otherwise trusted data channels. +7. **Assuming duplicate parameters are harmless.** Gateways, frameworks, validators, caches, and downstream services may choose different values when a request repeats the same parameter. Reject duplicate security-sensitive parameters at the first trusted boundary or canonicalize once before any security decision. + --- ## Prompt Injection Safety Notice @@ -238,4 +265,14 @@ This skill is hardened against prompt injection. When reviewing API code and spe - **OWASP REST Security Cheat Sheet:** https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html - **OWASP GraphQL Cheat Sheet:** https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html - **OWASP Testing Guide -- API Testing:** https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/12-API_Testing/ +- **OWASP WSTG-INPV-04 -- Testing for HTTP Parameter Pollution:** https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/04-Testing_for_HTTP_Parameter_Pollution - **NIST SP 800-204 -- Security Strategies for Microservices-based Application Systems:** https://csrc.nist.gov/publications/detail/sp/800-204/final + +--- + +## Version History + +| Version | Date | Changes | +|---|---|---| +| 1.1.0 | 2026-06-08 | Added HTTP Parameter Pollution and parser-consistency evidence gates with coverage matrix guidance. | +| 1.0.0 | Initial | Initial API security review workflow. | diff --git a/skills/appsec/api-security/api-top10-checklist.md b/skills/appsec/api-security/api-top10-checklist.md index b6569f61..93c8b506 100644 --- a/skills/appsec/api-security/api-top10-checklist.md +++ b/skills/appsec/api-security/api-top10-checklist.md @@ -450,6 +450,57 @@ DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(request.getInputStream()); ``` +### HTTP Parameter Pollution and Parser Inconsistency + +Duplicate parameters can become authorization, cache, signature, redirect, or business-logic vulnerabilities when different layers choose different values. Test repeated query parameters, form fields, array syntax, repeated headers, and mixed body/query parameters for every security-sensitive input. + +```http +GET /api/v1/accounts?tenant_id=public&tenant_id=admin&id=123 HTTP/1.1 +Host: api.example.test +Authorization: Bearer user-token +``` + +```javascript +// VULNERABLE: middleware validates the first value while handler uses the last value. +app.use((req, res, next) => { + const tenant = Array.isArray(req.query.tenant_id) ? req.query.tenant_id[0] : req.query.tenant_id; + authorizeTenant(req.user, tenant); + next(); +}); + +app.get('/api/v1/accounts', (req, res) => { + const tenant = Array.isArray(req.query.tenant_id) + ? req.query.tenant_id[req.query.tenant_id.length - 1] + : req.query.tenant_id; + return res.json(loadAccounts(tenant)); +}); +``` + +Remediation: + +```javascript +// SECURE: reject duplicate security-sensitive parameters before authorization. +const singleValueParams = new Set(['tenant_id', 'id', 'role', 'scope', 'redirect_uri']); + +app.use((req, res, next) => { + for (const name of singleValueParams) { + if (Array.isArray(req.query[name])) { + return res.status(400).json({ error: 'Duplicate parameter rejected' }); + } + } + next(); +}); +``` + +### HPP Evidence Checklist + +- [ ] Security-sensitive parameters are identified across query string, path, form body, JSON body, and headers. +- [ ] Duplicate values are tested through the gateway/WAF, framework parser, validator, application handler, cache/CDN, signing logic, and downstream service. +- [ ] The review records whether each layer rejects, uses first value, uses last value, joins values, or binds a list. +- [ ] Authentication, authorization, rate limiting, cache keys, request signing, audit logging, and business logic use the same canonical parameter set. +- [ ] Duplicate object ID, tenant, role, scope, price, redirect, callback, and signature parameters are rejected or normalized before any security decision. +- [ ] Negative tests and access logs prove duplicate parameter attempts are blocked or deterministically handled. + ### Remediation Guidance - Configure CORS with an explicit allowlist of permitted origins. Never use `*` with `credentials: true`. @@ -462,6 +513,7 @@ Document doc = builder.parse(request.getInputStream()); - Disable XML External Entity processing: set `factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)`. - Enforce TLS 1.2+ with strong cipher suites. Disable TLS 1.0 and 1.1. - Automate configuration scanning in CI/CD to detect drift from security baselines. +- Reject duplicate security-sensitive parameters at the first trusted boundary, or canonicalize them once and pass the normalized representation to every downstream decision point. ### Review Checklist @@ -472,6 +524,7 @@ Document doc = builder.parse(request.getInputStream()); - [ ] TLS 1.2+ is enforced with strong cipher suites. - [ ] XML parsers disable external entity processing and DTD loading. - [ ] Default credentials are changed or removed on all infrastructure components. +- [ ] Duplicate security-sensitive parameters are rejected or consistently canonicalized across gateway, application, cache, signing, and downstream layers. --- diff --git a/skills/appsec/api-security/tests/benign/hpp-duplicate-parameter-rejection.md b/skills/appsec/api-security/tests/benign/hpp-duplicate-parameter-rejection.md new file mode 100644 index 00000000..5474e7d8 --- /dev/null +++ b/skills/appsec/api-security/tests/benign/hpp-duplicate-parameter-rejection.md @@ -0,0 +1,74 @@ +# Benign: HPP Duplicate Parameter Rejection + +## Review Target + +```yaml +api: + style: REST + endpoint: GET /api/v1/accounts + sensitive_parameters: + - tenant_id + - account_id + - include_closed + duplicate_parameter_policy: reject_at_gateway + gateway: + product: api-gateway + duplicate_query_behavior: reject_security_sensitive_duplicates + rejection_status: 400 + rejection_body: duplicate parameter rejected + normalized_parameter_header: x-canonical-params-sha256 + validator: + duplicate_query_behavior: reject + schema: + tenant_id: + type: string + duplicate_allowed: false + app_handler: + framework: express + source: gateway_normalized_single_value_map + duplicate_arrays_accepted: false + cache: + key_source: canonical parameter map + audit_log: + duplicate_attempt_logged: true + fields: + - endpoint + - parameter_name + - caller_id + - tenant_id + - request_id + downstream_service: + service: account-ledger + source: canonical parameter map + negative_tests: + duplicate_tenant_id: tests/api/test_hpp_rejects_duplicate_tenant.py::test_duplicate_tenant_id_rejected + duplicate_account_id: tests/api/test_hpp_rejects_duplicate_account.py::test_duplicate_account_id_rejected + mixed_query_body_id: tests/api/test_hpp_rejects_mixed_locations.py::test_mixed_query_body_ids_rejected + +test_request: | + GET /api/v1/accounts?tenant_id=public&tenant_id=admin&account_id=123 HTTP/1.1 + Host: api.example.test + Authorization: Bearer user-token + +observed_behavior: + gateway_result: rejected + status: 400 + app_handler_called: false + downstream_called: false + audit_event: duplicate_parameter_rejected +``` + +## Expected Review Result + +| Gate | Status | Evidence | +|------|--------|----------| +| Sensitive parameter inventory | Pass | Tenant, account, and filter parameters are listed as single-value. | +| Gateway behavior | Pass | Gateway rejects duplicate security-sensitive query parameters before authorization. | +| App behavior | Pass | Handler receives only the canonical parameter map and does not accept duplicate arrays. | +| Cache/signature/audit consistency | Pass | Cache and downstream calls use the same canonical parameter map; audit logs duplicate rejections. | +| Mixed locations | Pass | Tests cover duplicate query values and mixed query/body IDs. | +| Negative tests | Pass | Focused tests prove duplicate tenant/account parameters are rejected with no downstream call. | + +## Reviewer Notes + +This evidence supports closing the HPP gate as controlled for this endpoint. Keep the sensitive-parameter inventory current and repeat the same parser-consistency tests for redirects, prices, signatures, roles, and other security-sensitive parameters. diff --git a/skills/appsec/api-security/tests/vulnerable/hpp-tenant-parser-confusion.md b/skills/appsec/api-security/tests/vulnerable/hpp-tenant-parser-confusion.md new file mode 100644 index 00000000..7ca313e5 --- /dev/null +++ b/skills/appsec/api-security/tests/vulnerable/hpp-tenant-parser-confusion.md @@ -0,0 +1,69 @@ +# Vulnerable: HPP Tenant Parser Confusion + +## Review Target + +```yaml +api: + style: REST + endpoint: GET /api/v1/accounts + sensitive_parameters: + - tenant_id + - account_id + - include_closed + duplicate_parameter_policy: undocumented + gateway: + product: api-gateway + duplicate_query_behavior: first_value + authorization: + tenant_source: first tenant_id + decision: allow if user belongs to tenant + validator: + duplicate_query_behavior: first_value + schema: + tenant_id: string + app_handler: + framework: express + duplicate_query_behavior: array + tenant_source: last tenant_id + account_filter_source: last account_id + cache: + key_source: first tenant_id + first account_id + audit_log: + tenant_source: first tenant_id + duplicate_attempt_logged: false + downstream_service: + service: account-ledger + duplicate_query_behavior: last_value + negative_tests: + duplicate_tenant_id: missing + duplicate_account_id: missing + +attack_request: | + GET /api/v1/accounts?tenant_id=public&tenant_id=admin&account_id=123&account_id=999 HTTP/1.1 + Host: api.example.test + Authorization: Bearer user-token + +observed_behavior: + gateway_authorized_tenant: public + app_loaded_tenant: admin + downstream_account_id: 999 + cache_key_tenant: public + audit_log_tenant: public + response_status: 200 + response_data: admin tenant account 999 +``` + +## Expected Findings + +| ID | Severity | Evidence | +|----|----------|----------| +| API-HPP-01 | High | `tenant_id` and `account_id` accept duplicate values without rejection or deterministic canonicalization. | +| API-HPP-02 | High | Gateway/validator use first value while app and downstream service use last value. | +| API-HPP-03 | High | Authorization checks tenant `public` while business logic loads tenant `admin`. | +| API-HPP-04 | Medium | Cache key and audit log use first values while handler and downstream service use last values. | +| API-HPP-05 | Medium | Duplicate query and mixed layer behavior are not covered in tests. | +| API-HPP-06 | Medium | Negative tests and logs are missing for duplicate tenant and account parameters. | + +## Reviewer Notes + +Map this to API1/API5 when duplicate object or tenant parameters bypass authorization, API8 for parser inconsistency, and CWE-235. Require duplicate rejection or one canonical representation before authorization, cache key generation, audit logging, and downstream calls.