/***************************************************************************\
* SPIP, Systeme de publication pour l'internet *
* *
- * Copyright (c) 2001-2016 *
+ * Copyright (c) 2001-2019 *
* Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
* *
* Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
* Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
\***************************************************************************/
-if (!defined('_ECRIRE_INC_VERSION')) return;
+/**
+ * Gestion des actions sécurisées
+ *
+ * @package SPIP\Core\Actions
+ **/
+
+if (!defined('_ECRIRE_INC_VERSION')) {
+ return;
+}
/**
- * interface d'appel:
- * - au moins un argument: retourne une URL ou un formulaire securises
- * - sans argument: verifie la securite et retourne _request('arg'), ou exit.
+ * Génère ou vérifie une action sécurisée
*
- * http://doc.spip.org/@inc_securiser_action_dist
+ * Interface d'appel:
+ *
+ * - au moins un argument: retourne une URL ou un formulaire securisés
+ * - sans argument : vérifie la sécurité et retourne `_request('arg')`, ou exit.
+ *
+ * @uses securiser_action_auteur() Pour produire l'URL ou le formulaire
+ * @example
+ * Tester une action reçue et obtenir son argument :
+ * ```
+ * $securiser_action = charger_fonction('securiser_action');
+ * $arg = $securiser_action();
+ * ```
*
* @param string $action
* @param string $arg
* @param string $redirect
* @param bool|int|string $mode
- * -1 : renvoyer action, arg et hash sous forme de array()
- * true ou false : renvoyer une url, avec & (false) ou & (true)
- * string : renvoyer un formulaire
+ * - -1 : renvoyer action, arg et hash sous forme de array()
+ * - true ou false : renvoyer une url, avec & (false) ou & (true)
+ * - string : renvoyer un formulaire
* @param string|int $att
* id_auteur pour lequel generer l'action en mode url ou array()
* atributs du formulaire en mode formulaire
* @param bool $public
* @return array|string
*/
-function inc_securiser_action_dist($action='', $arg='', $redirect="", $mode=false, $att='', $public=false)
-{
- if ($action)
+function inc_securiser_action_dist($action = '', $arg = '', $redirect = "", $mode = false, $att = '', $public = false) {
+ if ($action) {
return securiser_action_auteur($action, $arg, $redirect, $mode, $att, $public);
- else {
+ } else {
$arg = _request('arg');
$hash = _request('hash');
- $action = _request('action')?_request('action'):_request('formulaire_action');
- if ($a = verifier_action_auteur("$action-$arg", $hash))
+ $action = _request('action') ? _request('action') : _request('formulaire_action');
+ if ($a = verifier_action_auteur("$action-$arg", $hash)) {
return $arg;
+ }
include_spip('inc/minipres');
echo minipres();
exit;
}
/**
- * Attention: PHP applique urldecode sur $_GET mais pas sur $_POST
- * cf http://fr.php.net/urldecode#48481
- * http://doc.spip.org/@securiser_action_auteur
+ * Retourne une URL ou un formulaire sécurisés
+ *
+ * @note
+ * Attention: PHP applique urldecode sur $_GET mais pas sur $_POST
+ * cf http://fr.php.net/urldecode#48481
+ * http://code.spip.net/@securiser_action_auteur
+ *
+ * @uses calculer_action_auteur()
+ * @uses generer_form_action()
*
* @param string $action
* @param string $arg
* @param string $redirect
* @param bool|int|string $mode
- * -1 : renvoyer action, arg et hash sous forme de array()
- * true ou false : renvoyer une url, avec & (false) ou & (true)
- * string : renvoyer un formulaire
+ * - -1 : renvoyer action, arg et hash sous forme de array()
+ * - true ou false : renvoyer une url, avec & (false) ou & (true)
+ * - string : renvoyer un formulaire
* @param string|int $att
- * id_auteur pour lequel generer l'action en mode url ou array()
- * atributs du formulaire en mode formulaire
+ * - id_auteur pour lequel générer l'action en mode URL ou array()
+ * - atributs du formulaire en mode formulaire
* @param bool $public
* @return array|string
+ * - string URL, si $mode = true ou false,
+ * - string code HTML du formulaire, si $mode texte,
+ * - array Tableau (action=>x, arg=>x, hash=>x) si $mode=-1.
*/
-function securiser_action_auteur($action, $arg, $redirect="", $mode=false, $att='', $public=false) {
+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);
+ 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=$arg&hash=$hash" . (!$r ? '' : "&redirect=$r"), $mode, $public);
+ 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<input name='redirect' type='hidden' value='". str_replace("'", ''', $redirect) ."' />";
+ if ($redirect) {
+ $redirect = "\n\t\t<input name='redirect' type='hidden' value='" . str_replace("'", ''', $redirect) . "' />";
+ }
$mode .= $redirect . "
<input name='hash' type='hidden' value='$hash' />
<input name='arg' type='hidden' value='$arg' />";
/**
* Caracteriser un auteur : l'auteur loge si $id_auteur=null
*
- * http://doc.spip.org/@caracteriser_auteur
- *
* @param int|null $id_auteur
* @return array
*/
-function caracteriser_auteur($id_auteur=null) {
+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 (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','');
+ 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'])
+ 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 (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)
+ 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
+ } // Visiteur anonyme, pour ls forums par exemple
else {
- return array('0','');
+ return array('0', '');
}
}
* utilisee pour generer des urls personelles pour executer une action qui modifie la base
* et verifier la legitimite de l'appel a l'action
*
- * http://doc.spip.org/@_action_auteur
- *
* @param string $action
* @param int $id_auteur
* @param string $pass
*/
function _action_auteur($action, $id_auteur, $pass, $alea) {
static $sha = array();
- if (!isset($sha[$id_auteur.$pass.$alea])){
- if (!isset($GLOBALS['meta'][$alea]) AND _request('exec')!=='install') {
- include_spip('base/abstract_sql');
- $GLOBALS['meta'][$alea] = sql_getfetsel('valeur', 'spip_meta', "nom=" . sql_quote($alea));
- if (!($GLOBALS['meta'][$alea])) {
+ if (!isset($sha[$id_auteur . $pass . $alea])) {
+ if (!isset($GLOBALS['meta'][$alea]) and _request('exec') !== 'install') {
+ include_spip('inc/acces');
+ charger_aleas();
+ if (empty($GLOBALS['meta'][$alea])) {
include_spip('inc/minipres');
echo minipres();
spip_log("$alea indisponible");
}
}
include_spip('auth/sha256.inc');
- $sha[$id_auteur.$pass.$alea] = _nano_sha256($id_auteur.$pass.@$GLOBALS['meta'][$alea]);
+ $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]);
}
- 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
- * http://doc.spip.org/@calculer_action_auteur
*
* @param string $action
* @param int|null $id_auteur
* @return string
*/
-function calculer_action_auteur($action, $id_auteur=null) {
+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
- * http://doc.spip.org/@verifier_action_auteur
*
* @param $action
* @param $hash
*/
function verifier_action_auteur($action, $hash) {
list($id_auteur, $pass) = caracteriser_auteur();
- if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere'))
+ if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere')) {
return true;
- if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere_ancien'))
+ }
+ if ($hash == _action_auteur($action, $id_auteur, $pass, 'alea_ephemere_ancien')) {
return true;
+ }
+
return false;
}
* Le secret du site doit rester aussi secret que possible, et est eternel
* On ne doit pas l'exporter
*
- * http://doc.spip.org/@secret_du_site
- *
* @return string
*/
function secret_du_site() {
- if (!isset($GLOBALS['meta']['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)) {
+ 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');
+ 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
- * http://doc.spip.org/@calculer_cle_action
*
* @param string $action
* @return string
*/
function calculer_cle_action($action) {
- if (function_exists('sha1'))
+ if (function_exists('sha1')) {
return sha1($action . secret_du_site());
- else
+ } else {
return md5($action . secret_du_site());
+ }
}
/**
* Verifier la cle de signature d'une action valable pour le site
- * http://doc.spip.org/@verifier_cle_action
*
* @param string $action
* @param string $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;
+}
\ No newline at end of file