Monday, April 26, 2010

Quick way to cross thread invoke

In windows forms, everything is still single threaded. If you've ever tried to use multiple threads and various controls (form timers for example - the non thread safe kind) you will realize properties start disappearing because they all use TLS (Thread Local Storage) which of course - is thread specific. You must set properties and call events on the thread that created the control. You can cross post messages into another app's message queue (which interestingly enough was the source of the 'shatter' attack some years ago allowing attacking applications to take control of other apps by malformed wm_timer message) without a problem but that's a bit more rare now in managed code than it was in native code days.

If you need to set a property or call a method on a control from a worker thread in you needed to create a delegate. The syntax was sometimes a pain.


To easily call this without a 'outside' (ie in class declarations as was typical) setup of the delegates
simply call it directly by creating a delegate inline using an anonymous method or lambda expression. Either one works - it's your preference on syntax:


yourControl.Invoke((MethodInvoker)delegate() { yourControl.Value=0; });

//Or

yourControl.Invoke((Action) ( () => yourControl.Value=0));



What does this save?
1. You don't have to create a separate method.
2. You don't have to declare a separate delegate.

So let's say you wanted to increase the count on a progess bar from a worker thread, you could simply do (off the top of my head.. I believe the invoke needs to be called on the container StatusStrip control) :

statusBar.Invoke((MethodInvoker)delegate() { progressBar.Increment(1); });


Hope this helps - I use it quite frequently. Of course, please don't sprinkle the same line all over your code, wrap it in a method call in case you need to change it.

Animating the Visibility Property in Silverlight

Problem
If you define an animation in silverlight and try to animate the Visibility property (IE to hide or show an element on animation with other effects) it does not work. You will fine when you set the Visibility property it stays the entire animation and doesn't change.

Cause
If you are animating your properties, chances are you are using a double animation, which handles, you guessed it - double values. Visibility is not a double value. Its a specific enumeration value that requires in XAML the text name of the enum value.

Resolutions
In your animation simply add


<Storyboard x:Name="closeControl">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="controlToAnimate" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="00:00:02">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames>
...
</DoubleAnimationUsingKeyFrames>
</Storyboard>




I included a placeholder there for a double animation to show this XAML simply goes on top of your existing DoubleAnimation.

Friday, April 23, 2010

The Dangers of Internet Explorer Mime-Type Sniffing

Internet explorer 4+ has a unique feature called Mime-Type sniffing. It sometimes doesn't pay attention to the extension of your file, but instead wants to determine what kind of file it is for itself. This can be easily exploited via various sites that allow images to be uploaded and provide links to those images.

If the first few bytes of an image file are changed - say.. after bytes 12, to
<script>alert('bip');</script>
Internet Explorer now thinks this is content type text/html and not image/gif.
So if you were to click on a link to this image you would run whatever script was there in your browser.

Thanksfully, mime type detection for images has been disabled in IE8.
You can also turn off all Mime sniffing by doing:
Response.Headers.Add("X-Content-Type-Options", "nosniff");to get: X-Content-Type-Options: nosniff
to the client browser.

The lesson: never ever ever trust image uploads. You can actually check the mime type yourself upon file upload by using the following:



