[Cybersécurité] Se protéger des failles XSS avec les headers CSP (Content Security Policy)

Les failles XSS (Cross-Site Scripting), étant classées en troisième position du dernier TOP 10 de l’OWASP, sont une porte d’entrée dans les systèmes d’information (scan de ports, exploits, etc.) et  pour les utilisateurs (vol d’information d’identifications).  Les traditionnelles contre mesures mises en place sont le filtrage des données entrantes sur le serveur (input filtering) et l’encodage des données sortantes (output encoding) afin d’éviter l’exécution de scripts malveillants sur les postes clients.

Principe

Une autre manière de se protéger de ce type de faille est de mettre en place le header HTTP Content-Security-Policy définissant la stratégie mise en place pour contrôler les contenus récupérés et envoyés par une page web côté client. Le principe du CSP est de définir quelles sources pourront être utilisées pour les scripts javascript, les styles CSS, les fonts, les POST de formulaires, les requêtes AJAX, etc.

Les sources disponibles sont les suivantes :

  • self  : domaine courant
  • none : aucun domaine
  • all : tous les domaines
  • liste de domaines séparés par un espace.

Si un élément n’a pas de configuration, la valeur du paramètre default-src sera utilisée.

default-src dispose de 2 valeurs supplémentaires disponibles :

  • unsafe-inline : autorise l’exécution de scripts inline (<script></script), avec CSP, ils sont par défaut interdits
  • unsafe-eval : autorise l’utilisation de la méthode eval()

D’autres options sont disponibles, entre autres :

  • Manipulation du module Anti-XSS des navigateurs
  • Gestion de nonce pour les scripts inline (CSP v2)

Récupération de contenu

La quasi totalité des éléments (font, fichiers de style CSS, fichiers javascript, images, iframe, etc. pouvant être téléchargés et/ou exécutés par une page web peuvent être contrôlés par CSP. Pour chacun de ces éléments il est possible de définir une ou plusieurs sources de confiance.

Par exemple :

  • Javascript et CSS : tous les fichiers doivent provenir du domaine suivant : https://cdn.azure.com
  • Images : toutes les images doivent provenir du domaine : http://images.azure.com
  • Les scripts inline (<script></script>) sont interdits
  • Pour tous les autres contenus, n’autoriser que le domaine de la page courante

Se traduira par le header HTTP suivant :

Content-Security-Policy: default-src ‘self’ ; script-src https://cdn.azure.com; style-src https://cdn.azure.com; img-src http://images.azure.com;

La liste des paramètres est disponible ici.

Envoi de contenu

De la même manière, il est possible de définir une politique pour l’émission de requêtes depuis la page cliente. C’est principalement le cas pour les requêtes AJAX et les formulaires (CSP v2).

Par exemple :

  • Requêtes AJAX/WebSocket : N’autoriser les requêtes que vers les domaines : https://api.monsite.com et https://api.sitetiers.com
  • Envoi de formulaire : N’autoriser les requêtes que vers le domaine courant

Se traduira par le header HTTP suivant :

Content-Security-Policy: connect-src https://api.sitetiers.com https://api.monsite.com; form-action ‘self’ ;

Les paramètres sont cumulables avec l’exemple précédent.

Mode reporting

Mettre en place CSP avec une mauvaise configuration peut avoir de lourdes conséquences sur un site web. Si une source est oubliée, on peut facilement se retrouver avec des fichiers javascript qui ne se chargent pas, un site sans style CSS ou sans images par exemple.

A des fins de tests, le header Content-Security-Policy-Report est disponible. Il se comporte comme le header Content-Security-Policy mais ne bloque pas réellement les ressources.

Exemple :

content-security-policy-report

CSP met également un paramètre report-uri pour notifier une url des violations de stratégies CSP via une requête POST.

Exemple de requête report reçue :

report-uri

Compatibilité et versions

En terme de compatibilité, la version 1 de ce header est pris en charge par les dernières versions de principaux navigateurs :

Compatibilite_CSP

Source : http://caniuse.com/#feat=contentsecuritypolicy

La version 2 n’est quant à elle pas encore disponible partout :

Compatibilite_CSP2

Source : http://caniuse.com/#feat=contentsecuritypolicy2

Sur les navigateurs non compatibles, le header est simplement ignoré.

Un draft de la version 3 est sortie le 21 juin 2016 et est disponible ici.

Implémentation

Pour implémenter le CSP sur un site web, il suffit d’avoir la possibilité de rajouter des headers HTTP personnalisés sur les réponses faites par notre serveur. Ce type de mécanisme est intégré à la plupart des frameworks de développement web.

Afin de faciliter la mise en place et la maintenabilité des règles CSP, certaines librairies sont disponibles en fonction des frameworks. Par exemple :

Le serveur web peut également être configuré pour ajouter automatiquement ce header HTTP à toutes les requêtes sortantes.

Liens utiles

 

 

ASCII Folding dans Elasticsearch et appel de _analyze

Dans Elasticsearch, pour une faire une recherche sur un mot contenant un caractère unicode non présent dans la table ASCII (un accent par exemple) sans avoir à rechercher spécifiquement le mot avec ce caractère, une phase d’ASCII folding est nécessaire pour convertir ces caractères unicodes en leurs équivalents ASCII (si possible). Par exemple « ê » devient « e ».

Pour réaliser cette étape, le mapping du champ contenant ces caractères doit spécifier le filtre « asciifolding » fourni nativement par le moteur Elasticsearch.

Exemple de création d’index avec déclaration de notre analyzer avec asciifolding :

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "filter":  [ "standard", "asciifolding" ]
        }
      }
    }
  }
}

