Attention ! Etes vous sûrs d'avoir une bonne assurance emprunteur pour votre crédit immobilier ?


jan 11

Recherche et vérification automatique de proxy anonyme

Tag: Coding, Hacking, Non classé, SEOnoreply @ 11:36

Une lettre en principe n’est en vérité valable que dans la minute où elle a été écrite.

…disait je ne sais plus qui (Sacha Guitry ?). Et bien pour les listes de proxies, c’est pareil ! Encore plus lorsqu’il s’agit de proxies anonymes. Les Black Hat SEO le savent bien : trouver et maintenir des listes de proxies anonymes relève du casse-tête et on est souvent tenté de recourir à des solutions payantes pour pouvoir spammer en paix.

Et bien cette époque est révolue ! Dans un soucis de justice sociale, j’ai décidé de mettre à la disposition de tous un petit script PHP qui s’occupera de façon totalement automatisée de découvrir, tester et maintenir à jour votre liste de proxies anonymes. Le pourrissement des commentaires de blogs est donc désormais à la portée de tous !

Le fonctionnement du script

A noter pour la suite, que j’ai décidé dans ce script de réduire la reconnaissance d’un proxy à des adresses IP de la forme A.B.C.D:PORT mais qu’il est possible en modifiant légèrement le script d’accepter aussi des adresses de la forme hotname:port. J’ai pensé que les adresses IP étaient plus faciles à identifier et à extraire des pages web et qu’il y avait suffisamment d’adresses IP de proxy répertoriées sur le net pour que cela ne soit pas gênant à l’éxécution du script.

Par ailleurs, je précise que le script se restreint à la recherche de proxies HTTP anonymes, mais pas « Elite ». J’entends par « Elite » les proxies qui bloquent aussi les cookies par exemple (cela peut être embettant car on a souvent besoin de passer des cookies dans notre métier). Il faudra donc veiller à bien choisir vos services de validation de proxy pour qu’ils affichent également un dump des cookies sinon ils seront écartés au lancement du script.

Le principe du script est assez simple : à chaque appel, le script va suivre les étapes suivantes :

  1. Tout d’abord une rapide vérification des services de validation de proxy utilisés. Il faut vérifier qu’ils sont bien accessibles, et qu’ils répondent aux exigences de base : affichage de l’adresse IP du client ainsi que des cookies passés lors de la requête. Si aucun service de validation n’est trouvé, le script s’arrête.
  2. La génération de la liste peut débuter. L’exécution du script sera interrompue dès que le quota de proxies sera atteint. Les étapes suivantes ne seront donc pas forcément toutes accomplies !
  3. On commence par vérifier la dernière liste de proxies générée. Ceux qui ne fonctionnent plus sont écartés.
  4. On vérifie ensuite une par une les listes considérées comme « bonnes » (un ratio « Nombre de proxies valides / Nombre total de proxies » supérieur ou égal au paramètre SCORE_MIN qu’on aura choisi). Les meilleures listes sont bien sûr vérifiées en premier.
  5. A ce stade, le script va essayer de découvrir de nouvelles listes. Une recherche parmi celles définies dans le tableau $proxy_discover est choisie au hasard. On récupère les résultats et on les teste afin de voir si dans le tas il y a quelques bons proxies ! Les pages sur lesquelles on a effectivement trouvé des proxies valides sont ajoutées à la base de connaissances du script et pourront être utilisées plus tard.
  6. On continue avec la vérification des listes moins bonnes, celles dont le score est inférieur au paramètre SCORE_MIN. Toujours en commençant par les meilleures listes. Les listes qui pour la deuxième fois consécutive obtiennent un score inférieur ou égal au paramètre SCORE_DEL sont supprimées de la base de connaissances, parce que dans la vraie vie il n’y a pas de place pour les loosers.
  7. Si à ce point, on a toujours pas obtenu suffisamment de proxies, c’est terminé quand même. Je peux pas non plus faire des miracles hein !

