Faille SQL : LoL Guru, je LIKE.

Par Maxime, le 29 mars 2010.

Aujourd’hui a été annoncé en grandes pompes (…) le nouveau site du groupe PlayWeb, dirigé de main de maître par Romain Casolari (on sent que c’est le même, vu les sites), alias Chauffeur de Buzz, j’ai nommé LoL Guru. Le nom fait déjà frémir, cependant la description vaut le détour :

Bienvenue sur mon site, je suis LoL Guru ®, né le 21 décembre 1891, je suis considéré comme le Gourou du Divertissement !!! Je blogue, mets à jour ce site depuis le début du XXe siècle pour partager avec vous les news, photos, vidéos, sites Internet, jeux et coloriages en ligne, prochaines et dernières sorties de DVD, jeux vidéo, logiciels, applications, …

Amusez-vous bien LOL

Moi quand on me propose de m’amuser, je dis jamais non.

Mais… J’ai des loisirs un peu différents.

En tant que développeur, le backoffice d’un site si énorme m’intriguait. Je l’imaginais déjà rempli d’algorithmes, une interface à la Minority Report pour taper plus vite que son ombre, de l’autocompletion en AJAX en veux-tu en voilà…

akaa.png
Paie ta déception.

Ouais bon, on repassera. Je vous reprends encore quelques minutes de votre temps, mes petits pandas, pour vous expliquer comment j’en suis arrivé là.

Il ne faut pas très longtemps, même pour un con comme moi, pour trouver l’adresse de l’administration du site. En effet, un aller simple sur http://fr.lolguru.com/admin/connexion.asp nous laissait apparaître un joli formulaire d’identification.

Ayant peu d’espoir mais ne pouvant repartir sans avoir testé, je me lance et écris une apostrophe comme identifiant, ainsi qu’en mot de passe. Je m’attendais à me faire rejeter bien comme il faut, avec une alerte automatique au GIGN. Quand je vois simplement l’erreur suivante :

[Microsoft][Pilote ODBC Microsoft Access] Erreur de syntaxe dans la chaîne dans l’expression "login like ’’’ and password like ’’’".

Je n’ai donc pas pu accéder à l’administration du site. Cependant deux points m’intriguent :

  • L’utilisation de la syntaxe SQL LIKE pour l’identification autorise le wildcard %. Très dangereux pour un système d’identification.
  • Mes apostrophes n’ont en plus pas été échappées par un antislash, ce qui a provoqué l’erreur SQL. Très très dangereux…

L’idée de mettre le wildcard % comme identifiant ainsi que mot de passe rendrait donc la requête SQL suivante : login like ‘%’ and password like ‘%’, ce qui sélectionnerait donc n’importe quel identifiant stocké dans la base. Et effectivement, j’ai été identifié comme administrateur (voir capture d’écran ci-dessus).

axcv.png
Faut toujours deux preuves.

Ne cherchez plus à aller sur cette page, elle a été supprimée / renommée depuis. Encore mes talents de prestidigitateur.

Pour résumer, voici 4 erreurs à ne pas reproduire sur votre site :

  • Ne pas utiliser la syntaxe SQL LIKE en dehors de cas très particuliers (comme la recherche dans une table).
  • Ne pas afficher les erreurs SQL lorsque son site est en production (ou au moins, pas les requêtes complètes…).
  • Ne pas oublier d’échapper les données injectées dans une requêtes.
  • Eviter d’avoir un répertoire administration avec un nom évident si on n’est pas sûr de sa sécurité.

Si vous ne savez pas protéger correctement votre administration et que vous êtes sur Apache, vous pouvez tout aussi bien utiliser la méthode de la protection par .htaccess/.htpasswd.

Au-delà de cette faille, j’ai été surpris par l’austérité de ce backoffice. Rempli de cases austères tel un formulaire de l’administration pénitencière, avec des couleurs hasardeuses et des fautes d’orthographe, ça ne donne pas vraiment envie de travailler et produire du contenu.

Et vous, est-ce que l’administration de votre blog/site est agréable ? Est-ce que ça a de l’importance pour vous ?

L’infrastructure VDM : Memcache

Par Maxime, le 17 mars 2010.

La semaine dernière, je vous annonçais la création d’une suite d’articles concernant l’infrastructure de VDM. J’ai commencé en présentant mes serveurs MySQL. Récapitulatif :

betacie_network.png
L’infrastructure que j’ai montée pour VDM/FML