On créé ensuite le type en indiquant que notre champ utilisera notre nouvel analyzer :

PUT /my_index/_mapping/fruit
{
  "properties": {
    "name": {
      "type": "string",
      "fields": {
        "asciifolding": {
          "type": "string",
          "analyzer": "my_analyzer"
        }
      }
    }
  }
}

Enfin, on ajoute un document contenant une valeur accentuée dans le champ « name » :

POST my_index/fruit 
{
  "name": "pêche"
}

Les exemples de recherche suivants illustrent l’utilité de notre analyzer.

Requête Réponses
GET my_index/fruit/_search
{
  "query": {
    "match": {
      "name": "peche"
    }
  }
}
{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 0,
      "max_score": null,
      "hits": []
   }
}

GET my_index/fruit/_search
{
  "query": {
    "match": {
      "name.asciifolding": "peche"
    }
  }
}

{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1,
      "hits": [
         {
            "_index": "my_index",
            "_type": "fruit",
            "_id": "AVR4VgBfRZI8D8Bc982h",
            "_score": 1,
            "_source": {
               "name": "pêche"
            }
         }
      ]
   }
}

A noter que si on est en présence de mots français, il est plus intéressant d’utiliser l’analyzer « french » fourni dans Elasticsearch qui procède aussi à cette étape d’asciifolding mais ajoute également le stemming, les stopwords, etc. (analyzers de langues).

Pour tester un analyzer (ou un tokenizer ou un filtre), il est possible d’appeler « _analyze » sur un index via une requête HTTP GET.

Par exemple :

http://localhost:9200/my_index/_analyze?analyzer=my_analyzer&text=pêche

va renvoyer

{
  "tokens": [
    {
      "token": "peche",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 1
    }
  ]
}

Alors que

http://localhost:9200/my_index/_analyze?analyzer=standard&text=pêche

va renvoyer

{
  "tokens": [
    {
      "token": "pêche",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 1
    }
  ]
}

Attention, n’exécutez pas la requête « _analyze » dans Sense. L’encodage ne sera pas correct lors de la requête et la réponse sera fausse.

Requête :

GET /my_index/_analyze?analyzer=my_analyzer&text=pêche