J’ai essayé de penser le script pour qu’il soit le plus rapide possible. Par exemple en ne vérifiant jamais deux fois le même proxy durant la session, et en utilisant les connexions simultanées de curl pour tester les proxies 10 par 10 (ce paramètre peut d’ailleurs être changé, il correspond à la constante MAX_CURL_CONNECTIONS). Dans une liste, les proxies trouvés sont testés dans un ordre aléatoire afin d’essayer de maintenir un scoring cohérent pour la liste si le nombre de proxies à générer a été atteint et qu’on interrompt alors la vérification de la liste avant son terme.

Autre chose : si pour une adresse IP, il existe plusieurs proxies configurés sur différents ports, je n’en garde qu’un afin de garantir que chaque proxy de la liste finale correspond bien à une adresse IP différente !

Enfin, il est possible d’ajouter « à la main » un proxy ou une URL listant des proxies. Pour cela, rien de plus simple : vous devez éditer le fichier défini dans NEW_PROXY_FILENAME (par defaut il s’appelle add-proxy-or-url-here.txt) en indiquant un proxy ou une URL par ligne. Par exemple :

88.88.88.88:8888

http://example.dtd/proxy-list/

Etc. Le fichier sera lu au prochain lancement du script qui ajoutera les listes pour une vérification en priorité. Je vous recommande d’ajouter plutôt des listes que des adresses IP de proxy isolées, car dans ce cas elles seront testées une par une et vous ne bénéficierez pas des avantages du curl_multi_exec() !

Avec le temps, vous devriez obtenir une liste de proxies avec un excellent taux de réussite (de l’ordre de 80% de proxies encore valides une heure après la génération de la liste) et la regénération de la liste devrait se faire de plus en plus rapidement (personnellement cela prend moins de 5 minutes par heure pour une centaine de proxies).

Installation et configuration du script

Le script est conçu pour tourner en ligne de commande avec PHP 5 sur un serveur Debian. Point barre. Pour les autres configs ou mode d’exécution je vous invite à vous demmerder. Si cela peut vous aider, dites-vous que c’est forcément possible.

L’idéal est de l’ajouter dans votre crontab pour qu’il se lance une fois par heure :

0 * * * * cd /EMPLACEMENT-DU-SCRIPT; php NOM-DU-SCRIPT > NOM-DU-FICHIER-DE-LOG

Le script doit avoir les droits en écriture dans le répertoire dans lequel il tourne, à priori si vous le lancer depuis la crontab en votre nom, ça ne pose pas de soucis.

Il y a quelques constantes à définir si vous le souhaitez :

  • MY_IP : l’adresse IP de la machine sur laquelle le script s’exécute.
  • PROXY_LIST_LENGTH : le nombre de proxies à générer à chaque lancement.
  • MAX_CURL_CONNECTIONS : le nombre de connexions simultanées à utiliser pour vérifier les proxies.
  • PROXY_LIST_FILENAME : le nom du fichier dans lequel la liste de proxies sera écrite.
  • NEW_PROXY_FILENAME : le nom du fichier à éditer pour ajouter manuellement des proxies ou des URL.
  • CONFIG_FILENAME : le nom du fichier utilisé par le script pour sauvegarder sa base de connaissances. (vous pouvez le laisser tel quel).
  • MY_USERAGENT : le browser à simuler lors de toutes les requêtes (celui par défaut est passe-partout, inutile de le changer).
  • CHECK_TIMEOUT : le nombre de secondes maximum à attendre lors de toutes les connexions.
  • SCORE_MIN : entre 0 et 1, 0.2 signifiant 20% de proxies validés (un sur 5 fonctionne quoi).
  • SCORE_DEL : entre 0 et 1 également.
  • DEBUG : true ou false pour activer ou non l’écriture sur la sortie standard des logs de fonctionnement…
  • PROXY_REGEX : l’expression régulière utilisée pour détecter les adresses IP de proxy dans les pages scrappées.

$proxy_checker et $proxy_discover :

Ces deux tableaux sont très importants et pour le coup c’est à vous de vous démmerder pour les remplir de la meilleure façon possible (je ne vais pas non plus filer tous mes secrets hein).

