Thursday, September 07, 2006

ASP.net session bump/hijack issue - F5 & IIS cluster

I haven't seen this issue personally, but it is confirmed from multiple sources this happens. In short: user rarely bumps into another user's session, in verbose: user logs into web app and uses it for a while and all of a sudden gets data that is not in his session.

Current prod infrastructure is IIS cluster behind F5. Session affinity is set to 20 mins and IIS session time-out is set 20 mins. Session location is in-proc.

I have seen request getting postedback to different webserver and hence viewstate validation fail with exception CryptographicException: Bad Data. Authentication of viewstate failed. ...
i don't know how F5 works but i guess F5 lost identifier of the user and hence thinks it as new user and routes request to different web server. This is a normal issue and we can either turnoff viewstate encryption or explicitly specify viewstate encryption key in all servers in cluster.

But i have to totally rule out F5 from the session bump/hijack issue, 'coz F5 has no idea what asp.net session is, that is something internal to aspnet_wp.exe (IIS5) or w3wp.exe (IIS6).

Under the hood, aspnet maintains the session id using ASP.NET_SessionId cookie and IIS can guarantee its uniqueness across its requests. Here is an MSFT KB about reusing session id for apps in same DNS domain and this article explains behind the scene how asp.net serves request.

I'm guessing this could be the cause,
userA hits webserver1 in cluster in the initial request and F5 should take us to the same server for subsequent requests.
If F5 sends the userA's request to webserver2 in middle of the session, and by happenstance (theoretically this may be possible) userA's ASP.NET_SessionId is already assigned by webserver2 to some other user (say userB), then aspnet engine will think userA as userB and assign userB's sessionstate to this request.

Pls leave a note if you think this may be possible or how you think this could be happening.

I expect IIS team to provide option to make the sessionid unique in a cluster environment. For now what we planned is to generate a unique sessionid and store it in cookie as well as session, check if they match for every request, if not kill the session, this is going to kill both user's session, but i don't know if there is any way out.

I didn't want to include tis logic in existing app 'coz 1 i'll have to include this logic in every app i write and 2 i'm too lazy. I wrote following HTTPModule and hooked it to web app, ideally something like this should be configured in machine.config so that we don't need to do this every web.config, netiher i don't have authority nor i want to pursue this.

Right now code is written to send a mail when something like this happens (just dropping a line to see if something takes this bait) and it will redirect to itself, so page is written poperly to handle empty session which i expect to be a norm.

using System;
using System.Web;
using System.Web.SessionState;