Dans cette série de billets (normalement trois en comptant celui là), je vais vous raconter ce qui se passe derrière les URL viedemerde.fr et fmylife.com, qui représentent 98% du trafic de l’infrastructure d’hébergement de ma société, Beta&Cie. Ces articles n’ont pas pour vocation de me vanter ou de dire au monde entier que ma solution est la meilleure, c’est juste ma solution et elle marche bien jusqu’à maintenant.

Aujourd’hui, parlons de Memcache.

Présentation de memcached

Comme nous l’avons vu la semaine dernière, les serveurs de données sont bien remplis en données diverses et variées, que ce soit des votes, des anecdotes, les informations de connexion. Beaucoup de données, dont une partie est redondante. Mais pas la totalité de la page puisque chaque anecdote est votée, impossible donc de cacher toute la page.

Un serveur memcached (d pour daemon) est un serveur léger qui permet de stocker temporairement des données en RAM. Si léger qu’il ne dispose d’aucun système d’identification pour accéder ou modifier ces données, donc faites bien attention à ne faire écouter le serveur qu’en local si vous installez un jour une telle solution pour votre serveur.

Son utilisation sur VDM

La technologie memcached permet donc de stocker n’importe quel type de données. Il sert souvent par exemple sur des clusters de serveurs Web pour centraliser les sessions PHP. On peut donc facilement en tirer profit pour mettre en cache plusieurs informations du site, et ainsi soulager de manière significative les serveurs MySQL.

memcache.png
One server to rule them all.

La problématique est maintenant la mise en place de cette solution sur le code PHP existant de VDM. PHP possède une extension Memcache qui permet d’implémenter facilement une solution de cache.

La solution que j’ai retenue est résumée dans le code suivant. Les requêtes SQL sont transformées en hash MD5 pour le nom de la clé, et l’objet du résultat de la requête est stockée en cache, avec un cache par défaut d’une heure (3600 secondes). De cette manière, si la clé n’existe pas sur le serveur memcached, la requête est automatiquement exécutée et le résultat stocké.

<?php

$memcache = new Memcache();
$memcache->pconnect("127.0.0.1","9000");

function mcache($query,$timeout=3600,$force=false) {

	global $memcache;

	$hash = md5($query);

	if (!$force) {

		$value = $memcache->get($hash);

	}

	if ($force || $value === FALSE) {

		$result = mysql_query($query);
		$value = mysql_fetch_object($result);
		$memcache->set($hash,$value,NULL,$timeout);

	}

	return $value;

}

function mcache_set($query,$value,$timeout=3600) {

	global $memcache;

	$hash = md5($query);

	$value = $memcache->set($hash,$value,NULL,$timeout);

	return $value;

}

function mcache_delete($query) {

	global $memcache;

	$hash = md5($query);
	$value = $memcache->delete($hash);

	return $value;

}

?>

Grâce à ces fonctions nous avons conservé toutes nos requêtes, et pouvons utiliser de manière transparente soit Memcache soit MySQL. Plus pratique pour pouvoir synchroniser rapidement les données entre les deux serveurs, en plus d’avoir facilité la migration vers Memcache.

Exemple d’utilisation :

<?php

/* Je suis un nul alors je mets pas de mot de passe en root */
mysql_connect("localhost","root","");
mysql_select_db("base");

/* La requête MySQL normale sans cache */
$result = mysql_query("SELECT texte FROM articles WHERE id = '1337' LIMIT 1");
$row = mysql_fetch_object($result);

/* La requête avec le cache Memcache */
$row = mcache("SELECT texte FROM articles WHERE id = '1337' LIMIT 1");

?>

Grâce aux fonctions mcache() et consorts, on peut donc sans trop de difficultés alléger le travail des serveurs de bases de données. Si vous avez besoin de mettre en cache un tableau de résultats, vous pouvez utiliser la fonction suivante :

<?php

function mcache_array($query,$timeout=3600,$force=false) {

	global $memcache;

	$hash = md5($query);

	if (!$force) {

		$value = $memcache->get($hash);

	}

	if ($force || $value === FALSE) {

		$memcache->delete($hash);

		$result = mysql_query($query);
		while ($row = mysql_fetch_object($result)) {
			$value[] = $row;
		}

		$memcache->set($hash,$value,NULL,$timeout);

	}

	if (!is_array($value)) $value = array();
	return $value;

}

?>

