Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Voir fautes de l'avocate

La preuve par le code source : Bing Search et Bing Ads dans l'API Qwant de décembre 2016

← Sommaire | ← Chapitre 56

Avertissement probatoire

Ce chapitre décrit les capacités du code versionné dans le dépôt API-API-TAG-PACKAGER, au tag v1.32.1 du 22 décembre 2016. Une capacité inscrite dans une version de release n'établit pas, à elle seule, son activation sur tout le trafic, le volume effectivement traité, la durée de conservation des données, ni l'exploitation qui en a été faite. Les extraits sont reproduits verbatim ; les clés d'interface et les adresses de messagerie y sont masquées, sans que cela modifie le fait constaté. Chaque pièce est accompagnée des éléments permettant à un tiers de la retrouver et de la vérifier par lui-même.

Objet : rassembler en un seul document vérifiable les preuves techniques de la dépendance de Qwant à Microsoft — moteur de recherche et régie publicitaire — dispersées jusqu'ici entre plusieurs chapitres, et publier le protocole permettant de les reproduire.

Date de la revue : 5 août 2026.

1. Réponse synthétique

Au 22 décembre 2016, le code de l'API Qwant établit neuf faits.

Ce que ce corpus établit :

  • la totalité des recherches Web part chez Microsoft, la liste des langues réservées au moteur interne étant mise en commentaire dans le code ;
  • le nombre de recherches autorisées sur l'interface Bing V5 est plafonné par le nombre d'appels publicitaires émis le même jour : recherche et publicité sont couplées dans le code lui-même ;
  • deux interfaces Microsoft distinctes coexistent (bingapis.com et api.datamarket.azure.com), la seconde servant de déversoir lorsque le quota de la première est atteint ;
  • les résultats sont conservés en cache sept jours ;
  • chaque appel publicitaire transmet à Microsoft le User-Agent réel du visiteur, son adresse IP tronquée au dernier octet et son référent HTTP ;
  • la fonction nommée anonymizeIp() ne supprime que le dernier octet, ce qui désigne un réseau d'au plus 256 hôtes ;
  • la relation publicitaire repose sur des identifiants contractuels : deux propriétés Microsoft et douze unités publicitaires réparties sur six pays ;
  • la clé d'accès à l'interface de recherche est inscrite en clair dans le dépôt, rattachée en commentaire à des comptes de messagerie nominatifs ;
  • l'épuisement du crédit du compte Microsoft déclenche un courriel intitulé « [URGENT] un des compte n'a plus de crédit ! » adressé à trois destinataires personnels, dont celui du dirigeant.

Ce que ce corpus n'établit pas :

  • le volume de trafic réellement servi par chacun des deux endpoints en production ;
  • l'existence, le contenu ou les stipulations du contrat conclu avec Microsoft ;
  • la durée de conservation effective, côté Microsoft, des données transmises ;
  • une intention de tromper le public ou les financeurs, qui relève de l'appréciation judiciaire.

Ce chapitre est une pièce de vérification. Il ne rejoue pas les analyses déjà conduites ailleurs dans le dossier : il en fournit le socle reproductible et y renvoie systématiquement.

2. Provenance et protocole d'acquisition

Les constats portent sur un clone local du dépôt API-API-TAG-PACKAGER, dépôt de l'API front de Qwant (qwant_v2), une application PHP 5 bâtie sur CodeIgniter 2.

Remotes    origin  git@git.qwant.ninja:api/qwant_v2.git
           github  git@github.com:stephaneerard/qwantv2.git
Commits    13 070 accessibles depuis le tag v1.32.1
Étendue    30 avril 2012 → 22 décembre 2016
Tags       185, de v1.13.0 à v1.32.1
Volume     1 337 fichiers au tag v1.32.1, dont 1 281 fichiers .php

Point de référence retenu pour l'ensemble du chapitre :

tag        v1.32.1  (objet 1554284e8)
commit     22aa5650889b1a77e1ef9008ea55fef30cc8916f
date       2016-12-22 12:59:33 +0000
committer  Sampson SAVINEL <s.s***@qwant.com>
sujet      Merge branch 'prelive' into 'master'

