Pages

Friday, March 30, 2012

My Captcha Image

This is a work in progress, and for my furthering my notes. Your comments are welcome.

I decide to code my personal Captcha Image. My Captcha idea is new, which probably can be found somewhere on the internet, however I tweaked the idea into a .NET Class (CaptchaDisplay class within my Captcha namespace)..

This Captcha class is instantiated from two areas. It is instantiated within an User control and a Generic Handler.

The User control’s code behind handles generating the Captcha characters, while the Generic Handler handles creating the Image from characters passed it through a query string. The characters are generated from the User Control.

I added an external appSettings page to the Web.config:

<appSettings file="appSettings.config"/>

Within the appSettings.config file:

<?xml version="1.0"?>
<appSettings>
<add key="Tumblers" value="j(mW*)UYRvxQ$4^54hT" />
</appSettings>
 
The key attribute – Tumblers - is used in the Captcha encryption. Tumblers is synonymous with a Key Lock mechanism. Tumblers wouldn’t be shown and are invisible. The Lock and Key can be found by the visitor, however to unlock or to be verified all three must fit or equal to the Captcha characters generated.

Download myCaptcha code here

The User Control is shown below:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CaptchaControl.ascx.cs"
    Inherits="CaptchaHandler.CaptchaControl" %>
 
<asp:HiddenField ID="captchaKey" ClientIDMode="Static" runat="server" />
<asp:Panel ID="Captcha" ClientIDMode="Static" runat="server">
</asp:Panel>

and its Code Behind page:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using deDogs.Captcha;
 
namespace CaptchaHandler
{
    public partial class CaptchaControl : System.Web.UI.UserControl
    {
        private int _width;
 
        public int Width
        {
            set
            {
                _width = value;
            }
        }
 
        protected void Page_Load(object sender, EventArgs e)
        {
            using (CaptchaDisplay captcha = new CaptchaDisplay(_width))
            {
                captcha.GetCharacters();
                string[] c = captcha.characters;
                string key =new string(captcha.SaltKey(c));
                string encodeKey =  HttpUtility.UrlEncode(key);
                captchaKey.Value = encodeKey;
 
                Image img = new Image();
                img.ImageUrl = "Captcha.ashx?key=" + encodeKey + "&w=" + _width;
                img.AlternateText = "Captcha Image";
                Captcha.Controls.Add(img);
            }
        }
    }
}
Generic Handler C# Code is shown below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using deDogs.Captcha;
using System.Drawing.Imaging;
 
namespace CaptchaHandler
{
    /// <summary>
    /// Display Captcha Characters Image
    /// </summary>
    public class CaptchaHandler : IHttpHandler
    {
 
        public void ProcessRequest(HttpContext context)
        {
            HttpContext.Current.Response.ContentType = "image/jpeg";
            HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            HttpContext.Current.Response.BufferOutput = false;
            try
            {
                Int32 width = Convert.ToInt32(HttpContext.Current.Request["w"]);
 
                if (width > 0) {
                    using (CaptchaDisplay captcha = new CaptchaDisplay(width))
                    {
                        try
                        {
                            string key = HttpUtility.UrlDecode(HttpContext.Current.Request["key"].ToString());
 
                            if (key != "")
                            {
                                //Decrypt key
                                char[] unSalted = captcha.SaltKey(key.ToCharArray());
                                captcha.characters = unSalted.SelectMany(x => new string[] { x.ToString() }).ToArray();
                                captcha.CaptchaImage();
                                captcha._bitMap.Save(HttpContext.Current.Response.OutputStream, ImageFormat.Jpeg);
                            }
                        }
                        catch (Exception)
                        {
 
                            throw;
                        }
                    }
                }
            }
            catch (Exception)
            {
                
                throw;
            }
 
        }
 
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

Finally the Captch class shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;
using deDogs.Validation;
using System.Configuration;
using System.Web.SessionState;
namespace deDogs
{
    namespace Captcha
    {
        public class CaptchaDisplay : IDisposable
        {
            public CaptchaDisplay(int width)
            {
                this.fonts = new string[] { "Times New Roman", "Georgia", "Arial", "Comic Sans MS" };
                this.sizes = new int[] { 36, 48, 60, 72 };
                this.characterSet = @"ABDEFGHJKLMNQRTYZabdefghkmnqryz23456789*#@%$";
                this.colr = new Brush[] { Brushes.Black };
                this.backgroundColor = Brushes.White;
                this.salt = new char[] { 'g', '-', '$', 'k', '5', '?', 'O' };
                this._bitMap = new Bitmap(width, (int)(width * .32F));
            }
            public CaptchaDisplay() { }
 