Réponse :
{
   "tokens": [
      {
         "token": "p",
         "start_offset": 0,
         "end_offset": 1,
         "type": "<ALPHANUM>",
         "position": 1
      },
      {
         "token": "che",
         "start_offset": 2,
         "end_offset": 5,
         "type": "<ALPHANUM>",
         "position": 2
      }
   ]

 

Backbone, Node.Js et SEO

Les frameworks javascript du type Backbone.js ou Angular.js sont en plein essor et permettent de construire rapidement et proprement des applications dites Single page application. Ce type d’application permet principalement d’améliorer l’expérience de vos utilisateurs.

Lorsque l’utilisateur souhaite accéder à l’application, il envoie une requête sur un serveur web, qui lui répond par une page HTML classique. Cette page fait référence aux fichiers javascript de Backbone, underscore.js, jquery et le javascript contenant la logique de l’application basée sur Backbone.

Exemple :

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <meta name="fragment" content="!">

    <title></title>
</head>
<body>

</body>
</html>

<script src="http://underscorejs.org/underscore.js"></script>
<script src="http://code.jquery.com/jquery-2.1.3.js"></script>
<script src="http://backbonejs.org/backbone.js"></script>

<script>
    // Javascript correspondant à l'application Backbone
</script>

Le code javascript correspondant à l’application est ensuite exécuté par le navigateur. La page n’est plus rechargée, toutes les interactions avec le serveur se font via des appels AJAX. L’URL du navigateur peut changer via des ancres “#” ou via pushState disponible avec HTML5.

Problème

Même si Google indique dans ses best practices que les crawlers analysent le CSS et les fichiers javascript, il est fortement conseillé d’avoir le contenu du site web dans les balises <body> de la page lorsqu’elle est téléchargée depuis le serveur web.

Exemple :

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <!-- ... -->
</head>
<body>
    Le contenu du site web indexé par Google
</body>
</html>

Comme on peut le voir sur le 1er exemple, les balises <body> sont vides. Le contenu arrivera dynamiquement lors de la construction par Backbone.

On peut donc en déduire que l’utilisation d’un framework type Backbone ou Angular n’est pas bonne pour l’indexation par Google.

Solution

Heureusement, Google a mis en mécanisme permettant d’indexer les applications basées sur des appels AJAX. Tout le fonctionnement est résumé sur la page développeur de Google.

En résumé, 2 cas se présentent :

– Si vous utilisez les ancres dans les URLs pour naviguer dans l’application Backbone (exemple d’URL : www.example.com/index.html#key=value). Dans ce cas, transformez simplement le “#” en “#!” (exemple d’URL transformée : www.example.com/index.html#!key=value).

– Si vous utilisez le pushState de HTML5 (exemple d’URL www.example.com/user/1), ajoutez simplement la balise meta suivante dans le <head> de votre page :

<meta name="fragment" content="!">

Ainsi, si Google rencontre un “#!” ou la balise meta ci-dessus alors il appellera votre page avec un paramètre supplémentaire dans l’URL :

– Exemple du “#!” :

www.example.com/index.html#key=value devient

www.example.com/ajax.html?_escaped_fragment_=key=value

– Exemple du pushState :

www.example.com/user/1 devient

www.example.com/user/1?_escaped_fragment_=

Le paramètre “_escaped_fragment” est ajouté lors de l’appel au serveur. Ce paramètre permet de déclencher un comportement différent côté serveur pour rendre une page statique avec une contenu lisible par Google.

Mise en place de l’application

Côté serveur, nous allons utiliser Node.Js pour restituer la page HTML brute contenant le javascript pour les utilisateurs et une page HTML statique pour Google.

Le projet Node.Js se base sur la librairie ExpressJs permettant de mettre en place rapidement un serveur web. Le projet peut être téléchargé ici ou être généré via yeoman.

Dans notre exemple, l’application Backbone est minimaliste et présente un vêtement dérivé d’un animal de compagnie imaginaire au nom imprononçable (terme sans concurrence sur Google).

Une démonstration de l’application terminée est disponible ici.

L’architecture du projet est la suivante :

image

Notre application Backbone se trouve dans le fichier /public/groingrek/index.html. Elle fait quelques appels au serveur en AJAX pour récupérer les informations sur notre produit :

<script>
    $(document).on('click', 'a[href^="/"]', function (event) {
        event.preventDefault();
        Backbone.history.navigate(this.href);
    });

    var eventAggregator = _.extend({}, Backbone.Events);

    var Cara = Backbone.Model.extend({});
    Caras = Backbone.Collection.extend({
        model: Cara,
        url: "http://aymeric.cloudapp.net/groingrek/caracteristiques"
    });

    var CarasView = Backbone.View.extend({
        tagName: "ul",
        render: function () {
            console.log(this);
            console.log(this.collection);
            _(this.collection.models).each(function (cara) {
                //console.log(person);
                this.$el.append(new CaraView({ model: cara }).render().el);
            }, this);

            return this;
        }
    });

    var CaraView = Backbone.View.extend({
        tagName: "li",
        render: function () {
            this.$el.html("Nom : " + this.model.get("name") + "<br />Valeur : " + this.model.get("value") + "<br />Description : " + this.model.get("description"));
            return this;
        }
    });

    var MainModel = Backbone.Model.extend({
        url: "http://aymeric.cloudapp.net/groingrek/description"
    });

    var MainView = Backbone.View.extend({
        tagName: "div",
        model: MainModel,
        render: function () {
            this.$el.html("Nom : " + this.model.get("nom") +
                    "<br />Description : " + this.model.get("description") + "br />" +
                    'twitter : <a href="' + this.model.get("twitter") + '" title="Groingrex Twitter">groingrek Twitter</a><br />' +
                    'informations : <a href="./details" title="Groingrex caractéristiques">groingrek caractéristiques</a>');
            return this;
        }
    });

    var CarasRouter = Backbone.Router.extend({

        routes: {
            "details": function () {
                var caras = new Caras();
                caras.fetch({
                    success: function () {
                        $('body').html(new CarasView({ collection: caras }).render().el);
                    }
                });
            },
            "index": function () {
                var main = new MainModel();
                main.fetch({
                    success: function () {
                        $('body').html(new MainView({ model: main }).render().el);
                    }
                });
            },
            "*path": function () {
                router.navigate("index", { trigger: true });
            }
        }
    });

    var router = new CarasRouter();
    Backbone.history.start({ pushState: true, "root": "/groingrek" });

</script>

Côté serveur, des routes sont définies pour répondre aux appels AJAX et pour délivrer le fichier index.html (/route/index.html) :

var express = require('express');
var router = express.Router();

router.get("/groingrek/caracteristiques", function (req, res, next) {
    res.json([
        {
            name: "taille",
            value: 'XL',
            description: "La taille du groingrek est XL, elle permet aux hommes corpulents de pouvoir arborer leur groingrek préféré"
        },
        {
            name: "couleur",
            value: "bleu",
            description: "La magnifique couleur de groingrek permet de se prendre pour un vertitable groingrek de par son éclatante qualité"
        },
        {
            name: "forme",
            value: "conique",
            description: "La forme conique de notre produit donne vraiment l'illusion que l'on se prend pour notre bestiole préférée, le groingrek"
        }
    ]);
});

router.get("/groingrek/description", function (req, res, next) {
    res.json({
        "nom": "groingrek le terrible",
        "photo": "http://aymeric.cloudapp.net/images/groingrek.png",
        "description": "Le groingrek est un animal de compagnie doté d'une puissance incomparable. Il peut engloutir des centaines de kilos de viandes en très peu de temps. En outre, le groingrek est un très sympathique est vous fera passer d'excellents moments. Le produit que nous proposons pour la modique somme de 5€ est un costume complet pour devenir vous même un groingrek et encourager vos amis dans votre groingrekitude.",
        "lien": "http://aymeric.cloudapp.net/groingrek/caracteristiques",
        "twitter": "https://twitter.com/groingrek"
    });
});

router.get("/groingrek*", function (req, res, next) {
    res.sendFile("./public/groingrek/index.html", { root: "../" });
});

module.exports = router;

Ici, l’application est pleinement fonctionnelle pour les utilisateurs mais est presque invisible pour Google.

Prerender

Pour gérer le cas du paramètre _escaped_fragment_= envoyé par Google, nous allons utiliser  Prerender.

Prerender peut être utilisé en tant que service (gratuit jusqu’à un certain nombre de pages) mais il peut également être installé en standalone sur votre serveur. Il est basé sur PhantomJS, un navigateur sans interface graphique basé sur WebKit qui aura pour rôle de jouer le javascript de notre page HTML.

L’objectif ici est donc de déployer ce service et de l’utiliser depuis notre application web.

Pour installer prerender, rendez-vous sur la page GitHub dédiée et télécharger les sources.

Une fois téléchargé, déposez les sources sur votre serveur et lancez la commande suivante à la racine du répertoire :

npm install

Lancez ensuite le service Prerender avec la commande :

node server.js

Votre service Prerender est maintenant disponible à l’adresse : http://localhost:3000.

Pour tester que le service fonctionne correctement, tentez d’accéder à l’URL suivante :

http://localhost:3000/http://aymeric.cloudapp.net/groingrek/index

On constate que le HTML renvoyé par le service contient des balises <body> avec le contenu de ma page qui est normalement construit côté client avec Backbone.

image

Maintenant que le service est en place, il faut modifier notre application web pour utiliser ce service quand Google demandera la page.

On commence par installer le package Node.Js fourni par Prerender :

npm install prerender-node –save

On ouvre ensuite le fichier app.js et on ajoute la ligne suivante :

app.use(require('prerender-node').set('prerenderServiceUrl', 'http://localhost:3000'));

au dessus de la ligne :

app.use(logger('dev'));

Après avoir redémarré l’application web, les URLs qui utilisent _escaped_fragment_ utiliseront Prerender.

La commande :

curl http://aymeric.cloudapp.net/groingrek/index

renvoie toujours notre application avec un body vide et tout le javascript utilisé pour construire l’application.

image

En revanche, la requête :

curl http://aymeric.cloudapp.net/groingrek/index?_escaped_fragment_=

renvoie une page HTML statique sans javascript et avec le contenu de l’application dans les balises <body>.

image

Performance

Sur la requête qui appelle le service Prerender, on remarque que les temps de réponse sont mauvais. Ils viennent du temps de processing fait par PhrantomJS avant de renvoyer la page.

Temps :

– http://aymeric.cloudapp.net/groingrek/index : environ 10ms

– http://aymeric.cloudapp.net/groingrek/index?_escaped_fragment_= : environ 1s

Pour palier à ce problème, il est possible de mettre en cache le HTML généré par PhantomJS afin de le resservir très rapidement lors des prochains appels.

Cette gestion du cache est prévu dans le service Prerender grâce à Redis (installation de Redis ici). Lorsque Redis est installé sur votre machine, lancez la commande suivante à la racine du service prerender installé précédemment :

npm install prerender-redis-cache –save

Editez ensuite le fichier server.js de ce même service pour ajouter la ligne suivante :

server.use(prerender.httpHeaders());

au dessus de

server.start();

Relancez ensuite le service Prerender.

Si on relance des requêtes sur l’URL contenant _escaped_fragment_, la première requête prendra toujours environ 1s, en revanche, pour les suivantes, le temps se situe entre 10ms et 20ms.

Note : Sans configuration spécifique, prerender-redis-cache utilise l’adresse localhost et le port par défaut, le tout sans authentification pour se connecter au serveur Redis.

Cas pratique

Malheureusement, les outils proposés par Google dans les Wemasters tools ne gèrent pas les _escaped_fragment_. Le site apparait donc comme vide.

Afin de tester, j’ai mis quelques liens vers http://aymeric.cloudapp.net/groingrek/index sur mon blog, Twitter, etc. Après quelques jours, les 2 pages du sites sont indexées et disponibles dans Google.

image

Informations complémentaires

L’article présente ici l’utilisation de Prerender avec Node.Js et Backbone.Js mais le fonctionnement est identique avec des frameworks comme Ember.js ou AngularJs.

Du côté de Prerender, d’autres modules ou fichiers de configuration sont disponibles pour Nginx, Ruby, Apache, etc.

[Microsoft] Feedbacks sur la garantie de la Surface RT

Depuis novembre 2012, j’ai une Surface RT de 32Go que j’utilise pour aller sur internet, mail, news, etc. Tout fonctionnait très bien jusqu’à la semaine derrière où une tâche jaunâtre est apparue dans la partie inferieure droite de l’écran (à côté du connecteur pour le chargeur).

 

Après quelques recherches sur mon moteur de recherche préféré, je constate que je ne suis pas le seul à avoir ce soucis (exemple ici). Certaines personnes ont constaté ce problème dès la réception de la Surface alors que d’autres comme moi seulement après plusieurs mois. Tous les feedbacks parlent de remplacement sans soucis par Microsoft.

 

Sur le site Microsoft de support dédié à la Surface (http://www.microsoft.com/surface/fr-fr/support/contact-us/), après avoir choisi le matériel et la catégorie concernée, les moyens diffèrents moyens de contact sont proposés.

 

image

 

Après la FAQ et le forum d’aide de la communauté, il est possible de contacter le support via téléphone ou chat.

 

J’ai choisi le chat et suis tombé presque instantanément sur une conseillère. Après exposition de mon problème, elle m’a directement proposé un échange standard.

 

2 solutions s’offrent alors à moi. La première consiste à imprimer une étiquette UPS et envoyer la tablette nue chez Microsoft. Une fois reçue, une nouvelle tablette est envoyée sous 5 à 10 jours.

 

Deuxième solution, plus rapide, une nouvelle tablette est envoyée directement. Dès réception de celle-ci, vous avez 14 jours pour renvoyer la tablette défectueuse dans la boite de la nouvelle Surface. En attendant la réception de la tablette défectueuse, Microsoft bloque le prix de la tablette sur votre compte. Une fois reçue, l’argent est débloqué.

 

J’ai opté pour la deuxième solution. J’ai contacté le support un jeudi midi, le lundi midi suivant j’avais ma nouvelle tablette.

 

Quelques jours après la réception de la nouvelle Surface, la personne avec qui j’ai été en contact m’a rappelé (plusieurs fois avant que je puisse décrocher) pour savoir si la nouvelle tablette fonctionnait correctement.

 

Seul bémol, le colis de la nouvelle tablette ne contient aucune information sur les modalités de retour. Il faut se souvenir des informations transmises par l’opérateur du support (délai de 14jours, prendre contact avec UPS, etc.).

 

On tape toujours les doigts sur les SAV, j’ai été agréablement surpris par celui de Microsoft.

[Debug] Skype et CPU élevé

Depuis quelques temps, j’ai remarqué que mon Skype consommait beaucoup de CPU alors qu’il était simplement démarré et connecté à mon compte.

 

On peut le voir sur le screenshot de Process Explorer ci-dessous.

 

image

 

Dans l’onglet “Threads”, on voit qu’un seul thread (TID = 8556) consomme la quasi totalité du CPU utilisé par Skype.

 

image

 

Après avoir pris plusieurs dumps avec ProcDump (Skype semble ne pas trop aimer être breaké par WinDbg, il lance un nombre incalculable d’access violation lors de la libération du processus).

 

Grâce à la commande “!runaway” (informations disponibles ici), on retrouve le même thread qui consomme du CPU.

 

0:051> !runaway
User Mode Time
  Thread       Time
  31:216c      0 days 0:10:27.763
  17:1214      0 days 0:00:16.130
   9:610       0 days 0:00:05.647
   0:1fd4      0 days 0:00:05.226
  35:758       0 days 0:00:01.232
  39:21e8      0 days 0:00:00.904
  38:1e64      0 days 0:00:00.764
  36:1714      0 days 0:00:00.202
  10:10cc      0 days 0:00:00.109
   5:1710      0 days 0:00:00.093
   2:21e4      0 days 0:00:00.093

 

Pour rappel, le TID est donné en hexadécimal avec “!runaway”, il faut le convertir pour retrouver le même TID que celui donné dans Process Explorer.

 

0:051> ? 216c 
Evaluate expression: 8556 = 0000216c

 

On change le thread courant sur la thread 31 (debugger shorthand ID : identifiant donné par le débuggeur au thread). On affiche ensuite la callstack du thread courant.

 

On peut constater que le module “Flash” apparait plusieurs fois sur la callstack. Malheureusement, Adobe ne fournit visiblement pas de symbôles publics pour Flash (malgrès les commentaires de cet article datant de 2010).

C’est pourquoi on aperçoit des offsets énormes sur la callstack.

 

0:051> ~31s

0:031> kb
ChildEBP RetAddr  Args to Child             
WARNING: Stack unwind information not available. Following frames may be wrong.
0af5e300 5e21efc2 14b52c18 000124f8 00000000 Flash+0xfe534
0af5e324 5e21ed3d 00000000 00000000 000003e8 Flash+0xfefc2
0af5e3b0 5e273a5e 1311e0b0 0af5ece8 00000000 Flash+0xfed3d
0af5ecc4 5e272808 0af5ece8 13133040 13133040 Flash+0x153a5e
0af5ed2c 5e31ff7a 13133040 13133040 0af5ed40 Flash+0x152808
0af5eda0 5e31c1b4 00000001 13133040 0aa541b0 Flash+0x1fff7a
0af5edd0 5e32001e 0aa541b0 00000000 00000001 Flash+0x1fc1b4
0af5edf0 5e3c7e13 1311e0b0 00000001 0aa541b0 Flash+0x20001e
0af5eef0 67583e9a 0aa5417c 00000001 00000000 Flash!DllUnregisterServer+0x365b3
0af5ef10 66dbdea5 0a8b3564 0a8b3050 0a8b3564 mshtml!memcpy+0xb98f0
0af5ef38 66f7b1ec 0a8b3050 0a9096b8 00000000 mshtml!CPaintController::NotifyPreRender+0x9e
0af5f1ec 66a3b61a 00008c80 00000000 0a8b3050 mshtml!CView::EnsureView+0x1e21
0af5f40c 66d3c65b 00000001 00000695 0a8b3050 mshtml!CDoc::PaintWorker+0x173
0af5f424 66a39b47 00000001 0a90b530 0abf85f8 mshtml!CDoc::PaintInPlace+0x29
0af5f444 66a36cb3 0090b530 002cedb6 00000000 mshtml!CPaintController::RunRenderingLoop+0x48
0af5f45c 66a366e6 0abf85f8 0abd08a8 00000000 mshtml!CPaintController::OnUpdateBeat+0x25
0af5f47c 66a36e1e 00000000 0abd0870 0a90b500 mshtml!CPaintBeat::OnBeat+0x17a
0af5f494 66a3631a 0a8b306c 00000000 0abd0870 mshtml!CPaintBeat::OnVSyncMethodCall+0x51
0af5f4ec 669f9c19 26772b77 00000000 0000000f mshtml!GlobalWndOnPaintPriorityMethodCall+0x179
0af5f538 756d77d8 002d04f6 000003a2 00000000 mshtml!GlobalWndProc+0xd3
0af5f564 756d90e7 669f9b45 002d04f6 0000000f user32!InternalCallWinProc+0x23
0af5f5e0 756d7b6f 669f9b45 669f9b45 00000000 user32!UserCallWinProcCheckWow+0xdb
0af5f644 756d7c44 038dcce0 00000000 0000000f user32!DispatchClientMessage+0x15d
0af5f680 772c2c92 0af5f698 00000000 0af5f6fc user32!__fnDWORD+0x2b
0af5f694 038dcce0 00000000 0000000f 00000000 ntdll!KiUserCallbackDispatcher+0x2e
0af5f70c 756d8a66 00000000 0000000f 0af5f78c 0x38dcce0
*** WARNING: Unable to verify checksum for C:\Program Files (x86)\Skype\Phone\Skype.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files (x86)\Skype\Phone\Skype.exe
0af5f71c 0254bc85 0af5f72c 00000a00 002d04f6 user32!DispatchMessageW+0x10
0af5f78c 0131c4b1 0af5f7a0 0131c4bb 0af5f7bc Skype+0x127bc85
0af5f7bc 012d70f6 0af5f810 012d6b58 0af5f7d0 Skype+0x4c4b1
0af5f7d0 74a9850d 01221570 0af5f820 772dbf39 Skype+0x70f6
0af5f7dc 772dbf39 01221570 374f47f3 00000000 KERNEL32!BaseThreadInitThunk+0xe
0af5f820 772dbf0c 012d70cc 01221570 ffffffff ntdll!__RtlUserThreadStart+0x72
0af5f838 00000000 012d70cc 01221570 00000000 ntdll!_RtlUserThreadStart+0x1b

 

Si on fait la même opération sur les 2 autres dumps pris, on retrouve également le module “Flash” dans la callstack du thread 31.

 

On peut donc fortement penser que Flash est responsable de la consommation CPU.

 

Après plusieurs tests, je me suis rendu compte que la consommation CPU n’était forte que lorsque Skype était sur sa page d’accueil (ci-dessous) qui contient des publicités.

 

image

 

Un simple clique-droit sur un de ces encarts montre que ces derniers sont en Flash.

image

 

On voit très nettement lorsque Skype est sur son écran d’accueil et lorsqu’on change pour afficher une conversation.

 

image