jeudi 17 novembre 2011

Cross-site Ajax proxy version curl

La technique Ajax permet de modifier partiellement une page affichée par le navigateur pour la mettre à jour sans avoir à recharger cette dernière dans son intégralité. Elle dépend de XMLHttpRequest, objet intégré coté client utilisable en Javascript.

Il existe une multitude de tutoriels décrivant le fonctionnement et techniques.

Pour ce post, je m'intéresserai principalement à une limitation de la majorité des navigateurs actuels, due à des raisons de sécurité, concernant l'utilisation de domains différents.
En effet, une script JS lancé sur une page www.name.com ne peut normalement pas diriger d'objet vers un domain différent.

Là où cela devient vraiment embêtant, c'est que les subdomains sont égalements limités : impossible d'utiliser, par exemple, l'adresse ww1.name.com pour ces mêmes fonctions.
Dans le cas où vous devriez lancer des scripts sur différents serveurs, comme pour l'envoi d'images à un serveur Master, depuis un serveur Slave (Post à venir), le navigateur bloquerait ces communications.

Alors comment faire ?

La solution s'appelle le "Cross-site Ajax".
L'idée est de créer un fichier PHP fonctionnant comme un proxy et dont nous allons nous servir en appel Ajax pour communiquer avec les différents domains/subdomains.

Je vous propose donc un fichier permettant d'outrepasser les protections navigateurs, basé sur l'extension cURL (librairie libcurl, voir post Plus rapide que file_get_contents : optez pour cURL !) :

Contenu du fichier : csaproxy.php

 <?php
header('Content-type: text/html; charset=iso-8859-1');


if ( $_GET['pass'] != "abcde12345" ) die(); //protection par mot de passe lors de l'appel