            protected class CaptchaChars
            {
                public CaptchaChars() { }
 
                public CaptchaChars(string character, CaptchaRegion region)
                {
                    this.character = character;
                    this.region = region;
                    this.memoryStream = new MemoryStream();
 
                }
                public MemoryStream memoryStream { get; set; }
                public string character;
                public CaptchaRegion region;
                public int angle { get; set; }
            }
            protected class CaptchaRegion
            {
                public CaptchaRegion() { }
                public CaptchaRegion(float x, float y, float width, float height)
                {
                    this.x = x;
                    this.y = y;
                    this.width = width;
                    this.height = height;
                }
                public float x { get; set; }
                public float y { get; set; }
                public float width { get; set; }
                public float height { get; set; }
            }
            public Bitmap _bitMap;
 
            Brush color(Random rnd)
            {
                return colr[rnd.Next(0, colr.Length)];
            }
            PinTumblerLock pinTumblerLock;
 
            public char[] salt { get; private set; }
            public string[] characters;
            public string characterSet;
 
            public string[] fonts { get; set; }
            public int[] sizes { get; set; }
            public Brush[] colr;
            public Brush backgroundColor;
 
            public PinTumblerLock LockKey
            {
                get
                {
                    return pinTumblerLock;
                }
                set
                {
                    pinTumblerLock = value;
                }
            }
            public void GetCharacters()
            {
                Random rnd = new Random();
                int numChars = rnd.Next(4, 7);
                this.characters = new string[numChars];
 
                for (var i = 0; i < numChars; i++)
                {
                    this.characters[i] = this.characterSet.Substring(rnd.Next(0, this.characterSet.Length), 1);
                }
            }
 