namespace KCC
{
public class SessionCheck : IHttpModule
{
public SessionCheck() {}
public void Dispose() {}
public void Init(HttpApplication httpApp)
{
httpApp.PreRequestHandlerExecute += new EventHandler(this.PreRequestHandlerExecute);
if (httpApp.Modules["Session"] != null)
{
SessionStateModule session = (SessionStateModule) httpApp.Modules["Session"];
session.Start += new EventHandler(this.OnSessionStart);
}
}

public void OnSessionStart(object sender, EventArgs e)
{
HttpCookie sessionChk = new HttpCookie("KCC.SessionCheck");
sessionChk["iSAS.SessionID"] = Guid.NewGuid().ToString();
sessionChk["iSAS.MacName"] = System.Environment.MachineName;
sessionChk["iSAS.SesStrtTmstmp"] = System.DateTime.Now.ToString();
HttpContext.Current.Response.AppendCookie(sessionChk);
HttpContext.Current.Session["iSAS.SessionID"] = sessionChk["iSAS.SessionID"];
}


private void PreRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication httpApp = (HttpApplication) sender;
HttpSessionState session = httpApp.Context.Session;
if (session != null) //skip pages which don't use session
{
if(session["iSAS.SessionID"] != null) //skip if session value is empty
{
if (!httpApp.Context.Session.IsNewSession) //skip if new session
{
string szCookieHeader = System.Web.HttpContext.Current.Request.Headers["Cookie"];
if ((null != szCookieHeader) && (szCookieHeader.IndexOf("ASP.NET_SessionId") >= 0)) //skip if new session 2
{
if (httpApp.Request.Cookies["KCC.SessionCheck"] != null) //skip ifCookie not found
{
if (httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SessionID"] != null) //skip if cookie data not found
{
if (session["iSAS.SessionID"].ToString().Trim() != httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SessionID"].ToString().Trim())
{
System.Text.StringBuilder info = new System.Text.StringBuilder();
if (httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SesStrtTmstmp"] != null)
info.Append("Session start time: " + httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.SesStrtTmstmp"].ToString());
info.Append("<br/>");
if (httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.MacName"] != null)
info.Append("Session start web server name: " + httpApp.Request.Cookies["KCC.SessionCheck"]["iSAS.MacName"].ToString());
info.Append("<br/>");
info.Append("Current time " + System.DateTime.Now.ToString());
info.Append("<br/>");
info.Append("Current web server " + System.Environment.MachineName);
info.Append("<br/>");
info.Append("Logged on NTID" + httpApp.Context.Request.ServerVariables.Get("LOGON_USER").ToString());
info.Append("<br/>");
if (session["NTID"] != null) info.Append("Current NTID in session" + session["NTID"].ToString());
info.Append("<br/>");
info.Append("URL " + httpApp.Request.Url.ToString());
info.Append("<br/>");
System.Web.Mail.MailMessage msg = new System.Web.Mail.MailMessage();
msg.To = "me@mail.com";
msg.Subject = "-- SESSION HIJACK ISSUE --";
msg.From = "me@mail.com";
msg.Body = info.ToString();
msg.BodyFormat = System.Web.Mail.MailFormat.Html;
try
{
System.Web.Mail.SmtpMail.SmtpServer = "mailhost.com";
System.Web.Mail.SmtpMail.Send(msg);
}
catch //do nothing
{}
httpApp.Context.Response.Cookies["KCC.SessionCheck"].Expires = DateTime.Now.AddYears(-1);
session.Abandon();
httpApp.Context.Response.Write(httpApp.Context.Request.Url.AbsoluteUri.ToString());
httpApp.Context.Response.Redirect(httpApp.Context.Request.Url.AbsoluteUri.ToString(),true);
httpApp.CompleteRequest();
}
}
}
}
}
}
}
}
}
}

For further reading, Foiling Session Hijacking Attempts and HTTP application executes events sequence.

Chennai satellite town

Irrespective of what Ramadoss/JJ/KK thinks about the chennai satellite town, this is going to come up. Reason cited for objection "destroying fertile lands", CRAP! what if NY residents opposed to so called encroachment of urban buildings some 100 yrs back; ok tamil heroine's will have to look for different city to dance around, nothing much.

Current facilities is super sufficent for chennai city (if we can roll back 10 yrs). What leaders don't have is a vision (Dr. APJ strsses this in almost all his speeches), even the most hated Hitler had a vision to make Germany a modern nation, if not they will not have Autobahn now. Hmm, why should J needs to worry if she has to identify something everyday to spit out "arrikai".

Nothing is going to improve if TN politics is going to roll around J & K.

Vande Mataram!
:-s

Wednesday, September 06, 2006

asp:CustomValidator changing ErrorMessage displayed

requirement: custom validate the amount value; is required only if ddlContributionType value is 2 or 3.

code:
<!-- aspx -- >
<asp:dropdownlist id="ddlContributionType" runat="server">
<asp:textbox id="txtAmount" runat="server">
<asp:customvalidator id="CustomValidateAmount" runat="server" controltovalidate="txtAmount" errormessage="Invalid" clientvalidationfunction="CustomValidateAmount_ClientValidate" onservervalidate="CustomValidateAmount_ServerValidate" />

function CustomValidateAmount_ClientValidate(oSource,oArguments)
{
var ddlContributionType;
if (oSource.getAttribute("ContributionType") != null)
{
ddlContributionType = document.getElementById(oSource.getAttribute("ContributionType"));
}
if (ddlContributionType != null)
{
if ( (ddlContributionType.value != "2") && (ddlContributionType.value != "3") )
{
oArguments.IsValid = true;
return;
}
}
else
{
oArguments.IsValid = true;
return;
}
var amount = oArguments.Value;
oArguments.IsValid = false;
if ( isNaN(amount) == true)
{
oSource.innerHTML = "Invalid amount";
return;
}
var re = new RegExp("^\\d+(\\.\\d{1,2})?$");
m = amount.match(re);
if (m == null)
{
oSource.innerHTML = "Invalid amount format";
return;
}
if ( amount < innerhtml = "Minimum donation of $1 required"> 9999.99 )
{
oSource.innerHTML = "Maximum donation is $9999.99";
return;
}
oArguments.IsValid = true;
}

' .aspx.vb Page_Load
CustomValidateAmount.Attributes("ContributionType") = ddlContributionType.ClientID.ToString()

I explicity not included the check if required for condition 2 or 3, as i re-use an existing validator (RequiredFieldValidatorWithAEnablingCondition) that i developed long back, will mail the code if requested

<KCC:RequiredFieldValidatorWithAEnablingCondition runat="server" ID="RequiredFieldValidatorWithAEnablingConditionAmount" ControlToValidate="txtAmount"
ControlToCompare="ddlContributionType" TriggerValue="2;3" ErrorMessage="Required." />

Monday, September 04, 2006

Is life hitch hiking ?

Today saw Discovery documentry on panspermia, wonder what finally turned out in "kerala red rain". School taught the evolution theory, temple preached "intelligent design", after all who cares about these, idea is to stuff the answer paper and get results in mark sheet.
I think i really didn't take any side here, where do U stand ?

Saturday, September 02, 2006

LDAP authentication in website

Got a requet from a client to provide LDAP authentication in exisiting ASP website, in plain terms authenticate users using thier domain id and password. Initial thought of intergrated win auth was ruled out 'coz of Kiosk based computers used. Should make sure we SSL the page.

'ASP code
Const ADS_SECURE_AUTHENTICATION = &H1
On Error Resume Next
Err.Clear
Set dso = GetObject("LDAP:")
Set domain = dso.OpenDSObject("LDAP://server.com", strUserName, strUserPassword, ADS_SECURE_AUTHENTICATION)
If Err.number <> 0 then
Response.Write("Invalid login. Err.number=" & Err.number)
Else
Response.Write("Valid login. Err.number=" & Err.number)
End If
Set dso = Nothing
Set domain = Nothing

Also same functionality in ASP.net 1.0 http://support.microsoft.com/?id=326340.

dinamalar epaper

Its becoming almost an habit to read dinamalar in original paper format. Today i thought of scrapping the content for offline viewing, it was rediculously simple, here goes the code.


using System;
using System.Drawing;
using System.Text;
using System.Net;
using System.IO;

namespace GetDinamalar
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
DoWork();
}
static void DoWork()
{
// /Web/PagePrint/YYYY/MM/DD/DD_MM_YYYY_001.pdf
try
{
int pgCtr=1;
Console.WriteLine("Booting up ....");
string folderpath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()),DateTime.Now.ToString("MM.dd.yyyy") );
Directory.CreateDirectory(folderpath) ;
WebProxy proxy = null;
Microsoft.Win32.RegistryKey reg1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
if (reg1.GetValue("ProxyEnable", "1").ToString() == "1")
{
proxy = new WebProxy("http://" + reg1.GetValue("ProxyServer", "proxy.isas.com:80").ToString(),true,null,CredentialCache.DefaultCredentials);
}
reg1.Close();
while(true)
{
Console.WriteLine("Sending request to web for page " + pgCtr.ToString());
string address = "http://epaper.dinamalar.com/Web/PagePrint/" + DateTime.Now.ToString("yyyy/MM/dd/dd_MM_yyyy_") + pgCtr.ToString().PadLeft(3,'0') + ".pdf";
WebRequest httpRequest = WebRequest.Create(address);
if(proxy != null) httpRequest.Proxy = proxy;

Stream ReceiveStream = null;
try
{
ReceiveStream = httpRequest.GetResponse().GetResponseStream();
}
catch
{
return;
}
string filePath = Path.Combine(folderpath ,DateTime.Now.ToString("yyyy_MM_dd_") + pgCtr.ToString().PadLeft(3,'0') + ".pdf");
FileStream fs = new FileStream(filePath, FileMode.Create,FileAccess.Write);
BinaryWriter binWrt = new BinaryWriter(fs);
binWrt.BaseStream.Seek(0, SeekOrigin.End);
int data=0;
byte [] buffer = new byte [1024];
while ((data=ReceiveStream.Read(buffer,0,1024))>0)
{
binWrt.Write(buffer,0,data);
}
binWrt.Flush() ;
binWrt.Close() ;
pgCtr++;
}
}
catch (Exception exception)
{
Console.WriteLine("Error:" + exception.ToString());
Console.Read();
}
finally
{
Console.WriteLine("Exiting process ...");
}
}
}
}