function curl_http_request(
    $url = 'www.domain.com/index.php',         /* Target URL */
    $getdata = array(),        /* HTTP GET Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    $postdata = array(),       /* HTTP POST Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    $filesdata = array(),       /* HTTP FILES Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    $cookie = array()         /* HTTP Cookie Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    ) {

    $curlopt = array();
    $cookie_str = '';
    $getdata_str = count($getdata) ? '?' : '';

    foreach ($getdata as $k => $v) $getdata_str .= (strlen($getdata_str)>0?'&':'').urlencode($k).'='.urlencode($v);

   //assignation uploads en POST : _POST['champ']=filetmpname et _POST['champ_real']=filename
    foreach ($filesdata as $k => $v) {
        if ( $filesdata[$k]['error'] == UPLOAD_ERR_OK ) {
            $postdata[$k]='@'.$filesdata[$k]['tmp_name'];
            $postdata[$k."_real"]=$filesdata[$k]['name'];
        }
    }
   
    foreach ($cookie as $k => $v) $cookie_str .= (strlen($cookie_str)>0?'; ':'').urlencode($k).'='.urlencode($v);

    if ( count($postdata) ) {
        $curlopt = array (
        CURLOPT_POST => 1,
        CURLOPT_POSTFIELDS => $postdata
        );
    }

    $default_curlopt = array(
    CURLOPT_TIMEOUT => 2, //timeout 2sec
    CURLOPT_FAILONERROR => 1, //si page error, on ne d/l pas
    CURLOPT_RETURNTRANSFER => 1, //return le transfert as a string
    CURLOPT_FOLLOWLOCATION => 1, //on follow si redirection header desactive par défaut
    CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT']?$_SERVER['HTTP_USER_AGENT']:"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.13) Gecko/20101203 AlexaToolbar/alxf-1.54 Firefox/3.6.13 GTB7.1"
    );
    $curlopt = array(CURLOPT_URL => $url.$getdata_str) + $curlopt + $default_curlopt;
   
   
    // Création d'une nouvelle ressource cURL
    $ch = curl_init();
   
    // Configuration de l'URL et d'autres options
    curl_setopt_array($ch, $curlopt);
   
    //Envoi des données
    $results_returned = curl_exec($ch);
    if ( $results_returned === false ) trigger_error(curl_error($ch));

    // Fermeture de la session cURL
    curl_close($ch);
   
    return $results_returned;
}


//destinations autorisées, en double pour redoubler de vigilance
$accepted_file_proxycsa=array(
    '/script.php',
    '/script.php' => 'www.domain.com',
    '/main/postit.php',
    '/main/postit.php' => 'sub.domain.com'
);

if ( in_array($_GET['filename_csa'],$accepted_file_proxycsa) ) {
    $target_filename=$_GET['filename_csa'];
    $target_site=$accepted_file_proxycsa[$_GET['filename_csa']];
}

if (( strlen($target_filename) > 0 ) && ( strlen($target_site) > 0 )) {
    echo curl_http_request($target_site.$target_filename,$_GET,$_POST,$_FILES,$_COOKIE);
}

?>

Une fois ce fichier uploadé sur votre serveur, vous pouvez désormais appeler tout fichier présent dans n'importe quel domain/subdomain.
Ne pas oublier d'inclure les destinations autorisées dans le array $accepted_file_proxycsa. Vous pouvez également vous affranchir de ce passage et rediriger directement l'url contenu dans le $_GET.


Exemple de code Javascript incluant le cross-site Ajax :
<script type="text/javascript">

function csajax(CHOIX,idcible) {   
   
    var xhr_object = new Array;
    xhr_object[CHOIX] = null;
    if(window.XMLHttpRequest) // Firefox
        xhr_object[CHOIX] = new XMLHttpRequest();  
    else if(window.ActiveXObject) // Internet Explorer  
        xhr_object[CHOIX] = new ActiveXObject("Microsoft.XMLHTTP");  
    else { // XMLHttpRequest non supporté par le navigateur  
        alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");  
        return;
    }


   xhr_object[CHOIX].open("POST", "http://<?php echo $_SERVER['SERVER_NAME']; ?>/csaprx.php?filename_csa=/main/postit.php", true);  


    xhr_object[CHOIX].onreadystatechange = function() {
        if ( CHOIX == "postit" ) {
            if (( xhr_object[CHOIX].readyState == 4) && ( xhr_object[CHOIX].responseText.length > 0 )) {
                document.getElementById(idcible).innerHTML=xhr_object[CHOIX].responseText;
                xhr_object[CHOIX].abort;
             }
        }
     };

    xhr_object[CHOIX].setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=iso-8859-1");

    if ( CHOIX == "postit" ) {
        var data = "message="+encodeURIComponent(document.getElementById('champ_msg').value)+'&auteur='+encodeURIComponent(document.getElementById('champ_auteur').value);

        xhr_object[CHOIX].send(data);
    }
}
</script>

Formulaire HTML correspondant:
<input type="text" name="champ_msg">
<input type="button" value="Envoyer" onclick="csajax('postit','reponse');">
<div id="reponse"></div>

Cet exemple enverra le contenu du champ_msg à http://sub.domain.com/main/postit.php et retournera le résultat dans le div reponse.
C'est aussi simple que ça !

Protection injection MySQL et failles XSS

Ces derniers temps, nous pouvons assister à un déluge d'affaires de piratages, assez médiatisés car touchant plusieurs millions de clients (Sony, Steam, etc). Plus récemment, nous pouvons également parler du site de l'UMP dont une simple faille de type "SQL injection" a permis aux hackers de publier une liste complète de 1000 cadres.

Il faut savoir que ce type d'attaque est très basique et de ce fait, très répandu.
On ne peut concevoir un site internet impliquant l'appel d'une base de données par l'intermédiaire de formulaires, sans penser à sécuriser les informations entrantes qui seront utilisées dans les requêtes.

Alors ? Des sites internet ou plateformes créés un peu trop vite ? Oublis, inattentions ou incompétence des stagiaires développeurs ?

Quoi qu'il en soit, pour ce post, je vous propose un bout de script PHP destiné à sécuriser la récupération de données afin d'éviter les attaques de type "SQL injection" et failles "XSS". La fonction vérifie le type en analysant la chaine contenue dans la variable en utilisant des regexp et ne retourne que ce qui est conforme au pattern.

<?php

//réplique de la fonction mysql_escape permettant de s'affranchir d'une connexion MySQL ouverte (voir php.net)
 function mysql_escape_mimic($inp) {
    if (is_array($inp)) return array_map(__METHOD__, $inp);
    if(!empty($inp) && is_string($inp)) return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
    return $inp;
}

//fonction permettant de filtrer les types int, float ou string
function mysql_varclean($text,$dest=null) {
    if ( $dest == "var_int" ) { preg_match("/(-?[0-9]+)/i",$text,$matches); return $matches[1]?$matches[1]:0; }
    elseif ( $dest == "var_float" ) { preg_match("/(-?[0-9]+[.]?[0-9]*)/i",$text,$matches); return $matches[1]?$matches[1]:0; }
    elseif ( $dest == "var_string" ) return trim(mysql_escape_mimic(htmlentities($text)));
    else return trim(mysql_escape_mimic(htmlentities($text)));
}


?>


L'envoi d'une requête MySQL de lecture, modification ou suppression s'effectue alors de la manière suivante :

<?php

$rech_auteur=strlen($_GET['auteur'])>0?$_GET['auteur']$_POST['auteur']; //récupère la donnée "auteur" en priorité sur le GET sinon POST

mysql_connect("localhost", "mysql_user", "mysql_password") or die("Impossible de se connecter : " . mysql_error()); //connexion à la bdd Mysql
mysql_select_db("mydb"); //choix de la base

$result = mysql_query("SELECT `titre`, `annee` FROM `mytable` WHERE `auteur` = ".mysql_varclean($rech_auteur,"var_int")); //envoi de la requête sécurisée

while ($data = mysql_fetch_array($result)) {
   echo "Titre: ".$data['titre']." - année : ".$data['annee']." <br />";
}

mysql_free_result($result
);

?>

Principe de précaution (paranoïa oblige), je vous conseille d'appliquer la fonction à toutes les variables PHP incluses directement dans chacune de vos requêtes MySQL. A vous d'adapter le bon type (int, float ou string) aux variables. De cette façon, vous ne risquez aucun oubli.

Remarque : en ce qui concerne les variables de type string (sujettes aux attaques XSS), j'ai volontairement choisi d'appliquer un htmlentities() afin d'enregistrer des données saines directement dans la base de donnée MySQL. Logiquement, il aurait plutôt fallu enregistrer les données en brut puis appliquer le htmlentities() uniquement par la suite, à l'affichage, car :
- htmlentities convertit certains caractères en entité HTML et rend donc impossible l'exploitation de la base de données pour des recherches de mots clés (notamment accentués) ;
- la conversion en entité HTML peut augmenter considérablement la taille de la variable et par conséquent la taille de la table de votre base de donnée.

Plus rapide que file_get_contents : optez pour cURL !


Lors de la réalisation et mise au point de robots en PHP (bot crawlers notamment), je me suis aperçu que les durées d'exécution du script étaient énormes. Il s'agissait en fait de la fonction file_get_contents() qui met énormément de temps à se connecter/restituer le résultat de la page appelée.

Afin de résoudre le problème, il existe une librairie appelée libcurl contenant une extension : cURL. Cette extension *magique* est 2 à 3 fois plus rapide que les file_get_contents() et fopen() et beaucoup plus facile à utiliser que les fonctions de socket PHP.

Comment l'utiliser ?

Voici un exemple de fonction PHP :
<?php

function curl_http_request(
    $url = 'www.domain.com/index.php',         /* Target URL */
    $getdata = array(),        /* HTTP GET Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    $postdata = array(),       /* HTTP POST Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    $filesdata = array(),       /* HTTP FILES Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    $cookie = array()         /* HTTP Cookie Data ie. array('var1' => 'val1', 'var2' => 'val2') */
    ) {

    $curlopt = array();
    $cookie_str = '';
    $getdata_str = count($getdata) ? '?' : '';

    foreach ($getdata as $k => $v) $getdata_str .= (strlen($getdata_str)>0?'&':'').urlencode($k).'='.urlencode($v);

   //assignation uploads en POST : _POST['champ']=filetmpname et _POST['champ_real']=filename
    foreach ($filesdata as $k => $v) {
        if ( $filesdata[$k]['error'] == UPLOAD_ERR_OK ) {
            $postdata[$k]='@'.$filesdata[$k]['tmp_name'];
            $postdata[$k."_real"]=$filesdata[$k]['name'];
        }
    }
   
    foreach ($cookie as $k => $v) $cookie_str .= (strlen($cookie_str)>0?'; ':'').urlencode($k).'='.urlencode($v);

    if ( count($postdata) ) {
        $curlopt = array (
        CURLOPT_POST => 1,
        CURLOPT_POSTFIELDS => $postdata
        );
    }

    $default_curlopt = array(
    CURLOPT_TIMEOUT => 2, //timeout 2sec
    CURLOPT_FAILONERROR => 1, //si page error, on ne d/l pas
    CURLOPT_RETURNTRANSFER => 1, //return le transfert as a string
    CURLOPT_FOLLOWLOCATION => 1, //on follow si redirection header desactive par défaut
    CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT']?$_SERVER['HTTP_USER_AGENT']:"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.13) Gecko/20101203 AlexaToolbar/alxf-1.54 Firefox/3.6.13 GTB7.1"
    );
    $curlopt = array(CURLOPT_URL => $url.$getdata_str) + $curlopt + $default_curlopt;
   
   
    // Création d'une nouvelle ressource cURL
    $ch = curl_init();
   
    // Configuration de l'URL et d'autres options
    curl_setopt_array($ch, $curlopt);
   
    //Envoi des données
    $results_returned = curl_exec($ch);
    if ( $results_returned === false ) trigger_error(curl_error($ch));

    // Fermeture de la session cURL
    curl_close($ch);
   
    return $results_returned;
}