Ainsi, sur VDM et FML, nous mettons en cache principalement les données suivantes :

  • Les données « statiques » des anecdotes (texte, auteur, etc.)
  • Les totaux des votes sur les anecdotes (mis à jour à chaque vote sans appel à MySQL)
  • Les ID des dernières anecdotes lues par les utilisateurs, pour afficher les « NEW »
  • Certains rendus XML de l’API pour les applications mobiles

Au niveau des chiffres, voici des statistiques sur les 3 dernières semaines d’utilisation du serveur memcached :

  • 900 connexions simultanées en permanence
  • 90 000 connexions par jour
  • 14,4 Mo écrits par seconde (via 622 requêtes en moyenne)
  • 1,8 Go de données stockées en ce moment
  • 6,9 millions de données stockées en ce moment
  • 1,3 milliard de données stockées en tout

Comme d’habitude, n’hésitez pas à commenter pour plus d’informations, j’ai sûrement oublié beaucoup de choses. La semaine prochaine nous parlerons de serveurs Web.

PD.TC, le raccourcisseur du LOL

Par Maxime, le 15 mars 2010.

Peu de gens le savent, Beta&Cie faisait partie des premiers à créer un service de « raccourcisseur d’URL », en mauvais français. C’était en 2008 et il fonctionne toujours. Cependant aujourd’hui, 7 caractères c’est trop long. Il a fallu en trouver un autre.

2hgh.png

Laissez-moi vous présenter PD.TC, le raccourcisseur d’URL le plus LOL. Grâce à son acronyme équivoque, vos soirées Twitter n’en seront que plus gaies. Ô la joie de balancer à son prochain « C’est où que t’as trouvé machin ? PDTC ». Oui, c’est aujourd’hui à votre portée.

Mais pour bien l’utiliser au quotidien, quelle idée d’utiliser un simple formulaire ! Toi la star de Twitter, tu as besoin de l’intégrer directement dans les outils que tu utilises quotidiennement.

Voici trois tutoriaux qui couvriront toutes tes attentes pour utiliser PD.TC dans les meilleures conditions possibles, et ainsi diffuser l’esprit du LOL partout autour de toi.

Utiliser PD.TC avec n’importe quel client Twitter

Pour pouvoir utiliser mon service sur la plupart des clients Twitter Mac/Windows/Linux, mes petits pandas, il va falloir utiliser une méthode de sioux.

Sous Mac/Linux : Ouvrez une fenêtre de Terminal pour modifier le fichier hosts en tapant ces deux lignes :

echo '91.191.146.209 tinyurl.com' | sudo tee -a /etc/hosts

Sous Windows : Ajoutez la ligne 91.191.146.209 tinyurl.com dans votre fichier hosts, dont l’emplacement varie selon les versions. En général dans C:\Windows\system32\drivers\etc\hosts.

De cette manière, toutes les URL TinyURL passeront par mes serveurs qui simuleront son comportement. En contrepartie vous ne pourrez plus raccourcir d’URL sur TinyURL (en même temps le site est tout moche, c’est pas une perte), mais les URL raccourcies de TinyURL marcheront toujours !

bq6d.png

Modifiez ensuite dans votre client Twitter votre URL Shortening Service en indiquant TinyURL, et vous pourrez utiliser PD.TC facilement et rapidement.

q8vr.png

Je pense que la technique marche à peu près avec n’importe quel client Twitter, à partir du moment que vous modifiez le fichier hosts et que vous pouvez sélectionner TinyURL dans votre client, tout roule.

Utiliser PD.TC avec Tweetie iPhone

Tweetie 2 pour iPhone intègre nativement la possibilité d’avoir son propre raccourcisseur d’URL. Il suffit donc d’aller dans les réglages « URL shortening » de Tweetie :

IMG_0620.PNG
Settings > Services > URL Shortening > Custom…

Et entrez l’URL suivante pour utiliser PD.TC automatiquement :

http://pd.tc/?api=1&url=%@

Utiliser PD.TC avec PHP

Vous pouvez utiliser PD.TC dans tous vos scripts et applications. Voici un simple exemple en PHP, que vous pourrez adapter à à peu près tous les langages de programmation existants :

<?php

/* Appel de pd.tc avec l'URL qu'on veut raccourcir */

$url = "http://www.google.fr";
$pdtc = file_get_contents("http://pd.tc/?api=1&url=".urlencode($url));

/* Retournera : string(17) "http://pd.tc/0236" */

var_dump($pdtc);

?>