Back from Hibernation :-)

It is almost 3 months i blogged, got myself tied up for happier personal reasons. To re-start with c# (.net 1.x) code to scrap daily dilbert comic and set it as desktop wallpaper.

Due to frequent demand for this, download the application here. Copy GetDilbert.exe to a folder and create a shortcut in windows startup.

using System;
using System.Drawing;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;

namespace GetDilbert
{
class Class1
{
private const int SPI_SETDESKWALLPAPER = 0X14;
private const int SPIF_UPDATEINIFILE = 0X1;
private const int SPIF_SENDWININICHANGE = 0X2;

[DllImport("USER32.DLL", EntryPoint = "SystemParametersInfo", SetLastError = true)]
private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);

[STAThread]
static void Main(string[] args)
{
DoWork();
}

static void DoWork()
{
try
{
Console.WriteLine("Booting up ....");
string address = "http://www.dilbert.com/comics/dilbert/archive/";
WebProxy proxy = null;

WebRequest httpRequest = WebRequest.Create(address);
Microsoft.Win32.RegistryKey reg1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
if (reg1.GetValue("ProxyEnable", "1").ToString() == "1")
{
proxy = new WebProxy("http://" + reg1.GetValue("ProxyServer", "proxy.isas.com:80").ToString(),true,null,CredentialCache.DefaultCredentials);
}
reg1.Close();
string streamText = null;
Console.WriteLine("Sending request to web ...");
if(proxy != null) httpRequest.Proxy = proxy;
using (StreamReader rdr = new StreamReader(httpRequest.GetResponse().GetResponseStream(), System.Text.Encoding.ASCII))
{
streamText = rdr.ReadToEnd();
}
Console.WriteLine("Scrapping web response ...");
Match match = Regex.Match(streamText,@"/comics/dilbert/archive/images/(\w+).(gifjpg)",RegexOptions.IgnoreCase);

if (match != Match.Empty)
{
string imageLocation = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()),"Dilbert_" + DateTime.Now.ToString("MM.dd.yyyy") + ".bmp");
Console.WriteLine("Downloading todays comic ...");
httpRequest = WebRequest.Create("http://www.dilbert.com" + match.Value);
if(proxy != null) httpRequest.Proxy = proxy;
using (Bitmap bmp = new Bitmap(httpRequest.GetResponse().GetResponseStream()))
{
bmp.Save(imageLocation, System.Drawing.Imaging.ImageFormat.Bmp);
}
Console.WriteLine("Setting as desktop wallpaper...");
Microsoft.Win32.RegistryKey reg = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
reg.SetValue("TileWallpaper", "0");
reg.SetValue("WallpaperStyle", "0");
reg.Close();
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, imageLocation, SPIF_UPDATEINIFILE SPIF_SENDWININICHANGE);
Console.WriteLine("Shutting down app.");
}
}
catch (Exception exception)
{
Console.WriteLine("Error:" + exception.ToString());
Console.Read();
}
}
}
}

