| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Secrets API: WP_Secrets_Provider interface 4 * 5 * @package WordPress 6 * @subpackage Secrets 7 * @since 7.2.0 8 */ 9 10 /** 11 * Interface for a secrets provider. 12 * 13 * A provider is responsible for a secret: storing it, protecting it, and returning 14 * it to a caller. It is the outermost extension point in the Secrets API, and the 15 * one a hosting platform implements. 16 * 17 * WordPress ships WP_Secrets_Libsodium_Provider, which encrypts with libsodium and 18 * stores ciphertext in the options tables. That provider is the default rather than 19 * a privileged case: a platform that protects credentials in a key management 20 * service, a hardware security module, or its own control panel implements this 21 * same interface. 22 * 23 * A provider must be stronger than the default, never weaker. Storing a plaintext 24 * where the default would have stored ciphertext is what this interface exists to 25 * prevent. Receiving a value over an authenticated channel and holding it in a 26 * hardware security module satisfies that requirement. 27 * 28 * WordPress cannot verify any of this. A provider is loaded from a drop-in, which is 29 * fully trusted code: it runs before plugins, and could already read every secret by 30 * implementing the keyring. get_protection_boundary() and is_writable() are 31 * declarations rather than enforcement. They exist so that a developer reviewing a 32 * drop-in can see what it claims, Site Health can report where credentials are 33 * protected, and a settings screen can determine that a write will be refused before 34 * an operator enters a credential. They are not a security boundary. 35 * 36 * Every implementation shares these contracts: 37 * 38 * - Three states, never collapsed. get() returns a WP_Secret, null when the secret 39 * does not exist, or WP_Error when it exists but cannot be produced. Reporting an 40 * unreachable backend as absent turns an outage into an apparently deleted 41 * credential. 42 * - Fail closed. A provider that cannot answer returns WP_Error. It never 43 * substitutes a weaker source, and never returns a partial or placeholder value. 44 * - No filter on the retrieval path. A provider replaces a component; it is not a 45 * hook. Nothing is given the opportunity to observe or alter a value in transit. 46 * - Never persist a value more weakly than the provider protects it. A provider that 47 * fetches over the network must not write the plaintext into the persistent object 48 * cache. WP_Secret cannot round-trip a plaintext through wp_cache_set(), and 49 * caching the raw value alongside it would undo that. Memoize within the request 50 * only. 51 * 52 * @since 7.2.0 53 */ 54 interface WP_Secrets_Provider { 55 56 /** 57 * Protection happens inside WordPress. 58 * 59 * The provider holds ciphertext that WordPress produced, and WordPress holds the 60 * key material. 61 * 62 * @since 7.2.0 63 * @var string 64 */ 65 const BOUNDARY_WORDPRESS = 'wordpress'; 66 67 /** 68 * Protection happens outside WordPress. 69 * 70 * The provider is itself the encryption boundary, and WordPress is a consumer of a 71 * credential it does not protect. 72 * 73 * @since 7.2.0 74 * @var string 75 */ 76 const BOUNDARY_PROVIDER = 'provider'; 77 78 /** 79 * Retrieves a secret. 80 * 81 * Returning null means "I am not responsible for this name" as well as "this 82 * name does not exist". The two are the same answer from a caller's point of 83 * view, and keeping them distinct would require a provider to enumerate names 84 * it has never heard of. 85 * 86 * @since 7.2.0 87 * 88 * @param string $name The secret's name. 89 * @param string $version A WP_Secret_Version constant. A provider with no 90 * version history returns null for PREVIOUS, which is 91 * absence rather than an error. 92 * @param bool $network Whether this is a network-scope secret. 93 * @return WP_Secret|null|WP_Error 94 * 95 * @phpstan-param WP_Secret_Version::CURRENT|WP_Secret_Version::PREVIOUS $version 96 */ 97 public function get( string $name, string $version, bool $network = false ); 98 99 /** 100 * Stores a secret. 101 * 102 * A provider whose credentials are managed elsewhere, by a control panel, host 103 * tooling, or a KMS with its own access policy, returns WP_Error here with code 104 * WP_SECRETS_ERROR_PROVIDER_READ_ONLY, and reports false from is_writable() so 105 * that callers can find that out without attempting the write first. 106 * 107 * Implementations are responsible for firing the `wp_secret_changed` action once 108 * the write has succeeded, with `$network` as its last argument. The API does 109 * not fire it on the provider's behalf, because only the provider knows the 110 * prior fingerprint without paying for an additional read. An audit hook that 111 * stopped firing once a host installed a provider would be unreliable in exactly 112 * the situation it matters most, so WP_Secrets_Provider_Conformance checks it. 113 * A write that fails fires nothing. 114 * 115 * Writing the value a secret already holds is not a rotation. A settings form 116 * that saves every field on every submit does this routinely, so a provider 117 * that keeps a previous version must not discard it for an unchanged write; 118 * doing so would end a rotation's grace period early, with every consumer 119 * still on the old credential losing access. WP_Secrets_Provider_Conformance 120 * checks this. Treating the write as a no-op that fires nothing is preferred, 121 * but a provider that cannot tell the value is unchanged without an additional 122 * read may write it and report it. 123 * 124 * @since 7.2.0 125 * 126 * @param string $name The secret's name. 127 * @param string $value The plaintext value. 128 * @param bool $network Whether this is a network-scope secret. 129 * @param bool $needs_rotation Mark the stored secret as needing rotation. 130 * Set for values that arrived from somewhere 131 * less protected than this provider, such as 132 * an imported option. A provider with nowhere 133 * to record this may ignore it, but must not 134 * report it as honored. 135 * @param string|null $action Overrides the action reported to 136 * `wp_secret_changed`; null means the 137 * provider decides between 'created' and 138 * 'updated'. 139 * @return true|WP_Error 140 */ 141 public function set( string $name, $value, bool $network = false, bool $needs_rotation = false, ?string $action = null ); 142 143 /** 144 * Deletes a secret. 145 * 146 * Deleting a secret that does not exist is a success. 147 * 148 * Fires `wp_secret_changed` with the 'deleted' action once a secret has been 149 * deleted, for the reason given on set(). A provider that can tell nothing was 150 * there to delete fires nothing. 151 * 152 * @since 7.2.0 153 * 154 * @param string $name The secret's name. 155 * @param bool $network Whether this is a network-scope secret. 156 * @return true|WP_Error 157 */ 158 public function delete( string $name, bool $network = false ); 159 160 /** 161 * Clears a secret's previous version, if this provider keeps one. 162 * 163 * A provider with no version history treats this as a successful no-op: there 164 * is nothing to retire, which is the state the caller asked for. 165 * 166 * Fires `wp_secret_changed` with the 'retired' action once a previous version 167 * has been cleared, for the reason given on set(). Nothing fires when there was 168 * no previous version to clear. 169 * 170 * @since 7.2.0 171 * 172 * @param string $name The secret's name. 173 * @param bool $network Whether this is a network-scope secret. 174 * @return true|WP_Error 175 */ 176 public function retire_previous( string $name, bool $network = false ); 177 178 /** 179 * Lists secrets by name and metadata. Never values. 180 * 181 * @since 7.2.0 182 * 183 * @param string $name_prefix Restrict to names beginning with this prefix, or 184 * '' for all of them. 185 * @param bool $network Whether to list network-scope secrets. 186 * @return array|WP_Error List of arrays with keys 'name', 'fingerprint', 187 * 'created', 'has_previous', 'needs_rotation'. 188 * 189 * @phpstan-return list<array{name: string, fingerprint: string, created: int, has_previous: bool, needs_rotation: bool}>|WP_Error 190 */ 191 public function list_secrets( string $name_prefix = '', bool $network = false ); 192 193 /** 194 * A short human-readable name for this provider, for Site Health. 195 * 196 * Describes the protection, not the vendor's marketing: "AWS KMS 197 * (alias/wp-secrets)" tells an operator something, "SuperSecure Pro" does not. 198 * Never key material, never a credential, never a value. 199 * 200 * @since 7.2.0 201 * 202 * @return string 203 */ 204 public function get_label(): string; 205 206 /** 207 * Where secrets this provider holds are actually protected. 208 * 209 * One of the BOUNDARY_* constants. This is what lets Site Health and a future 210 * admin screen tell an operator whether their credentials are protected by 211 * WordPress's own libsodium envelope or by something outside it, a question 212 * they currently have no way to answer, and the one hosts asked to be able to 213 * answer honestly. 214 * 215 * @since 7.2.0 216 * 217 * @return string 218 * 219 * @phpstan-return self::BOUNDARY_* 220 */ 221 public function get_protection_boundary(): string; 222 223 /** 224 * Whether wp_set_secret() can succeed against this provider. 225 * 226 * False for a provider whose credentials are managed by host tooling or a 227 * control panel. Declared rather than discovered so a settings screen can 228 * disable its save control before an operator types a credential into a field 229 * that will only reject it. 230 * 231 * @since 7.2.0 232 * 233 * @return bool 234 */ 235 public function is_writable(): bool; 236 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Nov 3 08:20:36 2026 | Cross-referenced by PHPXref |