0 ? $name . '_' . $id : $name; } /** * Adds a nonce to a URL. * * @param string $url URL to protect. * @param string $action Nonce action. */ public static function url( string $url, string $action ): string { return wp_nonce_url( $url, $action ); } /** * Prints the hidden fields protecting a form. * * @param string $action Nonce action. * @param string $name Optional. Field name. Default '_wpnonce'. */ public static function field( string $action, string $name = '_wpnonce' ): void { wp_nonce_field( $action, $name ); } /** * Verifies an admin request, stopping it when the token does not match. * * Wraps `check_admin_referer()`, which checks both the nonce and the referer * and dies on failure. Used for links and forms inside wp-admin. * * @param string $action Nonce action. * @param string $name Optional. Field name. Default '_wpnonce'. */ public static function verify_admin( string $action, string $name = '_wpnonce' ): void { check_admin_referer( $action, $name ); } /** * Verifies an AJAX request without stopping it. * * @param string $action Nonce action. * @param string $name Optional. Field name. Default '_wpnonce'. * * @return bool True when the token is valid. */ public static function verify_ajax( string $action, string $name = '_wpnonce' ): bool { return (bool) check_ajax_referer( $action, $name, false ); } /** * Verifies a token supplied as a plain value. * * @param string $nonce Token to check. * @param string $action Nonce action. * * @return bool True when the token is valid. */ public static function verify( string $nonce, string $action ): bool { return '' !== $nonce && false !== wp_verify_nonce( $nonce, $action ); } /** * Determines whether a request came from this site. * * A second line of defence behind the nonce: browsers send `Origin` on cross * site POSTs, so a request that names a different host is refused before the * token is even considered. Requests without either header are allowed * through, because non browser clients legitimately omit them. * * @return bool True when the request is same origin, or its origin is unknown. */ public static function verify_origin(): bool { $origin = Request::server( 'HTTP_ORIGIN' ); if ( '' === $origin ) { $origin = Request::server( 'HTTP_REFERER' ); } if ( '' === $origin ) { return true; } $sent = wp_parse_url( $origin, PHP_URL_HOST ); $here = wp_parse_url( home_url(), PHP_URL_HOST ); /* * A header that is present and unparseable is refused, not waved through. * * The absent case above is the documented one: a non-browser client * legitimately sends neither header, and the nonce is what actually guards * the request. But a *malformed* one is different — something sent a header * and it does not name a host — and answering true there meant the check * could be defeated by sending `Origin: %` rather than by sending nothing. */ /* * The literal token `null` is an *opaque* origin, not a malformed one. * Browsers send it for a form inside a sandboxed iframe, for a document * loaded from `data:` or `file:`, and after some cross-site redirects. It * carries no host by design, so it is treated like an absent header — the * nonce is what actually guards the request. Refusing it turned a * legitimate embed into "this form has expired" with no way for the site to * allow it, because the filter below is not reached on the refusal path. */ if ( 'null' === strtolower( trim( $origin ) ) ) { return true; } if ( ! is_string( $sent ) || '' === $sent ) { return false; } if ( ! is_string( $here ) ) { return true; } /** * Filters whether a request is treated as same origin. * * @since 1.0.0 * * @param bool $same_origin Whether the origin matches the site. * @param string $sent Host the request claims to come from. */ return (bool) apply_filters( 'formpilot_verify_origin', strtolower( $sent ) === strtolower( $here ), $sent ); } }