Archives par étiquette : WPF

unsecure

Le bug de conception de la PasswordBox

C’est en utilisant la PasswordBox récemment (et en réfléchissant un peu plus que d’habitude) que j’ai réalisé ce que contrôle avait un bug de conception lié à la sécurité.

En WPF, le contrôle expose deux propriétés liées à la gestion du mot de passe.

La propriété SecurePassword est une propriété en readonly qui renvoie le mot de passe stocké par la PasswordBox sous la forme d’une SecureString.

Le type SecureString est un type intéressant qui permet de stocker en mémoire une string de manière sécurisée. Cette chaine de caractères est en effet cryptée en mémoire via DPAPI et ne peut donc être lue en clair malgré le fait qu’elle réside en mémoire.

Pour définir la valeur d’une SecureString il est nécessaire de le remplir caractère par caractère grâce à la méthode AppendChar, et pour lire en clair la valeur stockée par une SecureString on doit allouer de la mémoire et y stocker la valeur en clair de la SecureString via un Marshal.SecureStringToBSTR (qui au passage ne respecte pas les conventions de nommage .net). Une fois lue, on peut ensuite faire un reset la zone mémoire qui contient la valeur en clair via la méthode ZeroFreeBSTR (qui ne respecte toujours pas les conventions de nommage .net).

On réduit ainsi fortement l’intervalle de temps où la chaine de caractères, dans notre cas le mot de passe, est en clair en mémoire.

La PasswordBox utilisant une SecureString en interne on pourrait donc se dire que ce contrôle est parfaitement sécurisé… si l’on évoquait pas la seconde propriété dédiée à la gestion du mot de passe : la propriété Password.

Cette propriété de type String permet de définir et récupérer le mot de passe stocké par la PasswordBox. Et qui dit type String dit, lisible comme de l’eau de roche via n’importe quel outil de type Snoop et consors :

Donc en résumé, la propriété Password devrait en réalité se nommer “UnSecurePassword” et la propriété SecurePassword est inutile. Pourquoi en effet stocker le mot de passe de manière sécurisé si on l’expose en même temps en clair ?

Vous allez me répondre que c’est simple, il faut bien que l’on soit capable de définir la valeur de la PasswordBox via une propriété qui ne soit pas en Readonly. Certes, mais en a-t-on vraiment besoin ?

Le contrôle n’a pour objectif que d’afficher des ●●●●●● et on se moque donc de savoir si les ●●●●●● représente le vrai mot de passe ou en réalité de simples espaces. La classe SecureString exposant une propriété Length on pourrait facilement avoir une représentation visuelle du mot de passe sans avoir à le stocker en clair en mémoire.

On disposerait ainsi d’un contrôle PasswordBox vraiment sécurisé et non pas d’une sécurité en carton.

Quand à l’implémentation d’un tel SecurePasswordBox, il pourrait bien faire l’objet d’un prochain article…

Faites vraiment attention avec les BitmapEffect

Lors d’une session de profiling CPU effectué durant une session de test de l’application
sous TSE, je me suis rendu compte que l’application sur certaines sessions utilisateurs
essayait de pomper 100% du CPU. Et avec grande surprise, quand j’ai demandé aux testeurs
s’ils effectuaient des traitements longs et couteux, certains m’ont indiqué que non
pas du tout, ils étaient juste devant une fenêtre qui affichait une petite grille
avec quelques lignes.

Après vérification, la fenêtre était très simple, et lorsque l’on ouvre ou ferme la
fenêtre le CPU passe d’aux alentours de 0% à 100% alors qu’aucun traitement n’est
censé se dérouler. Bien évidemment le problème n’a pas été détecté jusqu’à présent
puisqu’utilisé ailleurs que dans une session TSE, ce comportement n’est pas reproductible.

On passe donc doucement aux choses sérieuses, et ne connaissant pas encore correctement
l’application sur laquelle je travaille, je lance Snoop afin de trouver le nom de
la fenêtre incriminée et je commence mes premiers tests afin d’affiner ma réflexion
et mieux cibler les potentielles sources de problèmes.

Snoop est un utilitaire gratuit disponible
sur CodePlex
qui permet de parcourir agréablement l’arbre de contrôle de n’importe
quelle application WPF. Il permet, de plus, de modifier les propriétés de ces contrôles
afin de voir l’impact que cela peut avoir sur l’interface… et/ou le comportement de
l’application. Il s’agit d’un de mes outils préférés de debug d’applications WPF.

Et première piste, je me rends compte que si je collapse un des usercontrol présent
dans la fenêtre, le CPU continue d’être à un haut niveau mais baisse de manière assez
notable.

Rendez-vous donc dans le code de ce usercontrol, je me rends compte qu’il y a un backgroundworker
qui est utilisé, que beaucoup d’évènements sont déclenchés, mais pas de trace flagrante
d’un traitement bloquant.

Afin d’analyser plus finement le comportement de ce background worker, et de ces évènements
qui me semblent à première vue potentiellement louches, je me lance donc dans une
session de profiling CPU afin de pouvoir rapidement détecter le point qui pose problème.
J’utilise pour cela le profiler de Visual Studio 2008 Team Suite, et je compare les
résultats avec les résultats proposés par dotTrace.

