Language preference:

Phase 2 / InjectionSecurityAttribute

Web applications are often attacked by hackers, who use SQL Injection and Cross Site Scripting attacks to hurt you and your site visitors. SQL Injection uses SQL syntax to attack your database while Cross Site Scripting adds javascript and HTML where it does not belong, to trip up your site's visitors.

If you've studied these issues, you know that its important to clean up incoming data to neutralize these attacks. This action is handled by BLD. But you can do better by detecting attacks with the InjectionSecurityAttribute because you need flexibility enough to allow certain inputs that blind attack prevention would otherwise stop. For example, you may want to allow some HTML tags in a string-type DataField. You also may want to allow the user to enter some of the SQL keywords along with a single quote. Both are blocked by the traditional protection mechanisms.

Detection has these benefits:

  • Notify you when an attack is happening.
  • Log the details of attacks for further study.
  • Shut the hacker out of your site.
  • Block garbage data from going into your database.

The InjectionSecurityAttribute requires that you have setup the Peter's Input Security module, which is part of the Peter's Data Entry Suite.

Add it to the Entity class or individual string-type properties. Those string-type properties without their own InjectionSecurityAttribute will inherit the one of the Entity class definition. Typically the Entity class's attribute is the most strict. The individual properties will reduce security to allow more cases. For example, you may want a string to allow a specific list of HTML tags, and use InjectionSecurityAttribute to block the rest and any javascript found inside the HTML tags you allow (a common hack).

Here are InjectionSecurityAttributes associated with the Category Entity class.

[DESDA.InjectionSecurity(DetectScriptInjection=true, DetectSQLInjection=false)]  // impacts CategoryName, but not Description which has its own rules
public class CategoryMetadata
{
   [DESDA.InjectionSecurity(DetectScriptInjection=true, DetectSQLInjection=true, 
      SQLDetectionLevel=PeterBlum.DES.Web.SQLDetectionLevel.MediumLow, 
      HTMLTagMode=PeterBlum.DES.Web.HTMLTagMode.IllegalExceptTags,
      HTMLTags="br|img|span|div|a")]
   public object Description { get; set; }

}

The Source Code Browser shows completed DataAnnotations. The InjectionSecurityAttributes have been highlighted.

In the next topic, you'll learn how to add custom columns that show calculated values.