            public void CaptchaImage()
            {
                Random rnd = new Random();
 
                CaptchaChars captchaChar;
                List<CaptchaChars> captchaChars = new List<CaptchaChars>();
 
                CharacterRange[] cR = { new CharacterRange(0, 1) };
 
                StringFormatFlags flags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
                StringFormat sF = new StringFormat(flags);
                sF.SetMeasurableCharacterRanges(cR);
 
                RectangleF displayRectangleF = new RectangleF(0, 0, this._bitMap.Width, this._bitMap.Height);
 
                Font largeFont;
 
                int width = 80, height = 80;
 
                for (var c = 0; c < characters.Length; c++)
                {
                    HttpContext.Current.Trace.Write(characters[c]);
 
                    int angle = rnd.Next(0, 30);
                    angle = (angle % 2 == 0) ? -angle : angle;
 
                    captchaChar = new CaptchaChars(characters[c], new CaptchaRegion(0, 0, width, height));
                    captchaChar.angle = angle;
 
                    using (Bitmap bitMap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
                    {
                        using (Graphics g = Graphics.FromImage(bitMap))
                        {
                            Rectangle rect = new Rectangle(new Point(0, 0), new Size(bitMap.Width, bitMap.Height));
                            g.FillRectangle(this.backgroundColor, rect);
 
                            largeFont = new Font(this.fonts[rnd.Next(0, this.fonts.Length)], this.sizes[rnd.Next(0, this.sizes.Length)], GraphicsUnit.Pixel);
                            Region[] charRegion = g.MeasureCharacterRanges(characters[c], largeFont, displayRectangleF, sF);
                            g.DrawString(characters[c], largeFont, colr[rnd.Next(0, colr.Length)], displayRectangleF);
 
                            bitMap.Save(captchaChar.memoryStream, ImageFormat.Jpeg);
                            captchaChars.Add(captchaChar);
                        }
                    }
                }
 
 
                this.Display(captchaChars);
 
            }
 
            void Display(List<CaptchaChars> c)
            {
                using (Graphics mainGraphic = Graphics.FromImage(this._bitMap))
                {
                    StringBuilder charSet = new StringBuilder();
                    Rectangle rect = new Rectangle(new Point(0, 0), new Size(this._bitMap.Width, this._bitMap.Height));
                    mainGraphic.FillRectangle(this.backgroundColor, rect);
 
                    int newX = 0;
                    float bwidth, bheight = 0;
                    foreach (CaptchaChars m in c)
                    {
                        bwidth = m.region.width;
                        bheight = m.region.height;
                        Bitmap bitMap = new Bitmap(m.memoryStream);
                        using (Graphics g = Graphics.FromImage(bitMap))
                        {
                            Rotate(m, g);
                            g.DrawImage(bitMap, 0, 0);
                            mainGraphic.DrawImage(bitMap, newX, 0);
                            newX += (int)m.region.width;
                            charSet.Append(m.character);
                        }
 
                    }
                    //Crop image bounding selected characters
                    this._bitMap = this._bitMap.Clone(new Rectangle(0, 0, newX, (int)bheight), PixelFormat.Format32bppArgb);
                    Lockup(charSet.ToString());
 
                    Distort();
                    Decorate();
                }
            }
 
            private void Lockup(string key)
            {
                PinTumblerLock lockup = new PinTumblerLock(key, (ConfigurationManager.AppSettings["Tumblers"]));
            }
 
            public char[] SaltKey(char[] key)
            {
                int len = key.Length;
 
                for (int i = 0; i < len; i++)
                {
                    key[i] ^= this.salt[i];
                }
                return key;
 
            }
            public char[] SaltKey(string[] key)
            {
                char[] c = string.Join(string.Empty, key).ToCharArray();
                return this.SaltKey(c);
            }
 
            void Rotate(CaptchaChars c, Graphics g)
            {
                g.TranslateTransform(c.region.width / 2, c.region.height / 2);
                g.RotateTransform(c.angle);
                g.TranslateTransform(-c.region.width / 2, -c.region.height / 2);
            }
 
            protected void Distort()
            {
                Random rnd = new Random();
                double distort = rnd.Next(5, 20) * (rnd.Next(10) == 1 ? 1 : -1);
 
                int width = this._bitMap.Width;
                int height = this._bitMap.Height;
                using (Bitmap copy = (Bitmap)this._bitMap.Clone())
                {
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            // Adds a simple wave
                            int newX = (int)(x + (distort * Math.Sin(Math.PI * y / 84.0)));
                            int newY = (int)(y + (distort * Math.Cos(Math.PI * x / 44.0)));
                            if (newX < 0 || newX >= width) newX = 0;
                            if (newY < 0 || newY >= height) newY = 0;
                            this._bitMap.SetPixel(x, y, copy.GetPixel(newX, newY));
                        }
                    }
                }
            }
            protected void Decorate()
            {
                using (Graphics g = Graphics.FromImage(this._bitMap))
                {
                    Random rnd = new Random();
                    int width = this._bitMap.Width;
                    int height = this._bitMap.Height;
 
                    // Draw lines between original points to screen.
                    g.DrawCurve(new Pen(color(rnd), 3.0F), Points(5, rnd).ToArray());
 
                    foreach (Point pt in Points(5, rnd))
                    {
                        g.DrawRectangle(new Pen(color(rnd), 3.0F), new Rectangle(pt, new Size(rnd.Next(5, 20), rnd.Next(5, 20))));
                    }
                }
            }
 