Dans $proxy_checker, je vous invite à lister un maximum d’adresses internet de pages proposant des tests d’anonymat ou de proxy. Il faut en trouver de bonnes, qui affichent également les cookies, ce qui est plus délicat. Il est préférable d’avoir plusieurs services listés pour éviter de taper trop fort sur un seul service lors des validations, et aussi pour éviter de se retrouver avec des listes marquées à 0 en cas de panne du checker en cours de route ! Vous pouvez également faire vous même ce genre de page, c’est très simple ! Par exemple une ligne de PHP suffit pour avoir un checker qui répond aux exigences de ce script :

<?php print_r($_SERVER); print_r($_COOKIE); ?>

Concernant $proxy_discover, ce tableau est la clef du succès pour un bon découvreur de proxies ! A vous donc de trouver les meilleures termes et moteurs de recherche. Ceci fait, pour chaque URL vous devrez vous coder une petite expression régulière qui permettra au script d’extraire les URL des résultats avec la fonction preg_match_all. N’oubliez pas de mettre la partie de votre regex concernant l’URL entre parenthèses. Pour plus de détails vous pouvez étudiez l’exemple fourni dans le code…

Code source

N’oubliez pas de renseigner correctement l’adresse IP de la machine hébergeant le script dans la constante MY_IP.

Je vous invite aussi vivement à ajouter d’autres URL de vérification dans le tableau $proxy_checker, et à changer l’URL de recherche donnée à titre d’exemple dans le tableau $proxy_discover si vous voulez que la découverte de nouveaux proxies soit efficace !

<?php
// begin config
define(‘MY_IP’,‘— IP ADDRESS OF THE SERVER HERE —’);
define(‘PROXY_LIST_LENGTH’,100);
define(‘MAX_CURL_CONNECTIONS’,10);
define(‘PROXY_LIST_FILENAME’,‘valid-proxy.txt’);
define(‘NEW_PROXY_FILENAME’,‘add-proxy-or-url-here.txt’);
define(‘CONFIG_FILENAME’,sprintf(‘%s.config.inc’,basename(__FILE__)));
define(‘MY_USERAGENT’,‘Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)’);
define(‘CHECK_TIMEOUT’,15);
define(‘SCORE_MIN’,0.2);
define(‘SCORE_DEL’,0.015);
// end config