//goes in the beginning of your class
[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
static extern int FindMimeFromData(IntPtr pBC,
[MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)]
byte[] pBuffer,
int cbSize,
[MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
int dwMimeFlags,
out IntPtr ppwzMimeOut,
int dwReserved);

//calling code would be like:
string mimeType = GetMimeFromFile(txtFileName.Text);
MessageBox.Show(mimeType);


//method to check would be:


///
/// Ensures that file exists and retrieves the content type
///

///
/// Returns for instance "images/jpeg"
public static string GetMimeFromFile(string file)
{
IntPtr mimeout;
if (!System.IO.File.Exists(file))
throw new FileNotFoundException(file + " not found");

int MaxContent = (int)new FileInfo(file).Length;
if (MaxContent > 4096) MaxContent = 4096;
FileStream fs = File.OpenRead(file);


byte[] buf = new byte[MaxContent];
fs.Read(buf, 0, MaxContent);
fs.Close();
int result = FindMimeFromData(IntPtr.Zero, file, buf, MaxContent, null, 0, out mimeout, 0);

if (result != 0)
throw Marshal.GetExceptionForHR(result);
string mime = Marshal.PtrToStringUni(mimeout);
Marshal.FreeCoTaskMem(mimeout);
return mime;
}


Tuesday, April 20, 2010

Transitioning from non-ssl to ssl and back again for login

Problem
You have a web site that needs to transition from non-ssl, to an ssl login page, back to non-ssl for the rest of the site navigation. This must use forms authentication

Details
Forms authentication has a method FormsAuthentication.RedirectFromLoginPage()
However the url is passed over the querystring as login.aspx?RedirectUrl=somepage.aspx
This is all automatic and you cannot specify an absolute url here. Even if you make it an absolte url such as
login.aspx?RedirectUrl=http://www.mysite.com/somepage.aspx (of course this would need to be url encoded) this wont work.
Also an oddity in my testing if you specify an absolute url for the login page, forms authentication - even with a valid login- redirects you right back to the login page.
For example: If my web.config contains:
<forms loginUrl="http://localhost/secure/Login.aspx"
Or SSL
<forms loginUrl="https://localhost/secure/Login.aspx"

Both of these cause forms authentication to send out a redirect NOT to the RedirectUrl, but right back to the login page!

If you are currently surfing a non-http site and need the login page to go SSL there doesn't seem to be an easy way to do this considering the problem above (at least on my laptop Im using for testing) . Even if you set the flag <forms requireSsl=true it just errors out if you are not using an ssl connection but it does not redirect to ssl.


Solution
1. For your login url in forms authentication you can specify the full https:// url. OR you can use
<forms loginUrl="~/secure/Login.aspx"
On your login.aspx page

if (!Request.IsSecureConnection)
{
Response.Redirect( --form your https url here or read from config);
}


OR you can use
<forms loginUrl="https://localhost/secure/Login.aspx"
and then in your login page, use a login control. Hook into the Logged_In method.


protected void Login1_LoggedIn(object sender, EventArgs e)
{
//see below for code on GetNonSslRedirectUrl
Response.Redirect(GetNonSslRedirectUrl(""));

}


If you are not using a login control, then simply do something like such:

if (Authenticate(userName, password))
{
string redirectUrl = GetNonSslRedirectUrl(userName);
FormsAuthentication.SetAuthCookie(userName, false);
Response.Redirect(redirectUrl);
}




///
/// Returns an http url (not https) to redirecto to a non-secure page
/// after being at a secure login page.
///

/// The username attempting the login.
/// The http://... url
private string GetNonSslRedirectUrl(string userName)
{
string redirectUrl = FormsAuthentication.GetRedirectUrl(userName, false);
//if its ssl, make non ssl.
if (redirectUrl.Contains("https://"))
{
redirectUrl = redirectUrl.Replace("https://", "http://");
}
//if it doesnt contain http:// then its a relative path. append http and the host to it.
if (!redirectUrl.Contains("http://"))
{
if (redirectUrl.StartsWith("/"))
{
redirectUrl = string.Format("http://{0}{1}", Request.Url.Host, redirectUrl);
}
else
{
//it needs a / in it. url is just 'something.aspx' - not sure if this can happen, its a failsafe
redirectUrl = string.Format("http://{0}/{1}", Request.Url.Host, redirectUrl);
}
}
return redirectUrl;
}


Saturday, April 17, 2010

Determining a user's permissions on sql objects

I use a variant of this for auditing user permissions for security checks. My preference is a user should only have execute access to a stored procedure and thats it. This will find tables the user has select access on (to return all permissions simply remove the: where permission_name='select' and subentity_name ='' )

This will work for users who receive permissions as part of role membership as well as direct permissions on an object.


--this WILL work for role based permissions as well.
select b.name + '.' + a.name, permissions.permission_name from sys.tables a
inner join sys.schemas b on a.schema_id=b.schema_id
cross apply (select * From fn_my_permissions(b.name + '.' + a.name, 'object') where permission_name='select' and subentity_name ='') as permissions
where a.type='u'

--should only have execute
select b.name + '.' + a.name, permissions.permission_name from sys.procedures a
inner join sys.schemas b on a.schema_id=b.schema_id
cross apply (select * From fn_my_permissions(b.name + '.' + a.name, 'object') ) as permissions

Tuesday, April 6, 2010

Hasing Passwords

I think some people aren't sure the best way to implement password hashing for storing in a database or config file. Its super fast and super easy.

Simply open snippet compiler an essential tool at ( ) and add a reference to system.web
then add:
using System.Web.Security;

and the code is simply:

string hashedText = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile("test", "SHA1");
Console.WriteLine(hashedText);


if you want the full class to use in snippet compiler for copying and pasting it is simply

using System;
using System.Collections.Generic;
using System.Web.Security;
public class HashPassword
{
public static void RunSnippet()
{
string hashedText = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile("test", "SHA1");
Console.WriteLine(hashedText);
}

#region Helper methods

public static void Main()
{
try
{
RunSnippet();
}
catch (Exception e)
{
string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
Console.WriteLine(error);
}
finally
{
Console.Write("Press any key to continue...");
Console.ReadKey();
}
}

private static void WL(object text, params object[] args)
{
Console.WriteLine(text.ToString(), args);
}

private static void RL()
{
Console.ReadLine();
}

private static void Break()
{
System.Diagnostics.Debugger.Break();
}

#endregion
}