            protected List<Point> Points(int pts, Random rnd)
            {
                List<Point> points = new List<Point>();
                int width = this._bitMap.Width - 20;
                int height = this._bitMap.Height - 20;
 
                for (int i = 0; i < pts; i++)
                {
                    points.Add(new Point(rnd.Next(0, width), rnd.Next(0, height)));
                }
                return points;
            }
 
            public void Dispose()
            {
                this._bitMap.Dispose();
                this._bitMap = null;
            }
        }
    }
}

Monday, February 20, 2012

Regular Expressions–Removing Script tags

I have been working with the regular expression:

<+script.*?.*<\/script>


It seem to do a good job removing script tags.


Wondering if anyone knows if there is someway someone can bypass this script? Would this remove all harmful script? I have been testing, which the script tag arrangement that I tested. this regular expression successfully removed the script.


However there are a lot of different combinations, and I am wondering if anyone know a script tag combination this regular expression would fail?

Saturday, February 11, 2012

ASP.Net Forms Authentication Notes

Validates Client side validation on the server:
  • Page.Validate();
  • Page.IsValid
Forms Authentication Class – Static Methods:
Checks supplied Username and Password against those stored in the
web.config file. Returning a Boolean value indicating whether a match was found.
--if (FormsAuthentication.Authenticate(Username,Password))
--else ERROR
-- FormsAuthentication.RedirectFromLoginPage(Username, false);
  • Creates an authentication ticket for the user.
  • Encrypts the information from the authentication ticket.
  • Creates a cookie to persist the encrypted ticket information.
  • Adds the cookie to the HTTP response, sending it to the client.
  • Redirects the user to the originally requested page (which is contained in the query string
    parameter of the login page request’s URL).
Loggin Out

FormsAuthentication.SignOut(); //Removes authentication cookie
FormsAuthentication.RedirectToLoginPage(); //sends user back to the login page.

Hashing Passwords in web.config

In the <credentials /> configuration section of the <forms /> element specified in the passwordFormat attribute with valid values:.
-- Clear, MD5, SHA1

Creating a hashed password:

Using System.Web.Configuration;

FormsAuthentication.HashPasswordForStoringInConfigFile(Password,”SHA1”);

Modifying the <credentials /> section:

Configuration config = WebConfigurationManager.OpenWebConfiguration(“~/”);

ConfigurationSectionGroup csg = config.SectionGroups[“system.web”];

AuthenticationSection as = (AuthenticationSection ) ccs.Sections[“authentication”];

as.Forms.Credentials.Users.Add(new FormsAuthentionUser(Username, Password);

config.Save();
===================================================

Cooklieles Forms Authentication
Option Description:
  • UseCookies
    Forces the runtime to use cookies when working with forms authentication.
    This requires the client browser to support cookies. If the browser does
    not support cookies, forms authentication will simply not work with that
    setting activated. As it will never receive a valid authentication cookie from
    the browser, ASP.NET redirects back to the login page over and over again,
    and you end up in an endless loop of presented login pages.
  • UseUri
    If this configuration option is selected, cookies will not be used for authentication.
    Instead, the runtime encodes the forms authentication ticket into
    the request URL, and the infrastructure processes this specific portion
    of the URL for establishing the security context.
  • AutoDetect
    Results in the use of cookies if the client browser supports cookies. Otherwise,
    URL encoding of the ticket will be used. This is established through a
    probing mechanism.
  • UseDeviceProfile
    Results in the use of cookies or URL encoding based on a device profile configuration
    stored on the web server. These profiles are stored in .browser
    files in the <drive>:\<windows directory>\Microsoft.NET\Framework\
    v2.0.50215\CONFIG\Browsers directory.

Source: Asp.Net 3.5 in C# Second Edition – Apress Pro

Persistent Cookies
Setting persistent cookies:

FormsAuthentication.RedirectFromLoginPage(Username,true);

Stay alive till FormsAuthentication.SignOut() method is called.
Setting a specified cookie duration have to be set explicitly:

if FormsAuthentication.Authenticate(UserName, Password);

HttpCookie c = FormsAuthentication.GetAuthCookie(Username,true);

c.Expires = DateTime.Now.AddDays(10);

Response.Redirect(FormsAuthentication.GetRedirectUrl(Username,true));

Creating the Data Store

Setting up the membership API database tables, you can execute the following command:
aspnet_regsql -S (local)\SQLEXPRESS -E -A all -d MyDatabase

Switch Description
-S
servername Specifies the SQL Server and instance for which you want to install the
ASP.NET database tables. You can use SQL Server 7.0 or newer as an underlying
storage for the membership API.
-U username The SQL Server database user with which you want to connect to SQL Server.
This is required if you do not want to use Windows authentication to connect
only to SQL Server.
-P password If the -U switch is specified, you need to specify the password switch as well.
This is required if you do not want to use Windows authentication to connect
only to SQL Server.
-E If you don’t specify -U and -P, you automatically connect through Windows
authentication to the SQL Server instance specified in -S. With -E, you can explicitly
specify to connect through Windows authentication to the SQL Server.
-C Allows you to specify a full-fledged ODBC or OLEDB connection string for
connecting to the database.
-sqlexportonly Creates the SQL scripts for adding or removing the specified features to the
database without installing them on a dedicated SQL Server instance.
-A Installs application services. The valid options for this switch are all, m, r, p, c,
and w. The command in the previous example used the option all for installing
all application services; m is dedicated to membership. r means role services, p
means ASP.NET profiles for supporting user profiles, c stands for personalization
of web part pages, and finally, w means SQL web event provider.
-R Uninstalls application services. This switch supports the same option as -A
and uninstalls the corresponding database tables for the application services.
-d Lets you optionally specify the name of the database into which you want to
install the application services. If you don’t specify this parameter, a database
named aspnetdb is created automatically (as is the case with the <default>
option for the database in the wizard interface).

Script Description
InstallCommon.sql Installs some common tables and stored procedures necessary for both the membership and roles APIs. This includes tables for identifying ASP.NET applications that use other ASP.NET features, such as the membership API, role service, or personalization.
InstallMembership.sql Installs the database tables, stored procedures, and triggers used by the membership API. This includes tables for users, additional user properties, and stored procedures for accessing this information.
InstallRoles.sql Installs all database tables and stored procedures required for associating users with application roles.
InstallPersonalization.sql Contains DDL for creating any table and stored procedure required for creating personalized portal applications with web parts.
InstallProfile.sql Creates all the necessary tables and stored procedures for supporting ASP.NET user profiles.
InstallSqlState.sql Installs tables for persistent session state in the TEMP database of SQL Server. That means every time the SQL Server service is shut down, the session state gets lost.
InstallPersistSqlState.sql Installs tables for persistent session state in a separate ASPState database. That means the state stays alive even if the SQL Server service gets restarted.

The SqlMembershipProvider’s Properties

Source: Asp.Net 3.5 in C# Second Edition – Apress Pro

name Specifies a name for the membership provider. You can choose any name you want. You can use this name later for referencing the provider when programmatically accessing the list of configured membership providers. Furthermore, the WAT will use this name to display the provider.
applicationName String value of your choice that specifies the name of the application for which the membership provider manages users and their settings. This setting allows you to use one membership database for multiple applications. Users and roles are always associated with an application. If you do not specify an application name, a root application name called “/” will be used automatically. More details are outlined after the table.
description An optional description for the membership provider.
passwordFormat Gets or sets the format in which passwords will be stored in the underlying credential store. Valid options are Clear for clear-text password storage, Encrypted for encrypting passwords in the datastore (uses the locally configured machine key for encryption), and Hashed for hashing passwords stored in the underlying membership store.
minRequiredNonalphanumericCharacters Specifies the number of nonalphanumeric characters the password needs to have. This is an important part for the validation of the password and enables you to specify strength requirements for the passwords used by your users.
minRequiredPasswordLength Allows you to specify the minimum length of passwords for users of your application. This is also an important property for specifying password strength properties.
passwordStrengthRegularExpression If the previously mentioned properties are not sufficient for specifying password strength conditions, then you can use a regular expression for specifying the format of valid passwords. With this option you are completely flexible in terms of specifying password format criteria.
enablePasswordReset The membership API contains functionality for resetting a user’s password and optionally sending an e-mail if an SMTP server is configured for the application.
enablePasswordRetrieval When set to true, you can retrieve the password of a MembershipUser object by calling its GetPassword method. Of course, this works only if the password is not hashed.
maxInvalidPasswordAttempts Specifies the number of invalid validation attempts before the user gets locked. The default value of this setting is 5. In many cases, you’ll likely want to set this to a lower level depending on your security policy.
passwordAttemptWindow Here you can set the number of minutes in which a maximum number of invalid password or password question-answer attempts are allowed before the user is completely locked out from the application. In that case, the user gets locked out, so the administrator must activate the account again. Again, the default value is ten minutes. Depending on your security policies, you might want to lower or raise the value.
requiresQuestionAndAnswer Specifies whether the password question with an answer is required for this application. This question can be used if the user has forgotten his password. With the answer he gets the possibility of retrieving an automatically generated, new password via e-mail.
requiresUniqueEmail Specifies whether e-mail addresses must be unique for every user in the underlying membership store.

Wednesday, February 8, 2012

Implementing a Custom Membership Provider –MembershipProvider Control

  • Created a new Class called SQLMemebershipProvider:
  • Inherits from the abstract MembershipProvider class, which virtual methods of concern will be overridden.
  • Must reference: System.Web.Security, System.Collections.Specialized
  • Edit web.config and add within <system.web> tag
    <membership defaultProvider="SecurityTutorialsMembershipProvider">
      <providers>
        <clear/>
        <add name="SQLMembershipProvider"
             type="deDogs.Data.SqlMembershipProvider"
             connectionStringName="SecurityTutorialsConnectionString"
             enablePasswordRetrieval="false"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="false"
             applicationName="SecurityTutorials"
             requiresUniqueEmail="true"
             passwordFormat="Hashed"
             maxInvalidPasswordAttempts="5"
             minRequiredPasswordLength="6"
             minRequiredNonalphanumericCharacters="1"
             passwordAttemptWindow="10"
             passwordStrengthRegularExpression=""
             />
      </providers>
    </membership>

The name, type, and connectionStringName tags from above are important.



  • Name is the name of the custom Membership Provider: SQLMembershipProvider
  • type is the class name, because Classes are types, so since I have placed the SQLMembershipProvider class within my custom namespaces (deDogs.Data), the type would be deDogs.Data.SQLMembershipProvider.
  • connectionStringName is the connection name of the SQL database created for membership schema (Creating The Membership Schema in SQL Server).

All attributes of the <add> tag are passed to the Initialize method of the MembershipProvider class. Passed in the collection parameter. Overridding the method the attribute values can be retrieved from the System.Collections.Specialized.NameValueCollection. The Initialize method:


I created one property above, which isn’t shown below in the code:



public override void Initialize(string name, NameValueCollection config)
{
    if (config == null)
    {
        throw new ArgumentNullException("config");
    }
    if (name == null || name.Length == 0)
    {
        name = "SQLMembershipProvider";
    }
    
    if (config["requiresQuestionAndAnswer"] == "true")
    {
        requiresQuestionAndAnswer = true;
    }
 
    base.Initialize(name, config);
}


The requiresQuestionAndAnswer attribute value is used to set the RequiresQuestionAndAnswer property. Must override readonly property:



public override readonly bool RequiresQuesionAndAnswe
{
    get
        {
            if (requiresQuestionAndAnswer)
                {
                    return true;
                }
                else
               {
                   return false;
               }
        }
}

Inheriting MembershipProvider abstract class, the class is populated with all methods that are overridden. They all return an NotImplementedException. Remove the exception.



public override bool RequiresQuestionAndAnswer

{
    get { throw new NotImplementedException(); }
}

and enter in the exception’s place: