From e5e17cbafce9d780f85c38a43c78e402c4443c13 Mon Sep 17 00:00:00 2001 From: Remo Zaros Date: Mon, 22 Jun 2026 09:34:14 +0200 Subject: [PATCH] swap session with cookie with hash --- .gitignore | 1 + pppr_cookie.php | 137 +++++++++++++++++++++++++++++++++++++++++ prijs-per-postcode.php | 91 ++++++++++++++++++++------- session_dialog.php | 59 +++++------------- 4 files changed, 224 insertions(+), 64 deletions(-) create mode 100644 .gitignore create mode 100644 pppr_cookie.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84fe0e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +wp-pppr-salt.php diff --git a/pppr_cookie.php b/pppr_cookie.php new file mode 100644 index 0000000..48c233f --- /dev/null +++ b/pppr_cookie.php @@ -0,0 +1,137 @@ + time() + 7200, + 'path' => '/', + 'domain' => $_SERVER['SERVER_NAME'], + 'secure' => true, // Only send over HTTPS + 'httponly' => true, // Prevent JavaScript access + 'samesite' => 'Lax' // Protect against CSRF + ]); +} + +function unset_pppr_cookie( $path = '/') { + unset($_COOKIE["PPPR"]); + + if (empty($domain)) { + setcookie("PPPR", '', time() - 3600, '/'); + } else { + setcookie("PPPR", '', time() - 3600, '/' , $_SERVER['SERVER_NAME']); + } +} + +function verify_pppr_cookie_string() { + if (!isset($_COOKIE['PPPR'])) { + return false; + } + + $cookie_value = $_COOKIE['PPPR']; + + $last_delimiter = strrpos($cookie_value, '|'); + if ($last_delimiter === false) { + return false; + } + + $data_part = substr($cookie_value, 0, $last_delimiter); + $hash_part = substr($cookie_value, $last_delimiter + 1); + + // Check if salt is defined + if (!defined('WP_PPPR_SALT')) { + return false; + } + + $expected_hash = hash_hmac('sha256', $data_part, WP_PPPR_SALT); + + return hash_equals($expected_hash, $hash_part); +} + +function get_PPPR_data() { + // 1. Check if cookie exists + if (!isset($_COOKIE['PPPR'])) { + return false; + } + + $cookie_value = $_COOKIE['PPPR']; + + // 2. Split safely using the LAST delimiter (handles pipes in street names) + $last_delimiter = strrpos($cookie_value, '|'); + if ($last_delimiter === false) { + return false; // Malformed cookie + } + + $data_part = substr($cookie_value, 0, $last_delimiter); + $provided_hash = substr($cookie_value, $last_delimiter + 1); + + // 3. CRITICAL: Verify the signature before trusting ANY data + // Ensure WP_PPPR_SALT is defined and matches the setter exactly + if (!defined('WP_PPPR_SALT')) { + return false; + } + + $expected_hash = hash_hmac('sha256', $data_part, WP_PPPR_SALT); + + if (!hash_equals($expected_hash, $provided_hash)) { + return false; // Tampered or invalid cookie + } + + // 4. Only now is it safe to explode the verified data + $fields = explode('|', $data_part); + + // Ensure we have enough fields + if (count($fields) < 5) { + return false; + } + + return [ + 'is_local' => (bool)$fields[0], + 'street' => $fields[1], + 'house_number' => $fields[2], + 'postcode' => $fields[3], + 'city' => $fields[4], + ]; +} + +function pppr_is_local() { + $first_char = $_COOKIE['PPPR'][0]; + return $first_char === '1'; +} diff --git a/prijs-per-postcode.php b/prijs-per-postcode.php index 40a9fd2..22b59fc 100644 --- a/prijs-per-postcode.php +++ b/prijs-per-postcode.php @@ -1,7 +1,5 @@ is_type("variation") && - isset($_SESSION["postcode_is_local"]) && - $_SESSION["postcode_is_local"] === true + isset($_COOKIE["PPPR"]) && + pppr_is_local() ) { $local_price = get_post_meta( $product->get_id(), @@ -189,10 +239,7 @@ class PrijsPerPostcode return false; } - $is_local = isset($_SESSION["postcode_is_local"]) - ? $_SESSION["postcode_is_local"] - : false; - $price = $is_local + $price = pppr_is_local() ? $variation->get_meta("_local_price", true) : $variation->get_regular_price(); diff --git a/session_dialog.php b/session_dialog.php index bda09f4..28037c3 100644 --- a/session_dialog.php +++ b/session_dialog.php @@ -5,7 +5,7 @@ function init_postcode_handlers($uri) if (strpos($uri, "/winkel/") !== false) { add_action("wp_enqueue_scripts", "modal_styles"); add_action("wp_footer", "send_postcode_data"); - if (!has_postcode()) { + if (!verify_pppr_cookie_string()) { if (!is_admin()) { WC()->cart->empty_cart(); } @@ -33,9 +33,9 @@ function show_modal() { echo <<<'HTML' + const postcodeModal = document.querySelector("#postcode_modal"); + postcodeModal.showModal(); + HTML; } @@ -185,14 +185,6 @@ function render_dialog_html() HTML; } -function has_postcode() -{ - if (isset($_SESSION["postcode"])) { - return true; - } - return false; -} - function handle_postcode_modal($data) { $params = $data->get_params(); @@ -276,23 +268,13 @@ function handle_postcode_modal($data) ]); exit(); } - $cookie_val = postcode_in_range($params["postcode"]).'|'.$params["postcode"].'|'.$straatnaam.'|'.$params["huisnummer"].'|'.$woonplaats.'|'; - - // 6. Succes: Sessie opslaan & response - $_SESSION["postcode"] = $params["postcode"]; - $_SESSION["huisnummer"] = $params["huisnummer"]; - $_SESSION["straatnaam"] = $straatnaam; - $_SESSION["woonplaats"] = $woonplaats; - $_SESSION["postcode_is_local"] = postcode_in_range($params["postcode"]); - - setcookie("PPPR", $cookie_val, [ - 'expires' => time() + 3600, - 'path' => '/', - 'domain' => $_SERVER['SERVER_NAME'], - 'secure' => true, // Required for cross-origin fetch - 'httponly' => true, // Prevents JavaScript access (XSS protection) - 'samesite' => 'Lax' // Use 'None' for cross-origin, requires secure=true - ]); + set_pppr_cookie( + postcode_in_range($params["postcode"]), + $straatnaam, + $params["huisnummer"], + $params["postcode"], + $woonplaats + ); echo json_encode([ "status" => "success", @@ -403,15 +385,16 @@ function modify_checkout_with_js() ) { return; } + $data = get_PPPR_data(); - $woonplaats = $_SESSION["woonplaats"]; + $city = $data["city"]; $postcode = $formatted_postcode = preg_replace( "/(\d+)([A-Z]+)/", '$1 $2', - strtoupper($_SESSION["postcode"]), + strtoupper($data["postcode"]), ); $address = - $_SESSION["straatnaam"] . " " . strtoupper($_SESSION["huisnummer"]); + $data["street"] . " " . strtoupper($data["house_number"]); echo << @@ -429,7 +412,7 @@ function modify_checkout_with_js() last_name: '', address_1: '{$address}', address_2: '', - city: '{$woonplaats}', + city: '{$city}', state: '', postcode: '{$postcode}', country: 'NL', @@ -465,10 +448,6 @@ function modify_checkout_with_js() HTML; } - - - - function load_assets_reset_postcode_on_checkout() { if (is_checkout() && !is_wc_endpoint_url()) { @@ -501,11 +480,7 @@ function handle_unset_session_fetch() if (!wp_verify_nonce($_POST["nonce"], "reset_postcode_nonce")) { wp_die("Security check failed."); } - - // Unset the specific session variable - if (isset($_SESSION["postcode"])) { - $_SESSION = []; - } + unset_pppr_cookie(); // Send a JSON response wp_send_json_success();