Ce tag est le même que celui retenu aux chapitres 22, 40 et 55. Les blobs de search_web.php et de search_ads.php relevés ici sont identiques à ceux publiés au chapitre 40, section 8.2 : les deux relevés, effectués à des dates différentes, concordent.

Précaution méthodologique

La branche develop du clone s'arrête au 6 juillet 2016 (a4d5a9a1d00afbb9e6ceff4cbc89be4f92991841), soit avant le tag v1.32.1. Les deux points de référence ne sont pas interchangeables : le modèle bing_error.php, présent sur develop, a été supprimé avant décembre, alors que la fonction d'alerte qu'il appelait subsiste dans Mailer.php (pièce n° 8). Aucun constat de ce chapitre ne mélange les deux référentiels.

Trois commandes suffisent à reproduire chaque ligne de l'annexe :

git -C <dépôt> rev-parse v1.32.1:<chemin>
git -C <dépôt> cat-file blob <blob> | sha256sum
git -C <dépôt> log -1 --format='%h|%cs|%cn' v1.32.1 -- <chemin>

La première donne l'identifiant de l'objet Git, la deuxième l'empreinte SHA-256 de son contenu exact, la troisième le dernier commit ayant modifié le fichier à cette version et le nom de son auteur. Sous PowerShell, la deuxième commande s'écrit git cat-file blob <blob> | Get-FileHash -Algorithm SHA256.

3. Chronologie de la dépendance

Date Commit Committer Fait
30 avril 2012 bd61943c6 JCC Import initial du dépôt. Il contient déjà des appels à api.bing.net.
31 juillet 2012 cdc4545aa Jcchemin Migration de l'API Bing 2.0 vers Azure DataMarket.
5 juin 2013 39c769e9c Jcchemin Création du modèle d'alerte « compte Bing vide ».
16 mars 2016 c10af1941 p.vignaux #API-394 bing instead of yboss : Bing passe de solution de repli à fournisseur principal.
30 mars 2016 ac6a6f049 p.vignaux Durée de cache des résultats Web et Images portée à sept jours.
15 juin 2016 c094aa279 Jonathan (j.cassar) add real user_agent and anonymized client ip for bing ads call.
17 juin 2016 b4ba79ddf p.vignaux Création de search_ads.php.
22 décembre 2016 22aa5650 Sampson SAVINEL Tag v1.32.1, point de référence du présent chapitre.

Deux enseignements. D'abord, Bing est présent dès le premier jour du dépôt : la dépendance n'est pas une dérive tardive, c'est le point de départ. Ensuite, la bascule décisive n'est pas technique mais contractuelle : en mars 2016, Bing cesse d'être un repli pour devenir la source. L'analyse détaillée de cette bascule figure au chapitre 22.

4. Pièce n° 1 — Le routage : la liste des langues réservées au moteur interne est mise en commentaire

Dans www/application/models/search/search_web.php, la propriété qui détermine quelles langues sont servies par le moteur interne « WebBrain » est déclarée ainsi, à la ligne 11 :

    /**
     * @var array
     * Authorized locales for WebBrain calls
     */
    private $webBrainLocales = [/*'fr', 'de'*/];

