swap session with cookie with hash
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
wp-pppr-salt.php
|
||||
137
pppr_cookie.php
Normal file
137
pppr_cookie.php
Normal 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';
|
||||
}
|
||||
@@ -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;
|
||||
@@ -148,7 +198,7 @@ class PrijsPerPostcode
|
||||
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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user