swap session with cookie with hash

This commit is contained in:
Remo Zaros
2026-06-22 09:34:14 +02:00
parent ba63181e6a
commit e5e17cbafc
4 changed files with 224 additions and 64 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
wp-pppr-salt.php

137
pppr_cookie.php Normal file
View File

@@ -0,0 +1,137 @@
<?php
function create_pppr_salt() {
// 1. Check permissions first (Optional but recommended)
if ( ! wp_is_writable( plugin_dir_path( __FILE__ ) ) ) {
// Log error or handle failure
return false;
}
// 2. Define path INSIDE the plugin folder
$file_path = plugin_dir_path( __FILE__ ) . 'wp-pppr-salt.php';
// 3. Get the salt
$current_salt = wp_salt( 'auth' );
if ( empty( $current_salt ) ) {
return false;
}
// 4. Prepare content
$file_content = "<?php\n";
$file_content .= "// Auto-generated salt constant\n";
$file_content .= "// Exported salt from " . date( 'Y-m-d H:i:s' ) . "\n";
$file_content .= "define( 'WP_PPPR_SALT', '{$current_salt}' );\n";
// 5. Write the file
$bytes_written = file_put_contents( $file_path, $file_content, LOCK_EX );
// 6. Verify success
if ( $bytes_written === false ) {
// Handle error (e.g., log it)
return false;
}
return true;
}
function set_pppr_cookie($is_local, $street_name, $house_number, $postcode, $city) {
$data_string = (int)$is_local . "|" . $street_name . "|" . $house_number . "|" . $postcode . "|" . $city;
$signature = hash_hmac('sha256', $data_string, WP_PPPR_SALT);
$cookie_value = $data_string . "|" . $signature;
setcookie("PPPR", $cookie_value, [
'expires' => 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';
}

View File

@@ -1,7 +1,5 @@
<?php
require_once plugin_dir_path(__FILE__) . "session_dialog.php";
require_once plugin_dir_path(__FILE__) . "admin.php";
/*
* Plugin Name: Prijzen per poscodeereeks.
* Description: Producten worden gefiltered aan de hand van opgegegeven postcodereksen.Klanten met een postcode Die in de opgegegeven postcode reeks vallen zullen een lokale prijs zien. Waar iedereen die niet in de reeks vallen de overige regios prijs te zien krijgt.
@@ -10,11 +8,23 @@ require_once plugin_dir_path(__FILE__) . "admin.php";
* Text Domeain: prijs-per-postcode
*/
require_once plugin_dir_path(__FILE__) . "session_dialog.php";
require_once plugin_dir_path(__FILE__) . "admin.php";
require_once plugin_dir_path(__FILE__) . "pppr_cookie.php";
if (!defined("ABSPATH")) {
echo "big bag of potatoes";
exit();
}
register_activation_hook( __FILE__, 'create_pppr_salt' );
if ( ! defined( 'WP_PPPR_SALT' ) ) {
$salt_file = ABSPATH . 'wp-pppr-salt.php';
if ( file_exists( $salt_file ) ) {
require_once $salt_file;
}
}
class PrijsPerPostcode
{
public function __construct()
@@ -25,10 +35,7 @@ class PrijsPerPostcode
public function init()
{
if (session_status() == PHP_SESSION_NONE) {
ob_start();
@session_start();
}
$uri = $_SERVER["REQUEST_URI"];
init_postcode_handlers($uri);
init_postode_admin();
@@ -61,7 +68,7 @@ class PrijsPerPostcode
);
add_action("template_redirect", [
$this,
"controleer_postcode_op_woocommerce_paginas",
"check_postcode_on_every_woocommerce_page",
]);
add_filter(
"woocommerce_variation_is_visible",
@@ -118,8 +125,8 @@ class PrijsPerPostcode
if (
$variation_id &&
isset($_SESSION["postcode_is_local"]) &&
$_SESSION["postcode_is_local"]
isset($_COOKIE["PPPR"]) &&
pppr_is_local()
) {
$local_price = get_post_meta(
$variation_id,
@@ -133,7 +140,50 @@ class PrijsPerPostcode
}
}
/*
public function controleer_postcode_op_woocommerce_paginas()
{
if (is_admin() || defined('DOING_AJAX')) {
return;
}
// 2. Define target pages (Product, Cart, Checkout, etc., but NOT the Shop home)
if (
(is_product() ||
is_product_category() ||
is_product_tag() ||
is_cart() ||
is_checkout() ||
is_account_page()) &&
!is_shop()
) {
// 3. Retrieve cookie safely (null coalescing prevents "Undefined index" errors)
$cookie_string = $_COOKIE['PPPR'] ?? '';
// 4. Validate: Check if empty OR if the signature is invalid (tampered)
// verify_ppp_cookie() returns false if the hash doesn't match the data
if (empty($cookie_string) || !verify_ppp_cookie($cookie_string)) {
// Optional: Clear the invalid cookie from the browser immediately
setcookie('PPPR', '', time() - 3600, '/');
$shop_url = wc_get_page_permalink('shop');
// Fallback to home URL if shop page is not set
if ($shop_url === -1) {
$shop_url = home_url('/');
}
// 5. Redirect to the shop/home page to force postcode selection
wp_redirect($shop_url);
exit();
}
// Optional: If valid, you can now safely access the data
// $is_local = ($cookie_string[0] === '1');
}
}
*/
public function check_postcode_on_every_woocommerce_page()
{
if (is_admin() || defined("DOING_AJAX")) {
return;
@@ -141,14 +191,14 @@ class PrijsPerPostcode
if (
(is_product() ||
is_product_category() ||
is_product_tag() ||
is_cart() ||
is_checkout() ||
is_account_page()) &&
is_product_category() ||
is_product_tag() ||
is_cart() ||
is_checkout() ||
is_account_page()) &&
!is_shop()
) {
if (!isset($_SESSION["postcode_is_local"])) {
if (!verify_pppr_cookie_string()) {
wp_redirect(home_url("/winkel/"));
exit();
}
@@ -159,8 +209,8 @@ class PrijsPerPostcode
{
if (
$product->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();

View File

@@ -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'
<script id="postcode_modal_open">
const postcodeModal = document.querySelector("#postcode_modal");
postcodeModal.showModal();
</script>
const postcodeModal = document.querySelector("#postcode_modal");
postcodeModal.showModal();
</script>
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 <<<HTML
<script type="text/javascript" id="fill_address_fields">
@@ -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();