A noter que j’enregistre tous les accès aux URL, donc si le service est utilisé je ferai peut-être des classements & consorts, mais pour le moment c’est surtout pour le fun du nom de domaine que j’ai créé ce service, et ça restera pour le fun :)

L’infrastructure VDM : MySQL

Par Maxime, le 10 mars 2010.

Depuis des mois je promets une série d’articles qui ne va peut-être pas intéresser grand monde, mais qui aura le mérite d’exister et de soulager ma conscience (ma mère m’a dit que ce n’était pas joli de ne pas tenir ses promesses).

Contrairement à ce que certains laissent penser, je fais quelque chose de mes journées. Je suis développeur Web, mais aussi administrateur système. Créer puis s’occuper de l’hébergement de sites comme VDM et FML n’arrive pas tous les jours, et j’ai gagné beaucoup d’expérience ces 12 derniers mois en montant une infrastructure qui accueille chaque jour plus de 3 millions de visiteurs.

betacie_network.png
L’infrastructure que j’ai montée pour VDM/FML

Dans cette série de billets (normalement trois en comptant celui là), je vais vous raconter ce qui se passe derrière les URL viedemerde.fr et fmylife.com, qui représentent 98% du trafic de l’infrastructure d’hébergement de ma société, Beta&Cie. Ces articles n’ont pas pour vocation de me vanter ou de dire au monde entier que ma solution est la meilleure, c’est juste ma solution et elle marche bien jusqu’à maintenant.

Passons au premier sujet : Les serveurs MySQL.

Réplication circulaire

tv8n.png
Mes sept petits bouts de chou.

Comme le montre le schéma, mes sept serveurs sont configurés en réplication circulaire. C’est à dire que chacun est maître et esclave à la fois (pas de connotations sexuelles ici, merci !). s1 est donc maître sur s2, mais il est esclave de s7.

Cette configuration a l’avantage de bien fonctionner derrière un load balancer puisque chaque serveur a le droit d’écriture. C’est donc transparent pour le développeur qui n’a qu’à renseigner l’IP du load balancer dans sa connexion à MySQL et travailler comme s’il n’avait qu’un seul serveur.

L’inconvénient est un inconvénient de taille. Comme les requêtes s’exécutent comme dans une ronde, si un serveur plante, toute la ronde est cassée et plus rien ne se réplique. Il faut donc avoir du bon matériel, et au cas où ça arrive quand même, agir très vite pour éviter que les utilisateurs ne râlent :)

Une petite astuce pour éviter que votre réplication ne s’arrête pour des raisons connes : Je fais ignorer par mes serveurs systématiquement les erreurs 1062 (nouvelle entrée avec le même ID) et 1053 (déclenchée quand le serveur maître s’éteint ou redémarre) :

slave-skip-errors=1062,1053

Pour diminuer les latences de réplication il est aussi important d’avoir un bon réseau…

Réseau VLAN

Au delà de l’onomatopée, un VLAN est l’abréviation de Réseau Local Virtuel (RLV ça le faisait pas, hein ?). Il permet de créer un réseau indépendant du reste des machines du réseau où est hébergée l’infrastructure. Concrètement chaque serveur possède deux interfaces physiques :

# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:30:48:b9:42:e2
          inet addr:91.191.146.199  Bcast:91.191.146.255  Mask:255.255.255.192

eth1      Link encap:Ethernet  HWaddr 00:30:48:b9:42:e3
          inet addr:10.191.146.199  Bcast:10.191.146.255  Mask:255.255.255.0

eth0 a l’adresse IP 91.191.146.199 qui est atteignable de l’extérieur du réseau, alors que eth1 a l’adresse IP 10.191.146.199 qui est interne au VLAN et qui par conséquent ne peut communiquer qu’avec ses copines du même VLAN qu’elle.

Avoir ses serveurs en VLAN diminue donc la latence entre les serveurs, la réplication se fait plus rapidement et avec plus de sécurité puisqu’aucun paquet ne sort de votre réseau local. Deux avantages indéniables qui devraient vous faire quitter OVH choisir un hébergeur qui fait du VLAN ! En effet les réseaux virtuels se configurent au niveau des routeurs/switches, il faut donc choisir un hébergeur qui puisse le prendre en charge.

Disques SSD