//exemple: fishing du fichier test.php, utilisant tous les paramètres de formulaire
echo curl_http_request("www.domain.com/test.php",$_GET,$_POST, $_FILES, $_COOKIE);
?>

Facile, accessible et supporte de nombreux protocoles.

Backup automatique de serveur lamp (debian)

En louant des serveurs dédiés lowcost de type Kimsufi (OVH) ou Dedibox (Online.net/Free), la plus grande source de stress (pour ma part) est que le serveur devienne inaccessible, tombe en panne, ou qu'il y ait carrément pertes de données... l'offre de service étant restreinte au minimum, il faut faire avec les moyens du bord.

Si votre serveur est sous debian, je vous propose donc un petit script shell à intégrer, qui:
  • passe votre site internet "en maintenance" (remplacement de fichiers .htaccess) ;
    • nécessaire: à la racine de votre site /var/www/
      • .htaccess
      • .htaccess_maintenance (redirigeant vers une page maintenance.htm)
  • effectue une maintenance de toutes les tables MySQL (optimize/repair/etc.) ;
  • archive et compresse le système sous un nom de fichier daté (format TGZ) ;
  • transfère l'archive vers votre serveur FTP tiers, dans des répertoires classés par date ;
    • nécessaire: package "yafc" (client FTP)
  • supprime les archives vieilles de plus de 30 jours (à régler en fonction de la capacité de votre FTP) ;
    • nécessaire: tâche cron quotidienne (sinon, redéfinir un nombre de jour facteur de la fréquence de la tâche cron)
  • vous envoie un email de notification au démarrage et à la fin d'exécution du script ;
