x, arg=>x, hash=>x) si $mode=-1. */ function securiser_action_auteur($action, $arg, $redirect = "", $mode = false, $att = '', $public = false) { // mode URL ou array if (!is_string($mode)) { $hash = calculer_action_auteur("$action-$arg", is_numeric($att) ? $att : null); $r = rawurlencode($redirect); if ($mode === -1) { return array('action' => $action, 'arg' => $arg, 'hash' => $hash); } else { return generer_url_action($action, "arg=" . rawurlencode($arg) . "&hash=$hash" . (!$r ? '' : "&redirect=$r"), $mode, $public); } } // mode formulaire $hash = calculer_action_auteur("$action-$arg"); $att .= " style='margin: 0px; border: 0px'"; if ($redirect) { $redirect = "\n\t\t"; } $mode .= $redirect . " "; return generer_form_action($action, $mode, $att, $public); } /** * Caracteriser un auteur : l'auteur loge si $id_auteur=null * * @param int|null $id_auteur * @return array */ function caracteriser_auteur($id_auteur = null) { static $caracterisation = array(); if (is_null($id_auteur) and !isset($GLOBALS['visiteur_session']['id_auteur'])) { // si l'auteur courant n'est pas connu alors qu'il peut demander une action // c'est une connexion par php_auth ou 1 instal, on se rabat sur le cookie. // S'il n'avait pas le droit de realiser cette action, le hash sera faux. if (isset($_COOKIE['spip_session']) and (preg_match('/^(\d+)/', $_COOKIE['spip_session'], $r)) ) { return array($r[1], ''); // Necessaire aux forums anonymes. // Pour le reste, ca echouera. } else { return array('0', ''); } } // Eviter l'acces SQL si le pass est connu de PHP if (is_null($id_auteur)) { $id_auteur = isset($GLOBALS['visiteur_session']['id_auteur']) ? $GLOBALS['visiteur_session']['id_auteur'] : 0; if (isset($GLOBALS['visiteur_session']['pass']) and $GLOBALS['visiteur_session']['pass']) { return $caracterisation[$id_auteur] = array($id_auteur, $GLOBALS['visiteur_session']['pass']); } } if (isset($caracterisation[$id_auteur])) { return $caracterisation[$id_auteur]; } if ($id_auteur) { include_spip('base/abstract_sql'); $t = sql_fetsel("id_auteur, pass", "spip_auteurs", "id_auteur=$id_auteur"); if ($t) { return $caracterisation[$id_auteur] = array($t['id_auteur'], $t['pass']); } include_spip('inc/minipres'); echo minipres(); exit; } // Visiteur anonyme, pour ls forums par exemple else { return array('0', ''); } } /** * Calcule une cle securisee pour une action et un auteur donnes * utilisee pour generer des urls personelles pour executer une action qui modifie la base * et verifier la legitimite de l'appel a l'action * * @param string $action * @param int $id_auteur * @param string $pass * @param string $alea * @return string */ function _action_auteur($action, $id_auteur, $pass, $alea) { static $sha = array(); if (!isset($sha[$id_auteur . $pass . $alea])) { if (!isset($GLOBALS['meta'][$alea])) { if (!$exec = _request('exec') or !autoriser_sans_cookie($exec)){ include_spip('inc/acces'); charger_aleas(); if (empty($GLOBALS['meta'][$alea])){ include_spip('inc/minipres'); echo minipres(); spip_log("$alea indisponible"); exit; } } } include_spip('auth/sha256.inc'); $sha[$id_auteur . $pass . $alea] = _nano_sha256($id_auteur . $pass . @$GLOBALS['meta'][$alea]); } if (function_exists('sha1')) { return sha1($action . $sha[$id_auteur . $pass . $alea]); } else { return md5($action . $sha[$id_auteur . $pass . $alea]); } } /** * Calculer le hash qui signe une action pour un auteur * * @param string $action * @param int|null $id_auteur * @return string */ function calculer_action_auteur($action, $id_auteur = null) { list($id_auteur, $pass) = caracteriser_auteur($id_auteur); return _action_auteur($action, $id_auteur, $pass, 'alea_ephemere'); } /** * Verifier le hash de signature d'une action * toujours exclusivement pour l'auteur en cours * * @param $action * @param $hash * @return bool */ function verifier_action_auteur($action, $hash) { list($id_auteur, $pass) = caracteriser_auteur(); if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere')) { return true; } if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere_ancien')) { return true; } return false; } // // Des fonctions independantes du visiteur, qui permettent de controler // par exemple que l'URL d'un document a la bonne cle de lecture // /** * Renvoyer le secret du site, et le generer si il n'existe pas encore * Le secret du site doit rester aussi secret que possible, et est eternel * On ne doit pas l'exporter * * @return string */ function secret_du_site() { if (!isset($GLOBALS['meta']['secret_du_site'])) { include_spip('base/abstract_sql'); $GLOBALS['meta']['secret_du_site'] = sql_getfetsel('valeur', 'spip_meta', "nom='secret_du_site'"); } if (!isset($GLOBALS['meta']['secret_du_site']) or (strlen($GLOBALS['meta']['secret_du_site']) < 64) ) { include_spip('inc/acces'); include_spip('auth/sha256.inc'); ecrire_meta('secret_du_site', _nano_sha256($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SERVER_SIGNATURE"] . creer_uniqid()), 'non'); lire_metas(); // au cas ou ecrire_meta() ne fonctionne pas } return $GLOBALS['meta']['secret_du_site']; } /** * Calculer une signature valable pour une action et pour le site * * @param string $action * @return string */ function calculer_cle_action($action) { if (function_exists('sha1')) { return sha1($action . secret_du_site()); } else { return md5($action . secret_du_site()); } } /** * Verifier la cle de signature d'une action valable pour le site * * @param string $action * @param string $cle * @return bool */ function verifier_cle_action($action, $cle) { return ($cle == calculer_cle_action($action)); } /** * Calculer le token de prévisu * * Il permettra de transmettre une URL publique d’un élément non encore publié, * pour qu’une personne tierce le relise. Valable quelques temps. * * @see verifier_token_previsu() * @param string $url Url à autoriser en prévisu * @param int|null id_auteur qui génère le token de prévisu. Null utilisera auteur courant. * @param string $alea Nom de l’alea à utiliser * @return string Token, de la forme "{id}*{hash}" */ function calculer_token_previsu($url, $id_auteur = null, $alea = 'alea_ephemere') { if (is_null($id_auteur)) { if (!empty($GLOBALS['visiteur_session']['id_auteur'])) { $id_auteur = $GLOBALS['visiteur_session']['id_auteur']; } } if (!$id_auteur = intval($id_auteur)) { return ""; } // On nettoie l’URL de tous les var_. $url = nettoyer_uri_var($url); $token = _action_auteur('previsualiser-' . $url, $id_auteur, null, $alea); return "$id_auteur-$token"; } /** * Vérifie un token de prévisu * * Découpe le token pour avoir l’id_auteur, * Retrouve à partir de l’url un objet/id_objet en cours de parcours * Recrée un token pour l’auteur et l’objet trouvé et le compare au token. * * @see calculer_token_previsu() * @param string $token Token, de la forme '{id}*{hash}' * @return false|array * - `False` si echec, * + Tableau (id auteur, type d’objet, id_objet) sinon. */ function verifier_token_previsu($token) { // retrouver auteur / hash $e = explode('-', $token, 2); if (count($e) == 2 and is_numeric(reset($e))) { $id_auteur = intval(reset($e)); } else { return false; } // calculer le type et id de l’url actuelle include_spip('inc/urls'); include_spip('inc/filtres_mini'); $url = url_absolue(self()); // verifier le token $_token = calculer_token_previsu($url, $id_auteur, 'alea_ephemere'); if (!$_token or $token !== $_token) { $_token = calculer_token_previsu($url, $id_auteur, 'alea_ephemere_ancien'); if (!$_token or $token !== $_token) { return false; } } return array( 'id_auteur' => $id_auteur, ); } /** * Décrire un token de prévisu en session * @uses verifier_token_previsu() * @return bool|array */ function decrire_token_previsu() { static $desc = null; if (is_null($desc)) { if ($token = _request('var_previewtoken')) { $desc = verifier_token_previsu($token); } else { $desc = false; } } return $desc; }