Open the Source Code Browser (C# only)
Source Code Browser
 
/* ------------------------------------------------
 * Describes the Entity class for: Category
 * Classes:
 *    Category - Entity class. Edit it for validation and to customize metadata at runtime
 *    CategoryMetadata - Entity Metadata class. It contains the DataAnnotations.
 *    CategoryDAO - BLD DataAccessObject format version of a Data Access Object.

 *    
 * Requires .net 4.0 and these references:
 *    System.ComponentModel.DataAnnotations
 *    PeterBlum.DES
 *    PeterBlum.DES.DataAnnotations
 * Generated: 7/8/2011 4:17:08 PM
 * ------------------------------------------------*/
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using PeterBlum.DES.DAO;
using PeterBlum.DES.DAO.Attributes;
using PeterBlum.DES.DAO.EntityDAO;
using PeterBlum.DES.DataAnnotations;

// Some members of this namespace have identical names to those in System.ComponentModel.DataAnnotations
// making it easy to switch from one to the other by adding the "DESDA." prefix
using DESDA = PeterBlum.DES.DataAnnotations;

namespace PeterBlum.WithDataAnnotations
{
   // --- ENTITY CLASS --------------------------------------------------
   /// <summary>
   /// Entity class. Edit it for validation and to customize metadata at runtime
   /// </summary>
   [EntityDAOType(typeof(CategoryDAO))]
   [MetadataType(typeof(CategoryMetadata))]
   public partial class Category
   {
/// <summary>
/// Associated with the CustomValidationAttribute on the Category.CategoryName property,
/// this uses LINQ to SQL to detect an existing Category with the same name
/// and reports it as an error.
/// </summary>
/// <remarks>
/// <para>You probably will create many of these methods. Consider using stored procedures
/// to search the database for duplicates. Write your code below to invoke the stored procedures.</para>
/// </remarks>
/// <param name="pNewName"></param>
/// <param name="pValidationContext"></param>
/// <returns></returns>
      public ValidationResult CheckForDuplicateCategoryNames(
         string pNewName, ValidationContext pValidationContext)
      {
         BaseDAOChangeEntityActionArgs vArgs = (BaseDAOChangeEntityActionArgs)pValidationContext.GetChangeEntityActionArgs();

         ChangeEntityAction vAction = vArgs != null ? vArgs.Action : ChangeEntityAction.Update;
         if ((vAction == ChangeEntityAction.Insert) || (vAction == ChangeEntityAction.Update))
         {
            Category vCategory = (Category)vArgs.Entity;
            int vThisCategoryID = (vAction == ChangeEntityAction.Insert) ? -1 : vCategory.CategoryID;
            NorthWindDataContext vDataContext = new NorthWindDataContext();
            System.Data.Linq.Table<Category> vTable = vDataContext.Categories;
            if (vTable.FirstOrDefault<Category>(category =>
               (String.Compare(category.CategoryName, pNewName, StringComparison.CurrentCultureIgnoreCase) == 0)
               && (category.CategoryID != vThisCategoryID)) != null)
            {
               DESDA.EntityValidationResult vResult = new DESDA.EntityValidationResult(
                  "CheckForDuplicateCategoryNames", // this is the Source parameter. It can be anything. It is used by the BLDPageManager.UpdateErrorMessage event. So make it useful for detecting this particular error
                  "This name already exists. Choose another.",
                  typeof(Category), "CategoryName", pNewName);
               vResult.SummaryErrorMessage = "{LABEL} already exists. Choose another.";
               return vResult;
            }

         }
         return ValidationResult.Success;
      }
   }  // class Category

   // --- ENTITY METADATA --------------------------------------------------
   /// <summary>
   /// Entity Metadata class.
   /// Companion to the Category Entity class that contains the DataAnnotations
   /// on properties with the same names as those in the actual Entity class.
   /// These properties do not require their types to match those in the Entity class.
   /// An Entity Metadata class allows the Entity class to be regenerated without
   /// overwriting DataAnnotations.
   /// </summary>
   [DESDA.InjectionSecurity(DetectScriptInjection=true, DetectSQLInjection=false)]  // impacts CategoryName, but not Description which has its own rules
   [DESDA.TableRestriction("Admin", DESDA.DenyAccess.None)]
   [DESDA.TableRestriction("Customer", DESDA.DenyAccess.Edit | DESDA.DenyAccess.Delete | DESDA.DenyAccess.Insert)]
   public class CategoryMetadata
   {

      [DESDA.Required()]
      [DESDA.CustomValidation(MethodName="CheckForDuplicateCategoryNames")]
      [DESDA.DisplayName("Name")]
      [DESDA.Filter(AutoGeneratePriority=DESDA.AutoGeneratePriority.Always, InMultiFieldSearch=true)]
      public object CategoryName { get; set; }
   
   /// <summary>
   /// In this example, explicitly make this a Multiline text element
   /// so it benefits from the MultilineText_Edit.ascx Field Template.
   /// Like most large textual fields, it should detect illegal hacking case.
   /// When the Peter's Input Security module is setup, the InjectionSecurityAttribute
   /// will block unwanted input.
   /// </summary>
      [DESDA.DataType(DataType.MultilineText)]
      [DESDA.Filter(InMultiFieldSearch=true)]
      [DESDA.InjectionSecurity(DetectScriptInjection=true, DetectSQLInjection=true, 
         SQLDetectionLevel=PeterBlum.DES.Web.SQLDetectionLevel.MediumLow, 
         HTMLTagMode=PeterBlum.DES.Web.HTMLTagMode.IllegalExceptTags,
         HTMLTags="br|img|span|div|a")]
      public object Description { get; set; }

      [DESDA.DbImageDataType(BadFormatErrorMessage="Bad format", ErrorMessage="Must be {EXTENSION}", SupportedFileTypes="jpg")]
      public object Picture { get; set; }

   }  // class CategoryMetadata

   // --- BLD DATAACCESSOBJECT  --------------------------------------------------
   /// <summary>
   /// BLD DataAccessObject class for the Category Entity class.
   /// It provides CRUD actions. The parent class already has default
   /// methods for Update(), Insert(), Delete() and several queries.
   /// </summary>
   /// <remarks>
   /// <para>For documentation, see the BLD DataAccessObject section of the Business Logic User's Guide.</para>
   /// </remarks>
   [TableName("Categories")]
   public class CategoryDAO : LINQtoSQLEntityDAO<Category>
   {
      public CategoryDAO() : base(typeof(NorthWindDataContext)) { }
      public CategoryDAO(object pDataContext) : base(pDataContext) { }

   }  // class CategoryDAO
}