Voici le contenu du script "/cron/backup.sh" à ajuster selon votre configuration :


#file: /cron/backup.sh

#source: http://y-thot.blogspot.com/

#Email de notif
email_notif="adresse@email.com"

#Configuration du FTP
ftp_login="login"
ftp_password="password"
ftp_host="adresse"

phpscript_optimisation="/var/www/maintenance_sql.php password all all"


echo 'Demarrage : ' `date`
echo "Backup commencé à " `date` | mail -s "Backup report" $email_notif

#Configurer les différents fichiers .htaccess pour la redirection vers une page maintenance.htm
echo 'Setting site to : Maintenance (htaccess moves)'
mv /var/www/.htaccess /var/www/.htaccess_moved
mv /var/www/.htaccess_maintenance /var/www/.htaccess

#Date du jour
NOW="$(date +"%d-%m-%Y")"

# Emplacement du dossier de backup local
Dir_backup_local="/ftp_backup"
[ ! -d "/var"$Dir_backup_local/ ] && mkdir -p "/var"$Dir_backup_local/ || :

echo "Optimisations des tables"
php $phpscript_optimisation

echo "Fermeture du service mysql"
service mysql stop

File_db="/var"$Dir_backup_local"/backup.$NOW.tgz"
echo "Enregistrement/compression de / :" $File_db
tar -czf $File_db / --exclude="/var"$Dir_backup_local --exclude=/proc --exclude=/dev --exclude=/sys --exclude=/mnt


#nbre de jour de backup a conserver
nbjours_backup=30
j_a_delete=$(date +%d-%m-%Y --date "$nbjours_backup days ago")

## Conf des rep de backup pour le ftp
Dir_backup_ftp="/backup/"


echo "Transfert au FTP"
## Upload sur le ftp
yafc $ftp_login:$ftp_password@$ftp_host <<EOF
mkdir $Dir_backup_ftp
cd $Dir_backup_ftp
mkdir $NOW
cd $NOW
lcd "/var"$Dir_backup_local/
put -nr *
cd ..
rm -r $j_a_delete
bye
EOF

if [ "$Dir_backup_local" != "" ]; then
cd /var/
echo "Suppression des backup locaux"
rm -r "/var"$Dir_backup_local/
fi

echo "Démarrage de mysql"
service mysql start

echo "ReOptimisations des tables"
php $phpscript_optimisation

echo 'Setting site to : Production (htaccess moves)'
mv /var/www/.htaccess /var/www/.htaccess_maintenance
mv /var/www/.htaccess_moved /var/www/.htaccess

echo 'Fin : ' `date`


echo "Backup terminé à " `date` | mail -s "Backup report" $email_notif


Une fois, le fichier créé, n'oubliez pas de lui donner les droits d'exécution en entrant dans la console :

root@ksXXXXXX:/# chmod u+x /cron/backup.sh

Au passage, vérifiez que votre script fonctionne.
Si tout est ok, il ne reste alors, plus qu'à configurer une nouvelle tache cron pour lancer quotidiennement le script. Pour se faire, lancez le programme de configuration des tâches cron:

root@ksXXXXXX:/# crontab -e

Puis ajoutez la ligne suivante (par exemple) afin de lancer l'opération de maintenance tous les jours à 05h10 du matin avec création de log :

10 05 * * * /cron/backup.sh >> /cron/backup.log

C'est fini !