Après avoir “nettoyé” plusieurs points ennuyeux qui parasitaient ma lecture du rapport
du profiler (essentiellement des appels de ressources I/O, réseau, etc. effectués
en synchrone), je commence à faire une grosse grimace en me rendant compte que le
CPU passe beaucoup de temps non pas à exécuter du code managé (ce qui m’aurait permis
de comprendre simplement où était l’erreur de développement) mais le passe à exécuter
du natif. Bref, ça sent vraiment pas bon, et d’un potentiel problème de développement
je me retrouve sur un problème qui risque d’être beaucoup plus compliqué à résoudre.

Etant dans une application WPF, il y a de bonnes chances que ce temps passé à exécuter
du natif soit du temps passé à exécuter du code lié à l’affichage. Je retourne donc
au sein de Visual Studio afin d’analyser le XAML du usercontrol que j’avais “spotté”
via Snoop et rien ne me choque, je remonte donc l’arbre de contrôle pour essayer de
trouver une piste, et je me rends compte que la fenêtre qui contient le usercontrol
n’a pas de bordure et à l’AllowTransparency d’activé. Ce sont des éléments potentiellement
couteux mais même désactivés le CPU ne souhaite toujours pas prendre de repos.

EasyDropShadowExamplesAlors
qu’une simple suppression du DropShadowEffect qui a été placé à l’intérieur de cette
fenêtre permet de passer d’une consommation de 100% à quelques % ! Et je dois bien
avouer que ce fut pour moi une vraie surprise, tout le monde sait que les
BitmapEffect sont lents et couteux comme je vous l’indiquais il y a presque 3 ans
,
mais de là à prendre 100% du CPU il y a quand même un pas… que WPF a franchi dans
certaines conditions sous TSE !

Conclusion de ce post mortem de séance de tests : Faites donc particulièrement attention
à l’utilisation des BitmapEffects si votre application doit être utilisé via TSE !

image

TextChanged… ou pas (encore)…

C’est dès la première semaine où je suis arrivé chez mon nouvel employeur que j’ai
pu m’exclamer en indiquant que “Rah purée, c’est pas vrai, c’est un bug de WPF…”

Je n’ai pas vu de sourire moqueur de mes collègues mais je suis à peu près sûr que
certains d’entre eux ont dû se dire qu’il était osé de rejeter la faute du bug que
j’avais introduit sur la technologie elle-même et non pas sur une erreur d’inattention.
Et quelques recherches plus tard, je me suis rendu compte… que j’avais bien raison
!

Le scénario est pourtant très simple, il s’agit tout simplement de modifier le texte
d’une TextBox. La particularité de cette modification est que je l’ai réalisé dans
l’évènement TextChanged comme ceci :

image

Je m’attendais à voir le message “Je suis vide” après un click sur mon bouton et bien…
non, ma textbox auparavant remplie devient vide. Et pourtant je rentre bien dans ma
condition et si je mets un espion sur la valeur de la propriété Text elle est bien
définie avec ma chaine. Seul l’affichage n’est pas impacté. Bizarre non ?

Et bien je me suis conforté dans l’idée qu’il s’agissait d’un bug, lorsque je n’ai
pas réussi à reproduire le soucis dans une application .net 4.0. En effet si l’on
fait le test dans VS 2010 et que l’on change de version de framework on se retrouve
avec l’effet désiré ou non !

[Update] Je vous propose ci-dessous le projet de test afin que vous puissiez changer
la version du framework et voir le changement de comportement :

<br />

image

WPF et Option Strict

Débutant une formation WPF la semaine dernière chez un client, je me suis retrouvé
sur un problème intéressant : lors de la création de projets WPF, le code généré par
Visual Studio ne compilait pas. Inutile de vous dire qu’il s’agit du genre d’incidents
qui jette un rapide discrédit sur la technologie ce qui n’est évident à gérer en début
de formation…

Le problème est lié à l’utilisation du langage VB et de la directive Option Strict.
Si vous êtes un développeur VB sérieux et rigoureux vous avez très probablement activé
l’option strict afin d’éviter d’autoriser le compilateur à ne pas signaler les opérations
de cast plutôt hazardeuses. Seul problème, le code généré par les templates de projet
de WPF n’est pas compatible avec cette vérification du compilateur si vous utilisez
Visual Studio 2008 RTM :

image 

En effet si vous éditez le fichier MyWpfExtensions.vb présent au sein de votre projet
vous trouverez la ligne de code fautive :

Friend ReadOnly Property Application() As Application
    Get
        Return Global.System.Windows.Application.Current
    End Get
End Property

Il est nécessaire de modifier le code du getter afin d’ajouter un cast explicite afin
que le code compile correctement :

Friend ReadOnly Property Application() As Application
      Get
          Return CType(Global.System.Windows.Application.Current, Application)
      End Get
End Property

La solution pour résoudre ce problème est simple : il vous faut installer
le Service Pack 1 de Visual Studio 2008
.

Autre solution si avez un besoin très urgent du correctif, vous pouvez directement télécharger
les templates mis à jour sur cet article de la KB de Microsoft
.