| [ 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 * @since 7.2.0 116 * 117 * @param string $name The secret's name. 118 * @param string $value The plaintext value. 119 * @param bool $network Whether this is a network-scope secret. 120 * @param bool $needs_rotation Mark the stored secret as needing rotation. 121 * Set for values that arrived from somewhere 122 * less protected than this provider, such as 123 * an imported option. A provider with nowhere 124 * to record this may ignore it, but must not 125 * report it as honored. 126 * @param string|null $action Overrides the action reported to 127 * `wp_secret_changed`; null means the 128 * provider decides between 'created' and 129 * 'updated'. 130 * @return true|WP_Error 131 */ 132 public function set( string $name, $value, bool $network = false, bool $needs_rotation = false, ?string $action = null ); 133 134 /** 135 * Deletes a secret. 136 * 137 * Deleting a secret that does not exist is a success. 138 * 139 * Fires `wp_secret_changed` with the 'deleted' action once a secret has been 140 * deleted, for the reason given on set(). A provider that can tell nothing was 141 * there to delete fires nothing. 142 * 143 * @since 7.2.0 144 * 145 * @param string $name The secret's name. 146 * @param bool $network Whether this is a network-scope secret. 147 * @return true|WP_Error 148 */ 149 public function delete( string $name, bool $network = false ); 150 151 /** 152 * Clears a secret's previous version, if this provider keeps one. 153 * 154 * A provider with no version history treats this as a successful no-op: there 155 * is nothing to retire, which is the state the caller asked for. 156 * 157 * Fires `wp_secret_changed` with the 'retired' action once a previous version 158 * has been cleared, for the reason given on set(). Nothing fires when there was 159 * no previous version to clear. 160 * 161 * @since 7.2.0 162 * 163 * @param string $name The secret's name. 164 * @param bool $network Whether this is a network-scope secret. 165 * @return true|WP_Error 166 */ 167 public function retire_previous( string $name, bool $network = false ); 168 169 /** 170 * Lists secrets by name and metadata. Never values. 171 * 172 * @since 7.2.0 173 * 174 * @param string $name_prefix Restrict to names beginning with this prefix, or 175 * '' for all of them. 176 * @param bool $network Whether to list network-scope secrets. 177 * @return array|WP_Error List of arrays with keys 'name', 'fingerprint', 178 * 'created', 'has_previous', 'needs_rotation'. 179 * 180 * @phpstan-return list<array{name: string, fingerprint: string, created: int, has_previous: bool, needs_rotation: bool}>|WP_Error 181 */ 182 public function list_secrets( string $name_prefix = '', bool $network = false ); 183 184 /** 185 * A short human-readable name for this provider, for Site Health. 186 * 187 * Describes the protection, not the vendor's marketing: "AWS KMS 188 * (alias/wp-secrets)" tells an operator something, "SuperSecure Pro" does not. 189 * Never key material, never a credential, never a value. 190 * 191 * @since 7.2.0 192 * 193 * @return string 194 */ 195 public function get_label(): string; 196 197 /** 198 * Where secrets this provider holds are actually protected. 199 * 200 * One of the BOUNDARY_* constants. This is what lets Site Health and a future 201 * admin screen tell an operator whether their credentials are protected by 202 * WordPress's own libsodium envelope or by something outside it, a question 203 * they currently have no way to answer, and the one hosts asked to be able to 204 * answer honestly. 205 * 206 * @since 7.2.0 207 * 208 * @return string 209 * 210 * @phpstan-return self::BOUNDARY_* 211 */ 212 public function get_protection_boundary(): string; 213 214 /** 215 * Whether wp_set_secret() can succeed against this provider. 216 * 217 * False for a provider whose credentials are managed by host tooling or a 218 * control panel. Declared rather than discovered so a settings screen can 219 * disable its save control before an operator types a credential into a field 220 * that will only reject it. 221 * 222 * @since 7.2.0 223 * 224 * @return bool 225 */ 226 public function is_writable(): bool; 227 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Oct 14 08:20:33 2026 | Cross-referenced by PHPXref |