Afin d'éviter de vilaines erreurs, je vous invite à ne pas recopier directement tout le script mais à l'étudier et l'adapter en fonction de vos besoins et de votre configuration.
Et ne lésinez pas sur les tests !


Maintenance des tables d'une base de données MySQL




Les tables d'une base de données MySQL impliquent nécessairement une fragmentation des fichiers à l'utilisation (suppressions/ajouts d'entrées, ou modifications de champs dynamique VARCHAR, BLOB ou TEXT). 
Les lignes effacées sont, par exemple, conservées dans une liste et les prochaines opérations d'INSERT réutilisent les vieilles positions de lignes, entraînant des espaces de "vide" dans les fichiers de table.
Plus les tables sont fragmentées et plus les temps d'accès aux informations peuvent en être impactés.
Afin de récupérer ces espaces et d'optimiser le contenu des tables, il est nécessaire d'effectuer des opérations de maintenance, en fonction de leur fragmentation.

Pour se faire, il existe plusieurs solutions, dont l'utilisation de l'interface phpMyAdmin qui propose, depuis la fenêtre de sélection des tables, d'effectuer ces opérations de maintenance. Cette solution implique évidemment d'avoir, au préalable, installé l'outil sur votre serveur.

Autrement, je vous propose un petit script PHP très simple vous permettant de lancer des opérations de maintenance de base (analyze, check... optimize) sur vos tables de manière automatique (ne s'applique qu'aux moteurs de table MyISAM).



<?php
/*
Syntaxe:
web: http://votre-url/script.php?pass=password&action=idaction [&option=all]
shell: php script.php password idaction [all]


idaction={
  • "1" ou "analyze" : (analyse et stocke les clés de distribution)
  • "2" ou "check" : (vérifie l'intégrité des tables)
  • "3" ou "checksum" : (calcule la somme de contrôle des tables)
  • "4" ou "repair" (répare les tables corrompues)
  • "5" ou "optimize" (défragmente, trie et met à jour les stats)
  • "all" : effectue toutes les opérations
}

l'ajout du paramètre supplémentaire "all" permet de forcer l'opération sur toutes les tables (sinon uniquement les tables avec perte).

Source: http://y-thot.blogspot.com/

*/

//variables à définir
$script_password="password";


$mysql_host="localhost";
$mysql_user="root";
$mysql_pass="";
$mysql_bdd="votrebasededonnee"; //base de donnée contenant les tables

echo "Script d'optimisation des tables: lancement $mode_cli";

if ( $pass != $script_password ) die("Done. $mode_cli"); //message de quit à personnaliser

if (is_array($argv)) { //accès par shell php
//cli: "php /etc/www/optimisealltables.php pass action option"
    if ($argv[1]) $pass = $argv[1];
    else $pass="";
    if ($argv[2]) $action = $argv[2];
    else $action="5";
    if ($argv[3] == "all" ) $query = "SHOW TABLE STATUS";
    else $query = "SHOW TABLE STATUS WHERE Data_free >0";
   
    $mode_cli="\n";
}
else { //accès par apache
    if ( $_GET['pass'] ) $pass = $_GET['pass'];
    else $pass = "";
    if ( $_GET['action'] ) $action=$_GET['action'];
    else $action = "5";
    if ( $_GET['option'] == "all" ) $query = "SHOW TABLE STATUS";
    else $query = "SHOW TABLE STATUS WHERE Data_free >0";
    $mode_cli="<br />";
   ini_set ('max_execution_time', 60*30); //30 minutes max d'exécution du script, mettre à 0 si pas de limite
}
  
if ( mysql_connect($mysql_host,$mysql_user,$mysql_pass) ) echo "Connexion à la base $mode_cli";
else die("Echec de la connexion");

if ( mysql_select_db($mysql_bdd) ) echo "Sélection de la bdd $mode_cli";
else die("Echec du choix de la bdd");

$result = mysql_query($query) or die("error query");
while($line = mysql_fetch_array($result)) {
    if ( $line[1] != "MyISAM" ) continue;
   
    if (( $action == "all" ) || ( $action == "analyze" ) || ( $action == "1" )) {
        echo "Analyze de la table ".$line[0]." : ";
        mysql_query("ANALYZE TABLE `".$line[0]."`"); //analyse et stocke les clés de distribution
    }
    if (( $action == "all" ) || ( $action == "check" ) || ( $action == "2" )) {
        echo "Check de la table ".$line[0]." : ";
        mysql_query("CHECK TABLE `".$line[0]."` EXTENDED"); //verifie intégrité des tables
    }
    if (( $action == "all" ) || ( $action == "checksum" ) || ( $action == "3" )) {
        echo "Checksum de la table ".$line[0]." : ";
        mysql_query("CHECKSUM TABLE `".$line[0]."` EXTENDED"); //calcule la somme de contrôle des tables
    }
    if (( $action == "all" ) || ( $action == "repair" ) || ( $action == "4" )) {
        echo "Repair de la table ".$line[0]." : ";
        mysql_query("REPAIR TABLE `".$line[0]."` EXTENDED"); //répare les tables corrompues
    }
    if (( $action == "all" ) || ( $action == "optimize" ) || ( $action == "5" )) {
        echo "Optimisation de la table ".$line[0]." : "; //défragmente, trie et met à jour les stats
        mysql_query("OPTIMIZE TABLE `".$line[0]."`");
    }

    echo "effectué. $mode_cli";
}

echo "Script d'optimisation des tables: terminé $mode_cli";
?>


La récupération des variables d'entrée permet d'utiliser le script soit à distance (méthode GET, Apache installé), soit de lancer directement le script en local sur votre serveur (linux ou windows) par la commande "php" (debian: package "php-cli" à installer). Cette seconde méthode servira, en outre, à pouvoir automatiser cette fonction de manière autonome (voir post Backup automatique de serveur LAMP).

Bref, à configurer/modifier/optimiser/personnaliser selon vos besoins.

lundi 14 novembre 2011

Exigez le plus pour obtenir le moins

Une technique vieille comme le monde : "exiger le plus pour obtenir le moins"

Ne vous faites plus avoir lors de vos entretiens ou périodes de négociations.
Une solution: Demandez toujours plus !

La plupart du temps, la majorité des gens font preuve d'un certain discernement. Mais, pris par surprise ou au dépourvu, cette stabilité de raisonnement peut se voir souvent dégradée.
Il faut savoir que le système de réflexion de l'homme fonctionne en grande partie par différenciation et comparaison. Toute analyse ne se fait qu'en éclatant l'objet étudié de son environnement.


Le plan est toujours le même:
  1. définissez clairement votre objectif (atteignable avec la personne concernée).
  2. trouvez la cible et proposez un truc énorme, inaccessible. La réaction de la cible ne doit pouvoir être que de l'ordre du refus.
  3. la cible refuse votre première proposition: souffrance émotionnelle ressentie chez la cible car le refus entraine toujours un certain sentiment de culpabilité (c'est pourquoi la cible "s'excuse", la plupart du temps).
  4. montrez alors votre (fausse) déception : intensification de la souffrance de la cible ("remuage" de couteau dans la plaie) qui transforme le sentiment de culpabilité en regrets. La cible se sent redevable et voudrait pouvoir se faire pardonner ;
  5. suggérez alors votre seconde proposition (c'est-à-dire, votre objectif initial) en la minimisant un maximum (utilisez le terme "au moins").
  6. empreinte du sentiment d'être redevable, la cible devrait accepter.
 
Quelques exemples:
- présenter des produits aux tarifs très élevés, pour en vendre d'autres moins chers qui paraissent soudainement beaucoup plus... abordables ; 
- accoster une inconnue dans la rue et lui proposer directement d'aller au cinéma. Refusant alors (sinon prenez la fuite), lui proposer innocemment d'aller "au moins" boire un verre à côté.
- un vendredi, demander à un employé de travailler tout le weekend en sachant pertinemment qu'il ne le pourra pas. Face au refus, lui suggérer qu'il reste "au moins" plus tard ce même soir pour finir.


L'idée est de toujours éviter de demander directement et d'utiliser d'autres chemins pour arriver à votre objectif, ou au contraire, éviter de vous faire manipuler.

Communication interpersonnelle : respectez les distances

Certains d’entre vous l’ont peut-être remarqué : nous nous tenons toujours à une certaine distance lors d'échanges et nous ne nous positionnons jamais de la même manière avec un inconnu, son supérieur, son petit ami, un membre de la famille, etc.

En fait, il existe une sorte de zone invisible mais perceptible, reflétant le degré d'affinité.

La plus éloignée est la distance publique : plus de 7 mètres. Elle s'applique généralement lorsque l’on s’adresse à un grand nombre de personnes. La plupart du temps, il est nécessaire de fortement hausser la voix ou d’utiliser un appareil amplificateur (micro) et la communication est unidirectionnelle (conférencier et son public).

De 1 à 3 mètres, c’est la distance sociale. Elle démontre un certain détachement voulu. Par exemple, un responsable de recrutement qui vous accueille (protégé) derrière son bureau (tel un bunker).

Un peu plus proche, de 0,5 à 1 mètre (en fait, un peu plus que la longueur de votre bras tendu), c'est la distance personnelle. Cette distance indique généralement une bonne entente entre les personnes.
C’est entre cette distance et la distance sociale que vous vous trouvez lorsque vous accostez un inconnu. Attention donc à ne pas trop vous rapprocher sous peine d’empiéter sur le territoire suivant, uniquement réservé aux intimes.

Enfin, vient la distance intime : du contact physique à 0,5 mètre. Les deux interlocuteurs sont souvent très complices. Nous pouvons parler normalement ou aller jusqu’à chuchoter pour être à l’abri d’oreilles extérieures. Les conversations sont plus faciles et spontanées.

Essayez désormais de visualiser ces distances à chaque contact. C’est en quelque sorte  un moyen de vérification pour connaître le jugement que vous porte l’autre. Eloignez-vous d’elle et remarquez si elle se rapproche ou, au contraire, en profite pour s’éloigner aussi. Vous souvenez-vous du malaise que vous aviez eu lorsqu'un lourdeau était venu vous chuchoter quelques mots à vos oreilles ?

Il faut toujours respecter ces différents territoires, être à l'écoute des réactions et évoluer en fonction. 

Balancez une blague ou complimentez la personne, puis observez son impact: si la personne se rapproche d'elle-même et franchit une nouvelle zone, c'est gagné !

Système de pensée

Pour aller plus loin dans mon post précédent, voici quelques autres détails à savoir...

A chaque individu, son fonctionnement  !

Vous savez tous que nous sommes dotés de 5 sens : la vue, l'ouïe, l'odorat, le toucher et le goût.
Chacun d'entre nous développe, au cours de sa vie, ces 5 sens.
Parait-il que durant notre enfance, notre organisme applique des priorités et favorise en particulier le développement d'un sens, plutôt que les autres.

D'après les études qui ont été menées, la PNL définit trois catégories que la plupart des gens utilise le plus souvent : le visuel, l’auditif et le kinesthésique (toucher).

  • 1ère catégorie : l’individu dit « visuel » (la grande majorité des gens).
Il est caractérisé par une posture un peu raide. Ses gestes sont souvent dirigés vers le haut (image). Il a un rythme respiratoire assez rapide et infime. Il aura tendance à avoir une voix aiguë car il n’y a attaché que peu d’importance durant son développement. Son rythme vocal est rapide et saccadé. De plus, vous le reconnaîtrez facilement en l’emploi de mots visuels, ou en rapport avec la vue (« je vois »).

  • 2ème catégorie : l’individu dit « auditif ».
Il est plutôt du genre à avoir une posture décontractée et a une position d’écoute « téléphone ». Sa voix est bien timbrée avec un rythme moyen. Sa respiration est assez ample. Il prend son temps et emploie des mots auditifs (« j’entends »).

  • La 3ème catégorie : l’individu dit « kinesthésique ».
Sa posture est très décontractée. Il parle beaucoup en effectuant des gestes qui miment les mots. De nature calme, il a une respiration ample et profonde. Son timbre de voix est assez grave avec un rythme lent et de nombreuses pauses pour réfléchir (ressentir) avant de donner les mots. Il emploie des mots du domaine du kinesthésique, de la sensation (« je sens »).

Ces précisions ajoutées à l’attention portée au regard (cf. post interprétation du mouvement des yeux), essayez de détecter le type de personne qui se trouve en face de vous et apprenez à deviner le plus rapidement possible à quel "sens" elle se rattache. Une fois ce sens repéré, imprégnez-vous en pour appliquer la technique de synchronisation (cf. post précédent sur le mimétisme). Vous pourrez alors recopier jusqu'à son système de pensée.

De cette façon, vous ouvrez une communication plus profonde, vers son être lui-même et non plus vers l’individu (bas niveau). C’est invisible et inconscient.

Synchronisation et mimétisme

Observons maintenant une autre technique de la PNL qui a pour objectif d’attirer la sympathie de l’autre, à son insu. Initialement, les thérapeutes en PNL utilisent ce procédé dans le but d’aider à mettre en confiance leur patient. Cette technique s’appelle le mimétisme.

En quoi consiste le mimétisme ? Pour faire simple, je vais prendre un exemple.

Si vous entrez dans un bar ou un café, observez autour de vous toutes les personnes qui discutent dans leur coin. De même sexes ou différents, cela n’a pas d’importance.

Remarquez comme ces gens se ressemblent, dans leur posture, leurs gestes. Un miroir pourrait presque être placé entre eux. Ils sont le reflet l’un de l’autre.

La PNL explique que nous sommes tous inconsciemment attirés par ce qui nous ressemble (ou du moins, en affinité). A chaque fois que nous sommes avec des amis, nous sommes « en phase »  avec eux, que ce soit de la posture, des gestes, de l’intonation de la voix et même du vocabulaire. Nous nous recopions sans arrêt mutuellement et c’est ce qui fait que nous éprouvons un attachement particulier.

Vous l’aurez donc compris, le principe est de recopier, sans évidemment singer, la posture et les gestes de la personne. De reprendre la même intonation de voix qu’elle, et si possible, d’utiliser le même vocabulaire. Au bout d’un certain temps, vous allez remarquer que la communication se fait de plus en plus facilement : c’est que vous vous êtes "synchronisés".

Pour vous donner un autre exemple (fictif), voici la situation de Brice et Céline :

Brice invite Céline à aller boire un verre dans un café. Tous deux sont assis face à face.
Pendant que la discussion démarre, Brice observe avec attention tous les mouvements et changements de posture de Céline. Sans se faire remarquer, il l’imite dans tous ses faits et gestes. Céline se rapproche de la table, il se rapproche. Quand elle s’éloigne ou regarde ailleurs, il fait de même. Il copie également l’intonation de sa voix. Lorsqu’elle s’approche de lui pour lui chuchoter deux mots, il s’approche bien évidemment pour l’écouter, mais aussi lui répondre en chuchotant.

Quelques dizaines de minutes plus tard, la conversation s’est installée admirablement bien et une réelle complicité s’est mise en place. Ces deux personnes se sont synchronisées.

Pour le jeu, Brice décide d’arrêter toute imitation. Il prend cette fois-ci une posture différente de celle de Céline. Quelle sera la prochaine réaction ? Céline va parfaitement suivre le mouvement de Brice, et absolument inconsciemment. C’est désormais elle à son tour, qui l’imite, aveuglément.


Interprétation des mouvements des yeux

Notre savoir actuel concernant le fonctionnement du cerveau a énormément évolué depuis quelques années.

De ce fait, nous avons découvert, et vous devez déjà le savoir, que notre cerveau est divisé en deux parties, travaillant de manière inversée. L’hémisphère gauche du cortex contrôlant la partie droite de notre corps, et évidemment, l’hémisphère droit de notre cerveau contrôlant la partie gauche de notre corps.

Des recherches ont permis de pousser nos connaissances un peu plus loin :
  • La partie gauche du cerveau est le siège de tout ce qui traite de la logique, du langage, du rationnel, la linéarité, l’analyse. Considérons cette partie gauche, comme le côté scientifique de notre cerveau. 
  • La partie droite intervient pour tout ce qui concerne l’imagination, l’invention, les formes et les couleurs, les émotions, la sensualité, la sexualité, certains chercheurs prétendraient même qu’il y aurait une place pour l’intuition. La partie droite sera vue plutôt comme le côté émotionnel.

En quoi ces informations sont-elles utiles ?

Simplement, car tous les jours, chacun d’entre nous effectue des centaines de gestes inconscients traduisant nos pensées et réactions. Le but est ici d’apprendre à les remarquer et les décoder, afin d’en tirer profit pour arriver à nos fins ou au contraire, de s'en protéger.

D’après les PNListes, la lecture et l’utilisation de la mémoire s’effectuent de manière linéaire, notamment de gauche à droite. Je ne sais pas si ce phénomène est lié à l’éducation que nous avons reçue très tôt de la méthode de lecture (dans notre pays, nous lisons de gauche à droite).

Posez une question à une personne, remarquez le changement soudain de son regard. Ses yeux vont dévoiler sa méthode de réflexion. En effet, pendant un court laps de temps, leurs orientations vont changer ; elle ne vous « regarde » plus, mais est plongée en elle-même pour trouver une réponse à votre question.

Sachez que la position de ses yeux va être déterminante de la pertinence de sa réponse.

Si la personne interrogée regarde vaguement vers le haut, c’est qu’elle recherche une image construite. Horizontalement, ce sont les sonorités. Vers le bas, ce sont les sensations et raisonnements intérieurs (elle se parle à elle-même). De plus, un mouvement des yeux dirigé vers la gauche appelle aux souvenirs, tandis qu'un regard porté sur la droite appelle à l'invention/fabrication.

Pour résumer, un petit schéma :

Position du regard et interprétation

Certains conférenciers initiés à la PNL vont jusqu'à adapter leur gestuelle (et/ou présentation) pour guider les yeux dans les directions qu'ils souhaitent afin d'amener les participants à penser d'une certaine manière en activant les fonctions voulues.

La PNL explique que cela fonctionne logiquement avec tout le monde (à l'exception de certaines personnes, pour qui, tout serait inversé). Néanmoins certains praticiens déconseillent de s'y fier avec exactitude.

Interrogeant une personne prise au dépourvue, un mensonge devrait logiquement porter son regard vers l'invention plutôt que les souvenirs. Mais attention, un mensonge préfabriqué (cad déjà pensé à l'avance) appellera, telle une réalité passée, aux souvenirs...
Alors finalement, mensonge ou pas mensonge ?

Tentez l'expérience avec vos proches et partagez votre expérience.