Les deux valeurs 'fr' et 'de' sont présentes mais neutralisées par un commentaire. Le tableau est donc vide à l'exécution. Or c'est lui qui commande l'aiguillage, lignes 51 à 55 :

    public function search($keyword, $locale, $count = 50, $offset = 0, $filters = false)
    {
        if (in_array($locale[0], $this->webBrainLocales)) {
            $type = 'webBrain';
            $version = 'W';
            $searchUrl = $this->getWebBrainUrl($keyword, $locale, $offset, $count, $filters);
        } else {

Ce code fait ceci : il teste si la langue de la requête figure dans une liste vide. Ce test ne peut jamais réussir. Toute recherche Web, quelle que soit sa langue, emprunte donc la branche else, c'est-à-dire Microsoft. Le moteur interne n'est pas absent du code — il est présent, appelable, et désactivé d'une ligne de commentaire.

L'historique de cette propriété et la question de son déploiement sont traités aux chapitres 22 et 40, ainsi que dans l'analyse forensique du dépôt.

5. Pièce n° 2 — Le quota : le nombre de recherches est plafonné par le nombre d'annonces affichées

C'est la pièce la plus significative de ce chapitre, et elle n'avait pas encore été relevée. La branche else de la fonction search(), lignes 55 à 70 :

        } else {
            // Enable Bing V5 calls until their number is equal to Ads queries count
            $adsQueryCount = intval($this->getCache(Search_ads::$bingV5CallsCountCacheKey));
            $queryCount = intval($this->getCache(static::$bingV5CallsCountCacheKey));

            if ($queryCount <= $adsQueryCount || config_item('bing_apiv5_search_web_enabled') === true) {
                $type = 'bingV5';
                $version = 'B' . ($adsQueryCount - $queryCount);
                $searchUrl = $this->getApiUrlBingV5($keyword, $locale, $offset, $count, $filters);
                $this->setCache(static::$bingV5CallsCountCacheKey, $queryCount + 1, static::$bingV5CallsCountCacheDuration);
            } else {
                $type = 'bingAzure';
                $version = 'A' . ($adsQueryCount - $queryCount);
                $this->getContainer()->getHttpService()->setBingKey(BING_KEY_WEBONLY);
                $searchUrl = $this->getApiUrlBingAzure($keyword, $locale, $offset, $filters);
            }
        }

Les deux compteurs sont des clés de cache journalières, définies dans les constructeurs respectifs des deux modèles :

// www/application/models/search/search_web.php, ligne 28
static::$bingV5CallsCountCacheKey = 'bing_v5_web_calls_count_' . date('Ymd');

// www/application/models/search/search_ads.php, ligne 57
static::$bingV5CallsCountCacheKey = 'bing_v5_ads_calls_count_' . date('Ymd');

Et le compteur publicitaire est incrémenté à chaque appel de la régie, dans search_ads.php, lignes 145 à 147 :

        // Count impressions
        $queryCount = intval($this->getCache(static::$bingV5CallsCountCacheKey));
        $this->setCache(static::$bingV5CallsCountCacheKey, $queryCount + 1, static::$bingV5CallsCountCacheDuration);

Ce code fait ceci : chaque jour, Qwant compte séparément le nombre d'appels envoyés à la régie publicitaire Microsoft et le nombre de recherches envoyées à l'interface de recherche Bing V5. Tant que le second reste inférieur ou égal au premier, la recherche passe par Bing V5. Dès que le second dépasse le premier, le trafic bascule sur l'interface Azure DataMarket, celle qui consomme le crédit du compte payant.

Ce qui l'implique : le droit d'interroger l'interface de recherche est gagé sur le fait d'avoir affiché des publicités, dans un rapport de un pour un, appliqué automatiquement, jour par jour. Le commentaire du développeur l'énonce sans ambiguïté — « Enable Bing V5 calls until their number is equal to Ads queries count ». La variable $version, qui transporte l'écart entre les deux compteurs sous la forme B-42 ou A17, montre que cet écart était suivi.

Ce constat éclaire d'un jour concret la question du coût traitée au chapitre 21 : l'accès à la recherche n'est pas seulement acheté, il est échangé contre de l'inventaire publicitaire, et l'échange est arbitré par le code en temps réel.

6. Pièce n° 3 — Deux interfaces Microsoft, et un cache de sept jours

Les deux fonctions de construction d'URL coexistent dans le même fichier. Ligne 126, l'interface Bing V5 :

    public function getApiUrlBingV5($keyword, $locale, $offset, $count, $filters)
    {
        $apiCall = 'https://www.bingapis.com/api/v5/search?appid=' . config_item('bing_app_id') . '&responseFilter=Webpages&format=json&offset=' . $offset . '&count=' . $count . '&mkt=' . implode('-', $locale) . '&q=' . urlencode($keyword);

Ligne 155, l'interface Azure DataMarket :

    public function getApiUrlBingAzure($keyword, $locale, $offset, $filters)
    {
        $apiCall = 'https://api.datamarket.azure.com/Bing/SearchWeb/v1/Web?$format=json&$skip=' . $offset . '&Market=%27' . implode('-', $locale) . '%27&Query=%27' . urlencode($keyword) . '%27';

Ce code fait ceci : il transmet à Microsoft, en clair dans l'URL, le texte intégral de la requête de l'utilisateur (q ou Query), son marché linguistique (mkt ou Market), sa position de pagination et son réglage de filtrage familial. Ce sont les paramètres de la recherche elle-même qui quittent l'infrastructure Qwant.

La dépendance n'est pas cantonnée au Web. Les autres verticales suivent le même schéma :

Verticale Fichier et ligne Interface appelée
Web search_web.php:126 https://www.bingapis.com/api/v5/search
Web search_web.php:155 https://api.datamarket.azure.com/Bing/SearchWeb/v1/Web
Actualités search_news.php:222 https://www.bingapis.com/api/v5/news/search
Actualités search_news.php:258 https://api.datamarket.azure.com/Bing/Search/v1/News
Images search_images.php:233 https://www.bingapis.com/api/v5/images/search
Images search_images.php:287 https://api.datamarket.azure.com/Bing/Search/v1/Image
Publicité search_ads.php:232 https://www.bingapis.com/api/v5/ads/search

Pour les actualités, le repli vers Microsoft est commandé par une liste de langues « couvertes par l'univers » interne, search_news.php ligne 13 :

    protected $universeLanguages = ['fr', 'de', 'en', 'it', 'co'];

Toute langue absente de cette liste est servie par Bing News. On relèvera au passage que l'appel News V5 est authentifié avec l'identifiant applicatif de la régie publicitaire et non celui de la recherche (search_news.php:222 : config_item('bing_ads_app_id_search')), ce qui confirme que les deux relations passent par le même compte Microsoft.

Enfin, la durée de conservation des résultats est fixée ligne 35 :

    /**
     * @return int
     * Web results cache lifetime
     */
    public function getCacheLifetime()
    {
        return 3600 * 24 * 7;
    }

Soit sept jours. La discussion de la compatibilité de cette durée avec les conditions d'utilisation publiques de l'API Bing figure au chapitre 22 ; elle n'est pas reprise ici.

7. Pièce n° 4 — L'authentification : Microsoft est câblé dans la passerelle HTTP

Le fichier www/application/models/httpgateway.php centralise tous les appels sortants. Lignes 181 à 183 :

    private function setupAuthentication($url, $curlHandler)
    {
        if (preg_match('/api.datamarket.azure.com/', $url) > 0) {
            curl_setopt($curlHandler, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
            curl_setopt($curlHandler, CURLOPT_USERPWD, "ignored:" . $this->bingKey);

Ce code fait ceci : il reconnaît l'hôte Microsoft par expression régulière et lui présente la clé Qwant en authentification HTTP Basic, le nom d'utilisateur étant littéralement ignored. Ce qui l'implique : la relation avec Microsoft n'est pas un branchement périphérique ajouté dans un module de recherche, elle est inscrite dans la couche de transport commune de l'application.

La même passerelle tient un relevé des appels sortants, lignes 229 à 231 :

        $wantedHosts = [
            'api.datamarket.azure.com' => 'bing',
            'www.bingapis.com' => 'bing',

Les deux hôtes Microsoft ouvrent la liste et sont rattachés à un même domaine logique, bing. Le devenir de ces relevés est traité au chapitre 55.

Un détail mérite d'être noté par contraste avec la pièce n° 6 : pour les appels de recherche, la passerelle substitue un User-Agent fixe à celui du visiteur, ligne 17 :

        $this->defaultUserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; " . "en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1";

L'équipe savait donc masquer le navigateur de l'utilisateur, et le faisait pour la recherche. La pièce n° 6 montre qu'elle ne le faisait pas pour la publicité.

8. Pièce n° 5 — La clé d'accès est inscrite en clair dans le dépôt

Dans www/application/config/constants_common.php, lignes 38 et 39, reproduites ici avec la valeur des clés et les adresses masquées :

//define('BING_KEY_WEBONLY', '***REDACTED***');  // q.q***@gmail.com
define('BING_KEY_WEBONLY', '***REDACTED***');    // m.j***@qwant.com

Ce code fait ceci : il définit, en clair dans un fichier versionné, la clé unique utilisée par toutes les recherches Azure DataMarket — Web, Actualités et Images. La ligne précédente conserve la clé antérieure, également en clair, en commentaire.

Deux constats en découlent. Sur le plan de la sécurité, un secret d'interface engageant un compte facturé a été committé dans un dépôt et y demeure accessible dans l'historique. Sur le plan de la caractérisation de la relation, les commentaires rattachent ces clés à des comptes de messagerie nominatifs, dont l'un est une adresse @gmail.com : l'accès à l'infrastructure de recherche de Microsoft était administré sur des comptes personnels.

Les valeurs réelles ne sont pas reproduites ici ; elles sont vérifiables par un tiers dans le blob 12c2c3c582ce8e8db67ac186a71a0308d70f84f0, lignes 38 et 39.

9. Pièce n° 6 — Ce que chaque appel publicitaire transmet à Microsoft

Dans www/application/models/search/search_ads.php, lignes 133 à 138, le commentaire du développeur précède l'énumération :

        // Bing Ads required headers:
        // Referer, IP address, User agent
        $referer             = !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : config_item('front_url_' . config_item(API_APP));
        $ipAddress           = $this->input->ip_address();
        $ipAddressAnonymized = $this->anonymizeIp($ipAddress);
        $userAgent           = $this->input->user_agent();

Puis lignes 154 à 157, ces valeurs sont posées sur la requête sortante :

        $extraOptions                     = [];
        $extraOptions[CURLOPT_USERAGENT]  = $userAgent;
        $extraOptions[CURLOPT_HTTPHEADER] = ['X-Search-ClientIP: ' . $ipAddressAnonymized];
        $extraOptions[CURLOPT_REFERER]    = $referer;

Ce code fait ceci : il transmet à Microsoft, pour chaque affichage publicitaire, le User-Agent réel du navigateur du visiteur, son adresse IP amputée du dernier octet, et l'URL de provenance. S'y ajoutent, dans l'URL de l'appel, le texte de la requête et le marché.

Le mot « anonymisée » employé dans le nom de la variable renvoie à cette fonction, lignes 422 à 427 :

    /**
     * @param $ip
     *
     * @return string
     * Replace last digits with 0 from an IP address
     */
    private function anonymizeIp($ip)
    {
        $lastPoint = strrpos($ip, '.');

        return ($lastPoint !== false) ? substr($ip, 0, $lastPoint) . '.0' : $ip;
    }

Ce que cela implique : la transformation remplace le dernier octet par zéro. Elle conserve donc le préfixe /24, qui désigne un réseau d'au plus 256 adresses. Combinée au User-Agent complet et au référent, elle ne fait pas obstacle à la ré-identification. Cette question est traitée au fond aux chapitres 33 et 53 ; l'analyse des en-têtes figure aux chapitres 9 et 40.

On relèvera enfin qu'un dispositif de répartition A/B, mis en commentaire lignes 220 à 230, calculait un identifiant de visiteur par hash('sha256', $config['client_ip'] . SALT_CLIENT_HASH) à partir de l'adresse IP non tronquée, transportée jusque-là dans $config['client_ip'] (ligne 152). Le dispositif est désactivé au tag examiné ; sa présence montre que l'IP complète restait disponible côté application.

10. Pièce n° 7 — Les identifiants contractuels : deux propriétés, douze unités publicitaires

L'URL de l'appel publicitaire, lignes 232 à 239 :

        $apiCall = 'https://www.bingapis.com/api/v5/ads/search?appid=' . config_item('bing_ads_app_id_' . config_item(API_APP)) .
                   '&adUnitId=' . $adUnitId .
                   '&propertyId=' . config_item('bing_ads_property_id_' . config_item(API_APP)) .
                   '&q=' . urlencode($keywords) .
                   '&mainlineCount=' . $count .
                   '&pageNumber=' . $page .
                   '&mkt=' . urlencode($mkt) .
                   '&textDecorations=true&textFormat=html';

Les identifiants correspondants, dans www/application/config/config_common.php, lignes 96 à 116 :

$config['bing_ads_app_id_search']      = '***REDACTED***';
$config['bing_ads_app_id_lite']        = '***REDACTED***';
$config['bing_ads_extensions']         = 'LongAdTitle,EnhancedSiteLinks';
$config['bing_ads_items_max']          = 2;
$config['bing_ads_property_id_search'] = 345761;
$config['bing_ads_property_id_lite']   = 349323;
$config['bing_ads_unit_search']        = [
    'fr' => 300774,
    'de' => 300775,
    'gb' => 300776,
    'ca' => 300777,
    'us' => 300778,
    'ch' => 300779
];
$config['bing_ads_unit_lite']          = [
    'fr' => 311248,
    'de' => 311247,
    'gb' => 311249,
    'ca' => 311245,
    'us' => 311250,
    'ch' => 311246
];

Ce code fait ceci : il déclare deux propriétés publicitaires Microsoft distinctes — 345761 pour le produit principal, 349323 pour le produit « lite » — et douze unités publicitaires réparties sur six pays pour chacune d'elles. Les identifiants applicatifs sont masqués ici car ils s'apparentent à des secrets ; les numéros de propriété et d'unité, eux, sont reproduits en clair car ce ne sont pas des secrets et ce sont eux qui portent la valeur probante.

Ce que cela implique : de tels identifiants ne s'obtiennent pas par une inscription libre à une interface publique. Ils sont attribués par la régie au terme d'un référencement, produit par produit et pays par pays. Leur présence dans le code établit l'existence d'une relation de monétisation formalisée entre Qwant et Microsoft Advertising, portant sur deux produits et six marchés.

Deux réserves accompagnent ce constat. D'une part, les interrupteurs bing_ads_enabled_search et bing_ads_enabled_lite sont à false dans la configuration commune du tag examiné, la valeur effective dépendant des surcharges d'environnement. D'autre part, bing_ads_testing_search et bing_ads_testing_lite valent true, ce qui ajoute &trafficType=test&form=monitr à l'appel. Le code établit donc l'intégration et son périmètre contractuel, non le volume commercial effectivement servi à cette date.

11. Pièce n° 8 — L'épuisement du crédit Microsoft alerte la direction

Dans www/application/libraries/Mailer.php, lignes 409 à 419, adresses masquées :

    public function sendAlertBing()
    {

        $list_user = array('e.l***@gmail.com', 'e.b***@gmail.com', 'j.c***@gmail.com');
        $title = '[URGENT] un des compte n\'a plus de crédit !';
        $message = 'Un des comptes n\'a plus de crédit, il faut impérativement le recharger immédatement.';

        foreach ($list_user as $user) {
            $this->doSendMail($title, $message, $user);
        }

    }

Ce code fait ceci : lorsqu'un compte Microsoft arrive à court de crédit, il expédie un courriel qualifié d'urgent à trois destinataires, en leur demandant de recharger « immédatement » — la faute d'orthographe est dans le code source.

Ce que cela implique tient en une phrase : si le crédit Microsoft s'épuise, le moteur de recherche s'arrête. On ne construit pas une procédure d'escalade de ce type pour un fournisseur accessoire ; on la construit pour une dépendance dont la rupture interrompt le service. Le fait que les trois destinataires soient des adresses personnelles, dont celle du dirigeant, indique le niveau auquel l'incident était traité.

Cette fonction est le vestige d'un dispositif plus ancien : le modèle bing_error.php, créé le 5 juin 2013, qui journalisait les erreurs Bing en base et déclenchait cette alerte au plus une fois toutes les cinq minutes. Ce modèle a été supprimé avant décembre 2016, mais la fonction d'alerte subsiste dans le tag examiné, désormais sans appelant. Sa présence documente une période d'au moins trois ans et demi, de juin 2013 à décembre 2016, pendant laquelle l'indisponibilité de Bing était traitée comme un incident critique.

12. Ce que le code prouve

  1. Au 22 décembre 2016, aucune recherche Web ne peut être servie par le moteur interne : la liste des langues qui l'autorisent est vide, ses deux valeurs étant mises en commentaire.
  2. Le nombre de recherches adressées à l'interface Bing V5 est plafonné chaque jour par le nombre d'appels publicitaires émis, selon un rapport de un pour un inscrit dans le code et documenté par un commentaire de développeur.
  3. Au-delà de ce plafond, le trafic bascule sur l'interface Azure DataMarket, qui consomme le crédit d'un compte payant.
  4. Le texte intégral des requêtes des utilisateurs, leur marché linguistique et leur réglage de filtrage familial sont transmis à Microsoft pour les verticales Web, Actualités, Images et pour la publicité.
  5. Les résultats sont conservés en cache sept jours.
  6. Chaque appel publicitaire transmet en outre le User-Agent réel du visiteur, son adresse IP tronquée au dernier octet et son référent HTTP.
  7. La fonction nommée anonymizeIp() conserve le préfixe /24, qui désigne un réseau d'au plus 256 hôtes.
  8. La relation publicitaire repose sur deux propriétés Microsoft et douze unités publicitaires réparties sur six pays, identifiants qui supposent un référencement préalable auprès de la régie.
  9. La clé d'accès à l'interface de recherche est inscrite en clair dans le dépôt et rattachée en commentaire à des comptes de messagerie nominatifs, dont un compte @gmail.com.
  10. Une procédure d'alerte adressait aux dirigeants, en cas d'épuisement du crédit Microsoft, un courriel qualifié d'urgent.
  11. La dépendance est présente dès le premier import du dépôt, le 30 avril 2012.

13. Ce qui reste à démontrer

  • La part de trafic réellement servie par chacune des deux interfaces Microsoft en production, à cette date et sur la durée : les compteurs journaliers existent dans le code, leurs valeurs ne figurent pas dans le dépôt.
  • Le contenu du contrat conclu avec Microsoft, et notamment si le rapport de un pour un entre recherches et annonces y est stipulé ou s'il procède d'un choix d'implémentation de Qwant.
  • L'état effectif des interrupteurs bing_ads_enabled_* sur les serveurs de production au 22 décembre 2016, les surcharges d'environnement n'étant pas toutes présentes dans le dépôt.
  • Le sort réservé par Microsoft aux données reçues : durée de conservation, croisement éventuel avec d'autres jeux, réutilisation.
  • Le nombre de personnes concernées et la période exacte pendant laquelle les en-têtes publicitaires ont été transmis en production.
  • Si les clés inscrites en clair ont fait l'objet d'une rotation après leur exposition, et si cette exposition a été signalée.

Annexe — Empreintes et objets Git

Empreintes relevées le 5 août 2026 sur le tag v1.32.1. La colonne « SHA-256 » porte sur le contenu exact du blob, obtenu par git cat-file blob <blob> | sha256sum. La colonne « Dernier commit » indique le dernier commit ayant modifié ce fichier à cette version, et le nom de son auteur.

Fichier Blob Git Dernier commit Date Committer
www/application/models/search/search_web.php d36ca4c6440552df106a722c73e86822c86bc9a1 22b32fdc0 21 novembre 2016 p.vignaux
www/application/models/search/search_ads.php 6081e444d17b26cfbc60cfc2f444c8910f842818 5a422fff1 1er décembre 2016 p.vignaux
www/application/models/search/search_news.php bbf7b20dd79be6a2d75d1492f9697fa964744095 933189f67 17 novembre 2016 Pierre
www/application/models/search/search_images.php b8b5241d33d481b5e9421fb85239af1f2061bfec 57429a973 18 novembre 2016 p.vignaux
www/application/models/httpgateway.php 2b475c883d8b9e317eaf934e15dd603843af7f4b ebcb62f62 2 août 2016 p.vignaux
www/application/config/constants_common.php 12c2c3c582ce8e8db67ac186a71a0308d70f84f0 78ad41fbb 17 août 2016 Sampson SAVINEL
www/application/config/config_common.php 00295cc10ec307449321538c4a4e42cd29ded49a ad767f7b5 16 décembre 2016 p.vignaux
www/application/libraries/Mailer.php d0b37b8191585822400a678f541e4329deb94bb7 ddd315c3a 21 novembre 2016 p.vignaux

Empreintes SHA-256 des contenus correspondants :

Fichier SHA-256
search_web.php C3C44BFC781B4577D1963572AC6A260E712A51E8A33E7855D9C363DD21CA3DF7
search_ads.php 05A38FE7D6284099491AB1BF9894E6565D554A788B01996ABD7B11A46CF43735
search_news.php 078A4EE68B93E55F99F4BC363970459141C1E6C24D4DDAA0CD1C231FD18F277C
search_images.php 9C3B18BA119D57BBFF470C454C9A8EC3235F8208B2AB3EBE6E5DC1DF0B70362D
httpgateway.php DFA8B45C88978746DA344A332CA27CA37C5D9758BF48BCC26CED04F96A182DAB
constants_common.php E6A9C892D01E64DCD28D8C2C5EF210ED20A99F228A4FE2D702694520CA80381A
config_common.php 4B8A223551370240D9462BBF1AB7499A3AAE524D642AF60CDF5EC40203D4AA5C
Mailer.php 164D0BD6D7CDCDFBA673923D6A627FB0FD5825EC044C3DB728593D92D224EF6E

Les blobs de search_web.php et search_ads.php sont identiques à ceux publiés à la section 8.2 du chapitre 40, relevés indépendamment à une date antérieure.

Pour une procédure contentieuse, une acquisition forensique complète devrait en outre documenter la provenance du clone, la liste de ses dépôts distants, le graphe complet des références, l'horodatage de l'acquisition et une empreinte de l'archive entière.

14. Articulation avec le reste du dossier

Chapitre Ce qu'il apporte
9 — De l'alerte publique à l'alerte interne Les alertes de 2016 sur les en-têtes transmis à Microsoft
18 — Qwant ou Xilopix : exister d'abord, ou construire d'abord La portée stratégique du choix de la dépendance
19 — Qwantify en 2014 et 2015 Ce qu'un crawler établit et n'établit pas
21 — Ce que l'accès à Bing et Bing Ads a pu coûter La reconstitution économique, que la pièce n° 2 vient éclairer
22 — Faire « mouliner » la technologie de Qwant sur les résultats de Bing L'historique Git de search_web.php et la question contractuelle du cache
33 — État de l'art de la ré-identification Pourquoi un préfixe /24 et un User-Agent ne sont pas anonymes
40 — Fraude aux financements publics La confrontation du code aux déclarations faites aux financeurs
49 — Qwant en 2020 : index figé, liens morts La suite de l'histoire, quatre ans plus tard
53 — L'avertissement prouve que Qwant savait La connaissance interne de l'enjeu du préfixe IPv4
55 — DataHub Ce que devenaient, côté Qwant, les événements liés à ces appels
Analyse forensique du dépôt Git La méthode d'examen du dépôt et la branche demo

Pièces locales

Sources externes

Conclusion probatoire

Le code du tag v1.32.1 ne se borne pas à montrer que Qwant appelait Bing : il montre que la recherche et la publicité étaient couplées par construction. Le droit d'interroger l'interface de recherche y est gagé, jour après jour et dans un rapport de un pour un, sur le nombre d'annonces affichées ; au-delà, le trafic bascule sur un compte facturé dont l'épuisement déclenche une alerte urgente vers la direction. La formulation la plus étayée est donc celle-ci : à cette date, l'existence même du service de recherche dépendait, dans le code, du maintien de la relation commerciale avec Microsoft.

Ce constat porte sur une version de release. Il n'établit ni les volumes servis, ni les stipulations du contrat, ni l'intention de ceux qui l'ont écrit. Il établit une architecture, et cette architecture est vérifiable par quiconque dispose du dépôt et des empreintes publiées à l'annexe.

← Retour au sommaire du dossier

⬇ Download