diff --git a/README.md b/README.md index d1afadc..8f4836b 100644 --- a/README.md +++ b/README.md @@ -216,3 +216,16 @@ Things to do in the future: * Sandboxed plugin architecture * Audio CAPTCHA samples * Interactive CAPTCHA samples + +## WordPress Plugin + +A WordPress plugin is included to protect forms on your WordPress site (such as Comments, Login, and Registration). + +### Installation +1. Copy the `plugins/wordpress/librecaptcha.php` file (or the `plugins/wordpress` directory) to your WordPress `wp-content/plugins/` directory. +2. Log into your WordPress admin dashboard and go to **Plugins**. +3. Activate the **LibreCaptcha** plugin. +4. Navigate to **Settings > LibreCaptcha**. +5. Enter the **LibreCaptcha Server URL** (e.g., `http://localhost:8888`). +6. Adjust the JSON configuration and select the forms you wish to protect. +7. You can also use the `[librecaptcha]` shortcode to embed the CAPTCHA in custom pages. diff --git a/plugins/wordpress/librecaptcha.php b/plugins/wordpress/librecaptcha.php new file mode 100644 index 0000000..537b007 --- /dev/null +++ b/plugins/wordpress/librecaptcha.php @@ -0,0 +1,451 @@ + 'application/json' ); + if ( ! empty( $auth_key ) ) { + $headers['Auth'] = $auth_key; + } + + $response = wp_remote_post( $server_url . '/v2/answer', array( + 'headers' => $headers, + 'body' => wp_json_encode( array( + 'id' => $captcha_id, + 'answer' => $captcha_answer, + ) ), + 'method' => 'POST', + 'data_format' => 'body', + ) ); + + if ( is_wp_error( $response ) ) { + return false; // Fail safe or fail secure? typically fail secure for captcha + } + + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + + if ( isset( $data['result'] ) && ( $data['result'] === 'True' || $data['result'] === true ) ) { + return true; + } + + return false; + } + + public function verify_login_captcha( $user, $username, $password ) { + // Only check if it's a POST request (login attempt) and user is not already an error + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && ! is_wp_error( $user ) ) { + if ( ! $this->verify_captcha() ) { + return new WP_Error( 'authentication_failed', __( 'ERROR: The CAPTCHA was incorrect.' ) ); + } + } + return $user; + } + + public function verify_registration_captcha( $errors, $sanitized_user_login, $user_email ) { + if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) { + if ( ! $this->verify_captcha() ) { + $errors->add( 'captcha_failed', __( 'ERROR: The CAPTCHA was incorrect.' ) ); + } + } + return $errors; + } + + public function verify_comment_captcha( $comment_post_id ) { + // If user is logged in, they might not see the captcha depending on form implementation, + // but since we hooked `comment_form_logged_in_after`, they do see it. + if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) { + if ( ! $this->verify_captcha() ) { + wp_die( __( 'ERROR: The CAPTCHA was incorrect. Please go back and try again.' ) ); + } + } + } + + public function ajax_test_load() { + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Unauthorized' ); + } + + $server_url = rtrim( get_option( 'lc_server_url', '' ), '/' ); + $auth_key = get_option( 'lc_auth_key', '' ); + $config_json_string = get_option( 'lc_config_json', '{"level":"easy","media":"image/png","input_type":"text","size":"350x100"}' ); + + if ( empty( $server_url ) ) { + wp_send_json_error( 'Server URL is not configured.' ); + } + + $headers = array( 'Content-Type' => 'application/json' ); + if ( ! empty( $auth_key ) ) { + $headers['Auth'] = $auth_key; + } + + $response = wp_remote_post( $server_url . '/v2/captcha', array( + 'headers' => $headers, + 'body' => $config_json_string, + 'method' => 'POST', + 'data_format' => 'body', + ) ); + + if ( is_wp_error( $response ) ) { + wp_send_json_error( 'Failed to connect to LibreCaptcha server.' ); + } + + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + + if ( isset( $data['id'] ) ) { + // Also return the full server URL so the client can load the image + wp_send_json_success( array( 'id' => $data['id'], 'server_url' => $server_url ) ); + } else { + wp_send_json_error( 'Invalid response from LibreCaptcha server.' ); + } + } + + public function ajax_test_check() { + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( 'Unauthorized' ); + } + + $captcha_id = sanitize_text_field( $_POST['captcha_id'] ?? '' ); + $captcha_answer = sanitize_text_field( $_POST['captcha_answer'] ?? '' ); + + if ( empty( $captcha_id ) || empty( $captcha_answer ) ) { + wp_send_json_error( 'Missing ID or Answer.' ); + } + + $server_url = rtrim( get_option( 'lc_server_url', '' ), '/' ); + $auth_key = get_option( 'lc_auth_key', '' ); + + $headers = array( 'Content-Type' => 'application/json' ); + if ( ! empty( $auth_key ) ) { + $headers['Auth'] = $auth_key; + } + + $response = wp_remote_post( $server_url . '/v2/answer', array( + 'headers' => $headers, + 'body' => wp_json_encode( array( + 'id' => $captcha_id, + 'answer' => $captcha_answer, + ) ), + 'method' => 'POST', + 'data_format' => 'body', + ) ); + + if ( is_wp_error( $response ) ) { + wp_send_json_error( 'Failed to connect to LibreCaptcha server.' ); + } + + $body = wp_remote_retrieve_body( $response ); + $data = json_decode( $body, true ); + + wp_send_json_success( $data ); + } + + public function render_captcha_shortcode() { + ob_start(); + $this->render_captcha(); + return ob_get_clean(); + } + + public function render_captcha() { + $server_url = get_option( 'lc_server_url', '' ); + if ( empty( $server_url ) ) { + return; + } + + $server_url = rtrim( $server_url, '/' ); + $auth_key = get_option( 'lc_auth_key', '' ); + $config_json_string = get_option( 'lc_config_json', '{"level":"easy","media":"image/png","input_type":"text","size":"350x100"}' ); + + $headers = array( 'Content-Type' => 'application/json' ); + if ( ! empty( $auth_key ) ) { + $headers['Auth'] = $auth_key; + } + + $response = wp_remote_post( $server_url . '/v2/captcha', array( + 'headers' => $headers, + 'body' => $config_json_string, + 'method' => 'POST', + 'data_format' => 'body', + ) ); + + if ( is_wp_error( $response ) ) { + echo '
Use this section to verify your server configuration and ensure CAPTCHAs are loading and validating correctly.
+