Monday, May 01, 2006

Microsoft.Web.AJAX


In past i was intrigued by new mail notification feature in Outlook Web Access {tracking back OWA history} and kept wondering on technology behind that.
Enter Google Maps, everything changed after people coined term AJAX . Happen to learn concept of remote scripting in last project which used JSRS technique to get data for dropdowns and help text from server for controls. And i decided to look out for alternatives/options for this new wave (hype?) of web programming model. Ajax.NET Professional seems to be easier to use. But support to asp.net controls are limited, and when googling around that ended up in ASP.Net 2.0 Client Callback feature & Atlas April CTP, Expect Atlas to shadow the limited callback feature of asp.net 2.0

Here is a 18 mins video demo on Developing ASP.NET 2.0 Applications using "Atlas" by Scott G. Its kind-of Houdini show, when you see him write a Todolist web page and AJAX enabling the page, all under 18 mins and without writing a SINGLE line of javascript! I can understand why java community hates MS. Have time? See Atlas in action

From an architect point of view, Ajax usage may probably violate tiered approach and presentation tier code may end up spaghetti, isn't the idea of asp.net code behind model is to avoid this. Atlas overcomes this by providing a declarative programming model for Ajax and enabling use of asp.net server controls

I gotta give credits to MS for developing something like Atlas, even though its an alternative to Smart Client architecture that MS wants to use for rich web apps. But, I'd expect to use this in limited and for appropriate cases and i don't want follow Ajax bandwagon of creating single-page interface application in web, IMHO web apps are not supposed to work that way, rather leverage on Ajax features in cases where it demands like Google Suggest. To end with an interesting fact i happen to learn how Google prevents against excessive queries, mainLoop sets itself up to be called repeatedly using the javascript setTimeout function, instead of keydown events, handling fast typers on slow connections and also timeout interval between calls seems to be dynamic, adjusted according to how quickly the server responds.