define(‘DEBUG’,true);
define(‘PROXY_REGEX’,‘#([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)#s’);

// proxy checker : add a list of URL here
$proxy_checker = array(
        ‘http://www.lagado.com/proxy-test’
);

// proxy discover : associative array where the key is a search URL and the value a regular expression to use in preg_match_all() to extract the results
$proxy_discover = array(
        ‘http://www.google.com/search?q=anonymous%20proxy’=>‘#<a href="(https?://[^"]+)" class=l#s’
);

$context = stream_context_create(array(
        ‘http’=>array(
                ‘method’=>‘GET’,
                ‘user_agent’=>MY_USERAGENT,
                ‘timeout’=>20
        ),
        ‘https’=>array(
                ‘method’=>‘GET’,
                ‘user_agent’=>MY_USERAGENT,
                ‘timeout’=>20
        )
));

// exit if http
if (array_key_exists(‘HTTP_HOST’,$_SERVER)) die(‘Not allowed !’);

// kill other running processes
$ret = trim(shell_exec(sprintf(‘pgrep -fx "php %s" | grep -v "^%s$"’,preg_quote(basename(__FILE__)),getmypid())));
if ($ret) {
        $ret = preg_split(‘#\n#s’,$ret);
        debug(sprintf(‘killing %s frozzen process(es)’,count($ret)));
        foreach ($ret as $pid) shell_exec("kill $pid");
}

set_time_limit(0);

// test proxy checkers
$ch = array();
$mh = curl_multi_init();
$test_cookie = sha1(uniqid());

foreach ($proxy_checker as $url) {
        $ch[$url] = curl_init($url);
        curl_setopt($ch[$url],CURLOPT_RETURNTRANSFER,true);
        curl_setopt($ch[$url],CURLOPT_USERAGENT,MY_USERAGENT);
        curl_setopt($ch[$url],CURLOPT_TIMEOUT,CHECK_TIMEOUT);
        curl_setopt($ch[$url],CURLOPT_COOKIE,sprintf(‘test=%s’,sha1($test_cookie.$url)));
        curl_multi_add_handle($mh,$ch[$url]);
}

do {
        curl_multi_exec($mh,$running);
} while ($running > 0);

$proxy_checker = array();

foreach ($ch as $k=>$v) {
        $content = curl_multi_getcontent($v);
        if ($content) {
                if ((strpos($content,MY_IP) !== false) && (strpos($content,sha1($test_cookie.$k)) !== false)) {
                        $proxy_checker[] = $k;
                }
        }
        curl_multi_remove_handle($mh,$v);
}
curl_multi_close($mh);

// exit if no proxy checker
if (!@count($proxy_checker)) {
        debug(‘no proxy checker available’);
        exit;
}

debug(sprintf(‘using %s proxy checkers’,count($proxy_checker)));

// functions

if (!function_exists(‘file_put_contents’)) {
        function file_put_contents($name,$content) {
                $f = fopen($name,‘w’);
                fputs($f,$content);
                fclose($f);
        }
}

function update_proxy_list($key,$value) {
        global $proxy_lists;
        $proxy_lists[$key] = $value;
        debug(sprintf(‘updating score for proxy %s (%s)’,$key,$value));
        @file_put_contents(CONFIG_FILENAME,serialize($proxy_lists));
}

function remove_proxy_list($key) {
        global $proxy_lists;
        debug(‘removing proxy ‘ . $key);
        unset($proxy_lists[$key]);
        @file_put_contents(CONFIG_FILENAME,serialize($proxy_lists));
}

function get_proxies($v) {
        global $context,$done;
        if (preg_match(‘#^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$#s’,$v)) {
                $r = array($v);
        }
        else {
                $data = @file_get_contents($v,false,$context);
                preg_match_all(PROXY_REGEX,$data,$m);
                $r = array();
                foreach ($m[1] as $proxy) $r[$proxy] = 1;
                $r = array_keys($r);
                shuffle($r);
        }
        return $r;
}

function check_proxy($list) {
        global $proxy_checker,$results,$done;
        $test_cookie = sha1(uniqid());
        $ch = array();
        shuffle($proxy_checker);
        $n = $n_ok = 0;
        foreach ($list as $proxy) {
                if (count($results) >= PROXY_LIST_LENGTH) break;
                list($ip,$port) = explode(‘:’,$proxy,2);
                if (array_key_exists($proxy,$done)) {
                        $n++;
                        $n_ok += $done[$proxy];
                }
                elseif (!array_key_exists($ip,$done)) {
                        $url = array_shift($proxy_checker);
                        array_push($proxy_checker,$url);
                        $ch[$proxy] = curl_init($url);
                        curl_setopt($ch[$proxy],CURLOPT_RETURNTRANSFER,true);
                        curl_setopt($ch[$proxy],CURLOPT_USERAGENT,MY_USERAGENT);
                        curl_setopt($ch[$proxy],CURLOPT_PROXY,$proxy);
                        curl_setopt($ch[$proxy],CURLOPT_TIMEOUT,CHECK_TIMEOUT);
                        curl_setopt($ch[$proxy],CURLOPT_COOKIE,sprintf(‘test=%s’,sha1($test_cookie.$proxy)));
                       
                        if (count($ch) == MAX_CURL_CONNECTIONS) {
                                multi_check($ch,$n,$n_ok,$test_cookie);
                                $ch = array();
                                shuffle($proxy_checker);
                        }
                }
        }
       
        if (count($ch)) {
                multi_check($ch,$n,$n_ok,$test_cookie);
        }
       
        return $n != 0 ? $n_ok / $n : 0;
}

function multi_check(&$ch,&$n,&$n_ok,$test_cookie) {
        global $results,$done;
        $mh = curl_multi_init();
        foreach ($ch as $k=>$v) curl_multi_add_handle($mh,$v);
       
        do {
                curl_multi_exec($mh,$running);
        } while ($running > 0);
       
        foreach ($ch as $k=>$v) {
                $n++;
                $done[$k] = 0;
                $content = curl_multi_getcontent($v);
                if ($content) {
                        if ((strpos($content,MY_IP) === false) && (strpos($content,sha1($test_cookie.$k)) !== false)) {
                                $done[$k] = 1;
                                $results[] = $k;
                                $n_ok++;
                                list($ip,$port) = explode(‘:’,$k,2);
                                $done[$ip] = 1;
                        }
                }
                curl_multi_remove_handle($mh,$v);
        }
        curl_multi_close($mh);
}

function check_done() {
        global $results;
        if (count($results)) save_results();
        if (count($results) >= PROXY_LIST_LENGTH) {
                exit;
        }
}

function save_results() {
        global $results;
        debug(sprintf(’saving %s proxies’,count($results)));
        file_put_contents(PROXY_LIST_FILENAME,implode("\n",$results));
}

function debug($s) {
        if (defined(‘DEBUG’) && DEBUG) printf("[%s] %s\n",date(‘H:i:s’),$s);
}

// retrieve last config
$proxy_lists = @unserialize(file_get_contents(CONFIG_FILENAME));
if (!$proxy_lists) {
        $proxy_lists = array();
        debug(‘no config file found’);
}

// add new proxies / lists
if (file_exists(PROXY_LIST_FILENAME)) {
        $proxy_lists[PROXY_LIST_FILENAME] = 1.2;
}
if (file_exists(NEW_PROXY_FILENAME)) {
        if ($lines = @file(NEW_PROXY_FILENAME)) {
                debug(‘adding new proxies / lists’);
                foreach ($lines as $line) {
                        $line = trim($line);
                        if (!array_key_exists($line,$proxy_lists)) $proxy_lists[$line] = 1.1;
                }
        }
        file_put_contents(CONFIG_FILENAME,serialize($proxy_lists));
        @unlink(NEW_PROXY_FILENAME);
}
touch(NEW_PROXY_FILENAME);

// generate working proxy lists
$good_proxy_lists = array_filter($proxy_lists,create_function(‘$x’,‘return $x >= ‘ . SCORE_MIN . ‘ ? true : false;’));
$bad_proxy_lists = array_filter($proxy_lists,create_function(‘$x’,‘return $x >= ‘ . SCORE_MIN . ‘ ? false : true;’));
arsort($good_proxy_lists);
arsort($bad_proxy_lists);
$results = array();
$done = array();

debug(‘good proxy lists: ‘ . count($good_proxy_lists));
debug(‘bad proxy lists: ‘ . count($bad_proxy_lists));

// checking good proxy lists
debug(‘checking good proxy lists’);
foreach ($good_proxy_lists as $k=>$v) {
        $proxies = get_proxies($k);
        $score = check_proxy($proxies);
        update_proxy_list($k,$score);
        if ($score) check_done();
}

// searching for new proxy lists (using one random query)
debug(’searching new proxy lists’);
$proxy_discover_keys = array_keys($proxy_discover);
shuffle($proxy_discover_keys);

foreach ($proxy_discover_keys as $url) {
        if (preg_match_all($proxy_discover[$url],@file_get_contents($url,false,$context),$m)) {
                foreach ($m[1] as $new_list) if (!array_key_exists($new_list,$proxy_lists)) {
                        $proxies = get_proxies($new_list);
                        $score = check_proxy($proxies);
                        if ($score) {
                                update_proxy_list($new_list,$score);
                                check_done();
                        }
                }
                break;
        }
        else {
                debug(sprintf(‘no results found at %s’,$url));
        }
}

// checking bad proxy lists
debug(‘checking bad proxy lists’);
foreach ($bad_proxy_lists as $k=>$v) {
        $proxies = get_proxies($k);
        $score = check_proxy($proxies);
        if (max($v,$score) <= SCORE_DEL) remove_proxy_list($k);
        else update_proxy_list($k,$score);
        if ($score) check_done();
}
?>

2 Responses à “Recherche et vérification automatique de proxy anonyme”

  1. Olivier dit :

    C’est du très bon boulot ! Merci.

  2. noreply dit :

    Merci :)
    J’ai la flemme d’éditer le billet mais en fait je me suis aperçu que pour N proxies on obtient pas forcement N ips différentes, même écartant les proxies qui partagent la même IP :) Mais bon, c’est assez marginal et pas trop gênant. Et dans tous les cas c’est toujours plus pratique de bosser avec la liste générée par ce script qu’avec une liste pêchée dans un forum black hat !

Poster un commentaire