diff --git a/ipa/general/0111.mdx b/ipa/general/0111.mdx index cd80146..744b594 100644 --- a/ipa/general/0111.mdx +++ b/ipa/general/0111.mdx @@ -468,6 +468,363 @@ the server may scale it up or down based on load. } ``` +### Sensitive Fields + +Some resource fields carry secrets — passwords, API keys, private credentials. +The API design **must** prevent accidental exposure of these values in +responses. + + + + + +Sensitive fields **must** be marked in OpenAPI so that consumers and tooling can +identify them (see [IPA-117](0117.mdx#sensitive-field-markings) for the required +annotations). + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + displayName: + type: string + key: + type: string + format: password + writeOnly: true +``` + + + The `key` property carries `format: password` and `writeOnly: true`, so + tooling and reviewers know the field contains a secret and must not appear in + responses. + + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + displayName: + type: string + key: + type: string +``` + + + The `key` property lacks any annotation — no `format: password`, no + `writeOnly` — so neither consumers nor tooling can tell that the field carries + a secret. + + + + + + + Identify fields that carry secrets — passwords, API keys, credentials, or + other sensitive material. + + + For each sensitive field, confirm it carries an OpenAPI annotation (e.g. + `format: password`, `writeOnly: true`) that marks it as sensitive. + + + Report sensitive fields that have no marking or rely only on naming + conventions. + + + + + + + + + +Sensitive fields **must** follow one of the following information-flow patterns: + + + + + +**Write-only.** The client submits the value on [Create](0106.mdx) or +[Update](0107.mdx) and the server **must not** return it in any response. + + + + + +```yaml +components: + schemas: + ApiKeyCreate: + type: object + properties: + displayName: + type: string + key: + type: string + format: password + writeOnly: true +``` + + + The `key` is write-only — the client sends it on create, and the server never + returns it in subsequent Get or List responses. + + + + + + +```yaml +components: + schemas: + UserUpdate: + type: object + properties: + email: + type: string + password: + type: string + format: password + writeOnly: true +``` + + + When updating a user, the client submits the new `password` on + [Update](0107.mdx). It is `writeOnly: true`, so the server accepts it but + never returns it in any response. + + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + displayName: + type: string + key: + type: string +``` + + + The `key` is not marked write-only, so the client expects it to appear in + responses — leaking the secret. + + + + + + + Identify fields whose value is submitted by the client and must not be + returned. + + + Confirm each such field is marked `writeOnly: true` and does not appear in + response schemas. + + + Flag any write-only field that appears in a response schema. + + + + + + + + + +**Create-response-only.** For values the client cannot retrieve later (e.g. +server-generated API keys), the field **may** appear in the [Create](0106.mdx) +response so the client can capture it, but **must not** appear in subsequent +[Get](0104.mdx) or [List](0105.mdx) responses. Create-response-only is the +sanctioned exception to the [schema consistency rule](0101.mdx#resources). + + + + + +```yaml +components: + schemas: + # Create (POST) response: returns the secret once + ApiKeyCreateResponse: + type: object + properties: + id: + type: string + displayName: + type: string + key: + type: string + format: password + readOnly: true + # Get and List response: omits the secret + ApiKey: + type: object + properties: + id: + type: string + displayName: + type: string +``` + + + The server-generated `key` appears only in the Create response schema, marked + `readOnly` since the client never sets it. The Get and List schema omits it + entirely, so the secret cannot be retrieved later. + + + + + + +```yaml +components: + schemas: + # Single schema reused by Create, Get, and List + ApiKey: + type: object + properties: + id: + type: string + displayName: + type: string + key: + type: string + format: password + readOnly: true +``` + + + A single response schema carries `key` across Create, Get, and List, so the + secret is returned on every read instead of only once on creation. + + + + + + + Identify fields that are server-generated and shown only once (e.g., initial + API key secret). + + + Confirm the field appears only in the Create response schema and is absent + from the Get and List response schemas. + + + Flag any create-response-only field that appears in Get or List response + schemas. + + + + + + + + + +When a masked representation of a sensitive field (e.g. `****` or a last-4 +display value) needs to appear in [Get](0104.mdx) or [List](0105.mdx) responses, +it **must** be modeled as a separate read-only **redacted sibling** property +following the [Effective Values](#effective-values) pattern. The raw and +redacted values **must not** share the same property name. A redacted sibling +**may** be added to either a write-only or a create-response-only field +independently of the chosen information-flow pattern. + + + + + +A masked display of a sensitive field (e.g. `****` or a last-4 display value) +**must** be modeled as a separate read-only **redacted sibling** property, not +reusing the raw field's name. + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + key: + type: string + format: password + writeOnly: true + keyRedacted: + type: string + description: Last four characters of the API key. + readOnly: true +``` + + + The raw value is `key` (write-only); the masked display is `keyRedacted` + (read-only, last-4 display). They are distinct properties with different + names, so no ambiguity exists about which carries the secret. + + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + key: + type: string +``` + + + A single property named `key` cannot simultaneously be write-only and carry a + masked display. Consumers cannot tell whether the value is the raw secret or a + redacted version. + + + + + + + Find fields whose response value is a masked or truncated display of a + secret (e.g. `****`, last-4). + + + Confirm the masked value lives on a separate property with a distinct name + (e.g. `keyRedacted`) rather than replacing the raw field. + + + Report any field that mixes a raw secret and a masked display under the same + property name. + + + + + + + + + ### Boolean Values diff --git a/ipa/general/0117.mdx b/ipa/general/0117.mdx index 09322d6..d341873 100644 --- a/ipa/general/0117.mdx +++ b/ipa/general/0117.mdx @@ -673,6 +673,157 @@ properties: +### Sensitive Field Markings + +The OpenAPI `format: password` keyword identifies properties whose value is +itself a secret. Guidelines below govern when to apply it. + + + + + +**Must use** `format: password` on any property whose value is itself a secret +per [IPA-111 — Sensitive Fields](0111.mdx#sensitive-fields). + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + key: + type: string + format: password + writeOnly: true +``` + + + The `key` property carries a secret value (an API key), so `format: password` + marks it as a secret for tooling and consumers. + + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + key: + type: string + writeOnly: true +``` + + + The `key` is write-only but lacks `format: password`, so tooling cannot + distinguish it from any other write-only string field. + + + + + + + Identify all properties that carry a secret value per IPA-111. + + + Confirm each such property sets `format: password` in its schema. + + + Report secret-carrying properties that omit `format: password`. + + + + + + + + + +**Must not** use `format: password` on a redacted sibling property whose value +is a masked display (`****`, last-4 digits, etc.). The redacted value is not +itself a secret; marking it as such misleads consumers and tooling. + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + key: + type: string + format: password + writeOnly: true + keyRedacted: + type: string + description: Last four characters of the API key. + readOnly: true +``` + + + Only the raw `key` carries `format: password`. The redacted sibling + `keyRedacted` carries a masked display, not a secret, so it omits the format. + + + + + + +```yaml +components: + schemas: + ApiKey: + type: object + properties: + key: + type: string + format: password + writeOnly: true + keyRedacted: + type: string + format: password + description: Last four characters of the API key. + readOnly: true +``` + + + `keyRedacted` is a masked display whose value is not a secret — it repeats a + few characters the consumer already provided. Marking it with `format: + password` misleads tooling into treating the redacted value as a secret. + + + + + + + Identify redacted sibling properties — read-only fields that show a masked + or truncated display of a sensitive field. + + + Confirm none of these sibling properties carries `format: password`. + + + Report redacted siblings that are incorrectly marked with `format: + password`. + + + + + + + + + ### Examples