Trop souvent, une base de donnée contient des données avec des caractères blancs complètement inutiles. Par exemple un nom, au lieu de “Gabriel” on se retrouve avec “Gabriel “ ou “ Gabriel”. C’est très facile pallier à ce problème en effectuant un .Trim() tout bête juste avant la persistance des données. Cependant, ça devient vite facile de l’oublier.

C’est pourquoi un custom model binder peut pallier à cette problématique très rapidemment, et ce pour l’ensemble de l’application.

public class TrimModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context.Metadata.ModelType.GetTypeInfo().IsValueType ||
            (context.Metadata.ModelType == typeof(string)))
        {
            return new TrimModelBinder();
        }

        return null;
    }
}
public class TrimModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        //Get the value
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None)
        {
            // no entry
            return Task.CompletedTask;
        }

        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

        //Set the value, this has to match the property type.
        var typeConverter = TypeDescriptor.GetConverter(bindingContext.ModelType);
        var propValue = typeConverter.ConvertFromString(valueProviderResult.FirstValue.Trim());
        bindingContext.Result = ModelBindingResult.Success(propValue);
        return Task.CompletedTask;
    }
}

Finalement, il suffit d’aller ajouter le code suivant dans votre Startup.cs pour ajouter votre model binder à la liste des ModelBinderProviders du framework. Je le place à la première position, car je veux qu’il soit éxécuté le plus rapidemment possible.

services.Configure(options =>
{
    options.ModelBinderProviders.Insert(0, new TrimModelBinderProvider());
});

Dans le cadre d’un projet d’amélioration de la sécurité d’une application, j’ai du notamment passer l’ensemble des champs VARCHAR d’une base de donnée vers NVARCHAR. La raison est bien simple: protéger le contenu des injections XSS.

Il faut en outre que:

  • Le caractère  “>” devienne “>”
  • Le caractère “<” devienne “&lt;”
  • Le caractère  ” devienne &quot;

Pour ce faire, il suffit de générer une série d’instructions “ALTER TABLE” à partir de cette requête SQL:

SELECT 'ALTER TABLE ' + isnull(schema_name(syo.id), 'dbo') + '.[' +  syo.name +'] ' 
    + ' ALTER COLUMN [' + syc.name + '] NVARCHAR(' + case syc.length when -1 then 'MAX' 
        ELSE convert(nvarchar(10),syc.length) end + ') '+ 
        case  syc.isnullable when 1 then ' NULL' ELSE ' NOT NULL' END +';' 
   FROM sysobjects syo 
   JOIN syscolumns syc ON 
     syc.id = syo.id 
   JOIN systypes syt ON 
     syt.xtype = syc.xtype 
   WHERE 
     syt.name = 'varchar' 
    and syo.xtype='U'

Finalement, ce n’est qu’une partie des éléments à mettre en place pour sécuriser une application convenablement d’une possible faille XSS.

Les applications .NET Core agissent différemment des applications traditionnelles ASP .NET sur la stack Windows / IIS. En effet, l’application lorsque démarée, procède à des locks sur l’ensemble des fichiers utilisés.

J’entends par là:

  • les .DLL
  • Les .EXE
  • Les fichiers de logs

Cela rend donc impossible le déploiement. Si vous essayez, vous aurez sans doute cette problématique:

Deployment job started
Found 1 deployable artifacts.
Deploying Web application TestProject.Web
Downloading artifact package TestProject.Web.zip (20,523,009 bytes)
Execute before-deploy.ps1 script for TestProject.Web application
All file locks have been released!
Website 'testproject.com' already exists with 'testproject.com' app pool and root directory at 'F:\websites\custom\TestProject'
Updating website bindings to:
 - http *:80:testproject.com
Application path with expanded environment variables: F:\websites\custom\TestProject
Updating web site 'testproject.com' contents from Zip archive C:\Users\appveyor\AppData\Local\Temp\1\2hylwzwk.w4o\TestProject.Web.zip
Info: Updating file (testproject.com\before-deploy.ps1).
Info: Updating file (testproject.com\refs\TestProject.Web.exe).
Info: Updating file (testproject.com\TestProject.Application.dll).
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 1 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 2 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 3 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 4 of 5.
Warning: An error was encountered when processing operation 'Create File' on 'TestProject.Application.dll'. 
Retrying operation 'Update' on object filePath (testproject.com\TestProject.Application.dll). Attempt 5 of 5.
Web Deploy cannot modify the file

Pour résoudre le problème, il faut créer un fichier “before-deploy.ps1” directement dans le root du projet Web et mettre le code suivant:

Restart-WebAppPool "testproject.com"
Write-Host "All file locks have been released!"

Pas de craintes, Restart-WebAppPool termine les requêtes courantes avant de redémarrer l’application pool.

Finalement, ne surtout pas oublier de l’inclure dans votre project.json:

"publishOptions": {
    "include": [
      "before-deploy.ps1"
    ]
  }