280 lines
No EOL
8.5 KiB
PHP
280 lines
No EOL
8.5 KiB
PHP
<?php
|
|
/**
|
|
* Plugin No-SPAM
|
|
* (c) 2008-2019 Cedric Morin Yterium&Nursit
|
|
* Licence GPL
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
if (!defined('_ECRIRE_INC_VERSION')) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Calculer une cle prive utilisee pour encoder les names des formulaires
|
|
* pour etre non predictible,
|
|
* on utilise un jeton fourni, le path du fichier qui depend du serveur et le secret_du_site()
|
|
*
|
|
* @param $jeton
|
|
* @return string
|
|
*/
|
|
function nospam_private_key($jeton) {
|
|
static $private_key = [];
|
|
|
|
if (empty($private_key[$jeton])) {
|
|
if (!function_exists('secret_du_site')) {
|
|
include_spip('inc/securiser_action');
|
|
}
|
|
|
|
$pk = $jeton . ":" . __FILE__ . ":" . secret_du_site();
|
|
|
|
if (function_exists('sha1'))
|
|
$pk = sha1($pk);
|
|
else
|
|
$pk = md5($pk);
|
|
$private_key[$jeton] = pack("H*", $pk);
|
|
}
|
|
|
|
return $private_key[$jeton];
|
|
}
|
|
|
|
/**
|
|
* Encoder un name avec un jeton
|
|
* les names que l'on encode sont prefixes de xx_ avant encodage pour permettre une verif apres decodage
|
|
* le name encode est lui prefixe de x_, pour eviter d'essayer de decoder brutalement tous les name
|
|
*
|
|
* @param string $name
|
|
* @param string $jeton
|
|
* @return string
|
|
*/
|
|
function nospam_name_encode($name, $jeton = "") {
|
|
static $private_key = array();
|
|
static $encoded = array();
|
|
if (isset($encoded[$jeton][$name])) {
|
|
return $encoded[$jeton][$name];
|
|
}
|
|
|
|
if (!$name) {
|
|
return $name;
|
|
}
|
|
|
|
if (!isset($private_key[$jeton])) {
|
|
$private_key[$jeton] = nospam_private_key($jeton);
|
|
if (!function_exists('_xor'))
|
|
include_spip("inc/filtres");
|
|
}
|
|
|
|
$cname = _xor("xx_$name", $private_key[$jeton]);
|
|
$cname = base64_encode($cname);
|
|
$cname = "x_" . rtrim(strtr(base64_encode($cname), '+/', '-_'), '=');
|
|
return $encoded[$jeton][$name] = $cname;
|
|
}
|
|
|
|
|
|
/**
|
|
* Decoder un name a partir d'un jeton
|
|
*
|
|
* @param string $name
|
|
* @param string $jeton
|
|
* @return string
|
|
*/
|
|
function nospam_name_decode($name, $jeton = "") {
|
|
static $private_key = array();
|
|
static $decoded = array();
|
|
|
|
if (isset($decoded[$jeton][$name])) {
|
|
return $decoded[$jeton][$name];
|
|
}
|
|
|
|
if (!$name) return $name;
|
|
if (strncmp($name, "x_", 2) !== 0) return $name;
|
|
if (!isset($private_key[$jeton])) {
|
|
$private_key[$jeton] = nospam_private_key($jeton);
|
|
if (!function_exists('_xor'))
|
|
include_spip("inc/filtres");
|
|
}
|
|
|
|
$cname = substr($name, 2);
|
|
$cname = base64_decode(str_pad(strtr($cname, '-_', '+/'), strlen($cname) % 4, '=', STR_PAD_RIGHT));
|
|
$cname = base64_decode($cname);
|
|
$cname = _xor($cname, $private_key[$jeton]);
|
|
// si ce n'etait pas un name encode, on retourne le name d'origine
|
|
if (strncmp($cname, "xx_", 3) !== 0) {
|
|
spip_log("decodage $name avec jeton $jeton errone ($cname)", 'nospam' . _LOG_ERREUR);
|
|
return $decoded[$jeton][$name] = $name;
|
|
}
|
|
|
|
return $decoded[$jeton][$name] = substr($cname, 3);
|
|
}
|
|
|
|
|
|
/**
|
|
* encrypter tous les names d'un form
|
|
* @param string $form
|
|
* @param bool $preserve_sessions_name
|
|
* si false les name commencant par session_ seront aussi encode
|
|
* @param null|string $jeton
|
|
* @param string $isbot
|
|
* @return mixed
|
|
*/
|
|
function nospam_encrypt_form_names($form, $preserve_sessions_name = true, $jeton = null, $isbot = false) {
|
|
// recuperer toutes les balises input, textarea, select
|
|
$balises = array_merge(extraire_balises($form, 'input'));
|
|
foreach ($balises as $k => $b) {
|
|
if (in_array(extraire_attribut($b, "type"), array("hidden", "file")))
|
|
unset($balises[$k]);
|
|
}
|
|
$balises = array_merge($balises,
|
|
extraire_balises($form, 'textarea'),
|
|
extraire_balises($form, 'select'));
|
|
|
|
if (is_null($jeton)) {
|
|
$jeton = "";
|
|
if (preg_match(",<input type='hidden' name='_jeton' value='([^>]*)' />,Uims", $form, $m))
|
|
$jeton = $m[1];
|
|
}
|
|
|
|
foreach ($balises as $k => $b) {
|
|
if ($name = extraire_attribut($b, "name")
|
|
AND (!$preserve_sessions_name or strncmp($name, "session_", 8) !== 0)) {
|
|
// cas des truc[chose] : on ne brouille que truc
|
|
$crypted_name = explode("[", $name);
|
|
$crypted_name[0] = nospam_name_encode($crypted_name[0], $jeton);
|
|
$crypted_name = implode("[", $crypted_name);
|
|
$b_e = inserer_attribut($b, "name", $crypted_name);
|
|
$form = str_replace($b, $b_e, $form);
|
|
}
|
|
}
|
|
|
|
if ($isbot) {
|
|
$form = str_replace(nospam_encrypt_html_hidden(), nospam_encrypt_html_checkbox(), $form);
|
|
}
|
|
|
|
return $form;
|
|
}
|
|
|
|
function nospam_encrypt_html_hidden() {
|
|
return "<input type='hidden' name='_nospam_encrypt' value='1' />";
|
|
}
|
|
function nospam_encrypt_html_checkbox() {
|
|
return "<label class='check_if_nobot'><input type='checkbox' name='_nospam_encrypt' value='1' /> " . _T('nospam:label_je_ne_suis_pas_un_robot') . "</label>";
|
|
}
|
|
|
|
/**
|
|
* Verifier/preparer les valeurs sur un formulaire crypte
|
|
* injecte un _encrypt en hidden, et gere le cas particulier ou l'on cohabite avec les fonctions autosave
|
|
* @param $valeurs
|
|
* @param $args
|
|
* @return array
|
|
*/
|
|
function nospam_encrypt_check_valeurs($valeurs, $args) {
|
|
$valeurs['_hidden'] .= nospam_encrypt_html_hidden();
|
|
// recuperer les autosave encryptes si possible
|
|
if (is_array($valeurs)
|
|
AND isset($valeurs['_autosave_id'])
|
|
AND $cle_autosave = $valeurs['_autosave_id']
|
|
AND include_spip("inc/cvt_autosave")
|
|
AND function_exists("autosave_clean_value")) {
|
|
|
|
$je_suis_poste = $args['je_suis_poste'];
|
|
|
|
$cle_autosave = serialize($cle_autosave);
|
|
$cle_autosave = $args['form'] . "_" . md5($cle_autosave);
|
|
|
|
// si on a un backup en session et qu'on est au premier chargement, non poste
|
|
// on restitue les donnees
|
|
if (isset($GLOBALS['visiteur_session']['session_autosave_' . $cle_autosave])
|
|
AND !$je_suis_poste) {
|
|
parse_str($GLOBALS['visiteur_session']['session_autosave_' . $cle_autosave], $vars);
|
|
if (isset($vars['_jeton'])
|
|
AND $jeton = $vars['_jeton']) {
|
|
foreach ($vars as $name => $val) {
|
|
if (($dname = nospam_name_decode($name, $jeton)) !== $name
|
|
AND isset($valeurs[$dname]))
|
|
$valeurs[$dname] = (is_string($val) ? autosave_clean_value($val) : array_map('autosave_clean_value', $val));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $valeurs;
|
|
}
|
|
|
|
/**
|
|
* Decrypter les name d'un POST (idempotent)
|
|
*
|
|
* @param $form
|
|
* optionel, on peut se reposer sur formulaire_action pour avoir le nom du form
|
|
* @return bool|string
|
|
* string : message d'erreur
|
|
* bool : indique si des names ont ete decodes ou non
|
|
*/
|
|
function nospam_encrypt_decrypt_post($form=null) {
|
|
if (is_null($form)) {
|
|
$form = _request('formulaire_action');
|
|
if (!$form) {
|
|
spip_log("SPAM_ENCRYPT_NAME active mais pas de formulaire_action pour utiliser nospam_encrypt_decrypt_post", 'nospam' . _LOG_INFO_IMPORTANTE);
|
|
#$ua = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'undefined');
|
|
#spip_log("Suspect SPAMMEUR (_nospam_encrypt) UA:$ua POST:" . json_encode($_POST), 'nospam_suspects' . _LOG_INFO_IMPORTANTE);
|
|
return _T('nospam:erreur_jeton');
|
|
}
|
|
}
|
|
|
|
// si l'encrypt a ete active depuis l'affichage initial de ce form, on rebalance l'erreur technique
|
|
// pour reforcer un POST
|
|
// si pas de _nospam_encrypt poste, on refuse la saisie => erreur
|
|
if (!_request('_nospam_encrypt')) {
|
|
spip_log("SPAM_ENCRYPT_NAME active mais _nospam_encrypt manquant sur formulaire $form", 'nospam' . _LOG_INFO_IMPORTANTE);
|
|
$ua = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'undefined');
|
|
spip_log("Suspect SPAMMEUR (_nospam_encrypt) UA:$ua POST:" . json_encode($_POST), 'nospam_suspects' . _LOG_INFO_IMPORTANTE);
|
|
return _T('nospam:erreur_jeton');
|
|
}
|
|
if (!$jeton = _request('_jeton')){
|
|
spip_log("SPAM_ENCRYPT_NAME active mais _jeton manquant sur formulaire $form", 'nospam' . _LOG_INFO_IMPORTANTE);
|
|
$ua = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'undefined');
|
|
spip_log("Suspect SPAMMEUR (_jeton) UA:$ua POST:" . json_encode($_POST), 'nospam_suspects' . _LOG_INFO_IMPORTANTE);
|
|
return _T('nospam:erreur_jeton');
|
|
}
|
|
|
|
$re_verifier = false;
|
|
foreach ($_POST as $k => $v) {
|
|
$kd = nospam_name_decode($k, $jeton);
|
|
if ($kd !== $k and _request($kd) !== $v) {
|
|
nospam_encrypt_posted($form, $kd, $v);
|
|
$re_verifier = true;
|
|
}
|
|
}
|
|
|
|
if ($re_verifier){
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Lister les valeurs que l'on a bien recue cryptees pour un formulaire donne
|
|
* Permet aux formulaires de verifier qu'une valeur critique n'a pas ete postee en clair par un spammeur
|
|
* * si $key et $value sont fournies, peuple le tableau et fait un set_request en meme temps
|
|
* @param string $form
|
|
* @param string $key
|
|
* @param mixed $value
|
|
* @return array
|
|
* liste des valeurs postees pour ce formulaire
|
|
*/
|
|
function nospam_encrypt_posted($form, $key = null, $value = null) {
|
|
static $posted = [];
|
|
|
|
if (!is_null($key)) {
|
|
if (!isset($posted[$form])) {
|
|
$posted[$form] = [];
|
|
}
|
|
|
|
$posted[$form][$key] = $value;
|
|
set_request($key, $value);
|
|
}
|
|
|
|
return $posted[$form] ?? [];
|
|
} |