Tuesday, April 25, 2006

iPod encounters

I never thought of buying an iPod, and had been very much satisfied with Nomad Muvo 512, which i still believe gives good bang for buck. Until company gave all an iPod Video 30G. It was a more than a surprise when it got shipped in less than 48 hours from Shangai, CN to Knoxville, TN; hmm FedEx! Anyway here I’m trying to abuse this piece of electronic.

Anyone will fall for the slick user interface and cool click wheel, but when you start using it from a WinX OS, you are hit with a quite a bit of annoyances. iTunes interface for iPod is one crazy software, it doesn’t tell you if a selected/drag-n-dropped media file is not supported or anything, it just acts dumb. I can understand if Apple decided not to support many WinX media formats in iTunes, but I expect them to provide atleast an encoder in it to support those? I'v got ephPod downloaded; breathing fire over iTunes shoulder, waiting to make that kill.

Media are internally stored with cryptic file names, once I get them in, I don’t know how to backup them with original file names. A useless info I gained while googling for converters, AAC (internal media format) doesn’t (surprisingly) stand for Apple Audio Codec.

MediaCoder did most of the work converting all my audio collections in myriad formats to AAC (pointe de note: Only Nero AAC Encoder was able to convert my rm’s and gave better conversion quality over iTunesEncoder). Sadly I was not able to convert mpeg/dvd-rips using MediaCoder video conversion, even converting strictly to Apple iPOD supported video formats (tried both MPEG-4 & H.264 video in 768 Kbps); not able to get any support in web for this software, should be satified to have a free converter for most used feature of iPOD. Finally ended up using Xilisoft iPOD video converter, there is lot on the market.

And finally a note on v.rarely talked about feature, eBooks, check out yourself thousands of free ebooks for iPOD @ http://manybooks.net.

Keeping issues aside, iPod gets whatz supposed to: look&feel, good battery (audio only, video drains out battery v.v.quickly, drains 75% in 10-15 mins video playback), simple and easy interface and more than anything raising eyebrows in next seat ;-)

Bye and Happy iPoding.

Thursday, April 06, 2006

Reverse Engineering Taboo

Many (esp in IT world) think this a taboo, well here i'm trying to break. I don’t understand why such negativity associated with this; if this is not so in other industries. Take an automobile manufacturing industry, isn't a common practice to buy competitor product and disassemble it to examine and understand for the purpose of enhancing their vehicles/components. If you can consider that legal (of course if it doesn’t violate any patent/copy-right), then it should be okay to disassemble software for the purpose of understanding and enhancing existing system.

For people who confuse this with Security Hack, question yourself why you have that secured data/logic in the binaries which can be reversed in a matter of seconds (Lutz Roeder's .NET Reflector), esp with the these high-order languages. No, not even NGEN isn't protected from reversing, hmmm, are you still in Fool's Paradise!

Wake-up Buddy. The silent guy in that last desk could be a black hat! Hahahaha.