Pour économiser un certain nombre de machines dans ma réplication circulaire et ainsi réduire le nombre de défaillances matérielles, j’ai très vite décidé d’utiliser du SSD pour héberger les données de mes bases. Au début j’ai eu des X25-M chez OVH (mon ancien hébergeur), qui est le moyen de gamme du SSD, puis je suis passé récemment au X25-E, la version haut de gamme, comme le montre ce benchmark par SD-France, mon gentil hébergeur :

20090824-test-ssd-graph-ecriture.gif

Le X25-E dépasse largement tous ses concurrents en terme d’écriture et de lecture de disque. Cela assure évidemment un meilleur temps de réponse sur les requêtes, différence qui se ressent lorsque l’on traite 15 000 requêtes par seconde

Les deux inconvénients de cette méthode :

  • La capacité. La plus grande taille disponible est 64 Go, ce qui peut paraître beaucoup pour de simples bases de données, mais qui se remplit vite quand le site repose sur beaucoup d’anecdotes / commentaires et surtout votes.
  • Le prix. Les disques SSD X25-E sont encore très chers, même s’ils économisent parfois de nouvelles machines, je me fais taper quand j’en demande des nouveaux :D

Optimisations

Comme le disque SSD est déjà pas mal occupé à traiter les requêtes SQL, la copie des résultats de SELECT sur les bases temporaires dans /tmp (configuration par défaut) est fortement ralentie. En parallèle la RAM n’est en général pas utilisée à fond. Il est donc judicieux d’utiliser la RAM restante comme espace pour les fichiers temporaires, grâce au système de fichier tmpfs :

mkdir /tmpfs
mount tmpfs /tmpfs -t tmpfs

Vérifiez que votre nouveau répertoire fonctionne correctement, puis modifiez la configuration de votre my.cnf pour la valeur suivante :

tmpdir = /tmpfs

Ne pas oublier de rajouter le montage du tmpfs dans votre /etc/fstab, pour éviter les problèmes au reboot :)

Avec tout ceci, nous arrivons à traiter vaguement ces quelques données :

  • 2 millions d’anecdotes, 8000 nouvelles par jour
  • 1,7 million de commentaires, 5000 nouveaux par jour
  • 630 millions de votes, 850 000 nouveaux par jour

Voilà mes petits pandas, je pense avoir couvert pas mal des aspects principaux « caractéristiques » de l’infrastructure au niveau de MySQL. Mais j’ai sûrement oublié beaucoup de choses, alors n’hésitez pas si vous avez des questions.

Pas de date pour le prochain article mais j’espère la semaine prochaine.

Envoyé Spécial : La révolution Twitter

Par Maxime, le 8 mars 2010.

Si vous êtes un peu familier avec Twitter, il ne vous aura pas échappé que jeudi dernier passait le reportage le plus attendu de la communauté dans Envoyé Spécial, puisqu’il était consacré au service Web (ça vous aurait étonnés que ça soit un reportage sur l’ex-URSS).

Ce reportage, appelé La Révolution Twitter, a exposé de manière concise et sans aucune stigmatisation le phénomène et ses conséquences. Le reportage en lui-même était donc de l’avis de tous plutôt une réussite.

J’étais moi-même devant ma télévision ce soir-là et j’ai pu constater que les journalistes avaient fait un travail remarquable allant même jusqu’à San Francisco dans les locaux de Twitter pour parler monétisation du service.

Non en vérité, le seul problème, ce sont les 8 premières minutes du reportage. Celles où le journaliste fait découvrir aux gens qui sont les utilisateurs de Twitter, sa communauté. Et là, on a du mal à relever le niveau. Extraits choisis.

vlcsnap-2010-03-08-13h29m51s195.png

« Si pendant 3 heures, Twitter est éteint, J’AI RATÉ QUELQUE CHOSE. »

vlcsnap-2010-03-08-13h30m37s143.png

« J’ai tweeté « je cherche un appart », une fille qui a LE MÊME PRÉNOM QUE MOI a tweeté et j’ai pas visité l’appart j’ai directement habité avec elle ! ^^ »

vlcsnap-2010-03-08-13h30m45s2.png

« J’ai posté ça, j’ai eu UNE VINGTAINE DE RETWEETS ! C’est hyper rapide ! »

En dehors de ça les deux tiers restants du reportage sont honnêtes et bien documentés. En fin de compte le reportage prouve que Twitter est devenue la meilleure source d’information des journalistes en crise, puisqu’elle permet de récupérer gratuitement plein de contacts très (trop) bavards sans se dévoiler comme sur Facebook.

Encore un bon service Web, avec de mauvais utilisateurs, c’est un peu ça Internet.