HttpHandler and security

K

klausand

I have wriiten a HttpHandler-class that works as a dispatch layer - the
class analyses the incomming url and creates the appopriate classes to
serve the request.

I some cases I want the user to authenticate himself (fx. if the url
ends with the word "edit") - how do I force the user to authenticate
himself - fx. via basic authentication - when he fires a request
containing such a virtual folder - only expressed internally in my
application.

Hope someone can give me a hint - or maybe a solution - on this topic.

Regards Klaus
 
D

Dominick Baier [DevelopMentor]

You could emit a 401 authorize header - if IIS is configured for basic auth
this will trigger the authentication handshake...

response.StatusCode=401;
responser.header.add("Authorize") - dunno the exact format at the moment

for the exact format lookup the RFC for basic auth.
 
J

Joe Kaplan \(MVP - ADSI\)

The other thing is that the built in modules for authentication and
authorization can be used here. All he would need to do is set up the
<allow> and <deny> tags in his web.config to not allow access the path
served by his handler for unauthenticated users an the
UrlAuthorizationModule will take care of it.

Since he wants basic authentication as well, IIS will basically just do it.

If he wanted to handle basic auth in his own code, he could write his own
basic authentication module. In this case, it would detect an
unauthenticated user and send back the appropriate "www-authenticate"
header. The browser would then prompt the user for credentials and send
them in the "authorization" header in the next request. His module would
then read the credentials and authenticate them.

However, I would never recommend doing this if he wants to authenticate
against Windows. Just let IIS do it.

Joe K.
 
K

klausand

Thanks - this is exactly what I did.

In IIS check "Enable anonymous access" and "Basic Authentication".
In my code I can check for the principals role like this

if (!context.User.Identity.IsAuthenticated ||
!context.User.IsInRole("SomeGroup")) {
context.Response.StatusCode = 401;
context.Response.End();
}

If the user isnt authenticated or is member of a specific group (or
whatever criteria) then IIS will take care of the authentication.

In this way I think that other authentication-schemes will work as
well.

Regards
Klaus Andersen, Teknologisk Institut


Dominick skrev:
 
K

klausand

Thanks - this is exactly what I did.

In IIS check "Enable anonymous access" and "Basic Authentication".
In my code I can check for the principals role like this

if (!context.User.Identity.IsAuthenticated ||
!context.User.IsInRole("SomeGroup")) {
context.Response.StatusCode = 401;
context.Response.End();
}

If the user isnt authenticated or is member of a specific group (or
whatever criteria) then IIS will take care of the authentication.

In this way I think that other authentication-schemes will work as
well.

Regards
Klaus Andersen, Teknologisk Institut


Dominick skrev:
 
J

Joe Kaplan \(MVP - ADSI\)

You don't even need to do that though. You can just use the
UrlAuthorizationModule with the allow and deny tags to allow
role="SomeGroup" and deny user="*" (in that order) and you'll get that
behavior declaratively.

Either way works.

Joe K.
 
D

Dominick Baier [DevelopMentor]

a basic auth module could look like this:

namespace LeastPrivilege
{
class BasicAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new EventHandler(OnAuthenticate);
}

void OnAuthenticate(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;

bool authenticated = false;

string authHeader = ctx.Request.Headers["Authorization"];

if (authHeader != null && authHeader.StartsWith("Basic"))
{
string encodedUserPass = authHeader.Substring(6).Trim();
string userPass = Encoding.ASCII.GetString(
Convert.FromBase64String(encodedUserPass));

string[] credentials = userPass.Split(':');

if (Membership.ValidateUser(credentials[0], credentials[1]))
{
GenericIdentity id = new GenericIdentity(credentials[0],
"CustomBasic");
GenericPrincipal p = new GenericPrincipal(id, null);
ctx.User = p;

authenticated = true;
}
}

if (authenticated == false)
{
ctx.Response.StatusCode = 401;
ctx.Response.AddHeader(
"WWW-Authenticate",
"Basic realm=\"localhost\"");
ctx.Response.End();

return;
}
}

public void Dispose()
{}
}
}
 
K

klausand

I thought of that - but my problem is (was) that it is my
HttpHandler-class that dispatches the request, and I only want the user
to authenticate himself in some special cases.

As I understand the declarative settings in web.config, they works
globally in the scope covered by the web.config file.

But hanks for your advice anyway.

Klaus
 
J

Joe Kaplan \(MVP - ADSI\)

This is cool, Dominick.

I would just change two things:

You'll get unexpected results if the user's password contains a ":"
character. Split doesn't work anymore. You just need to find the first :
and everything after is password (including null in some cases).

For encoding, the basic auth Base64 header data is neither ASCII nor UTF-8,
but is actually ISO-8859-1. I haven't found this documented anywhere, but I
have done a bunch of testing with non-ASCII characters and have concluded
that IE and IIS both expect that encoding.

I'm also not sure if it is the server's responsibility to stop sending
www-authenticate headers after x number of failed attempts, or if that
behavior is baked into the browser (I think the latter...).

Joe K.

Dominick Baier said:
a basic auth module could look like this:

namespace LeastPrivilege
{
class BasicAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new
EventHandler(OnAuthenticate);
}

void OnAuthenticate(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;

bool authenticated = false;

string authHeader = ctx.Request.Headers["Authorization"];

if (authHeader != null && authHeader.StartsWith("Basic"))
{
string encodedUserPass = authHeader.Substring(6).Trim();
string userPass = Encoding.ASCII.GetString(
Convert.FromBase64String(encodedUserPass));

string[] credentials = userPass.Split(':');

if (Membership.ValidateUser(credentials[0],
credentials[1]))
{
GenericIdentity id = new
GenericIdentity(credentials[0], "CustomBasic");
GenericPrincipal p = new GenericPrincipal(id, null);
ctx.User = p;

authenticated = true;
}
}

if (authenticated == false)
{
ctx.Response.StatusCode = 401;
ctx.Response.AddHeader(
"WWW-Authenticate",
"Basic realm=\"localhost\"");
ctx.Response.End();

return;
}
}

public void Dispose()
{}
}
}

---------------------------------------
Dominick Baier - DevelopMentor
http://www.leastprivilege.com
You don't even need to do that though. You can just use the
UrlAuthorizationModule with the allow and deny tags to allow
role="SomeGroup" and deny user="*" (in that order) and you'll get that
behavior declaratively.

Either way works.

Joe K.
 
D

Dominick Baier [DevelopMentor]

thanks. i will work on that more in the next days - i will ping you then :))
I'm also not sure if it is the server's responsibility to stop sending
www-authenticate headers after x number of failed attempts, or if that
behavior is baked into the browser (I think the latter...).

i think the former - from my testing (which was very brief) i think i could
get the auth window to pop up as long as i wanted...

yeah - i just hacked this mod together because this was something i wanted
to do for longer...

---------------------------------------
Dominick Baier - DevelopMentor
http://www.leastprivilege.com
This is cool, Dominick.

I would just change two things:

You'll get unexpected results if the user's password contains a ":"
character. Split doesn't work anymore. You just need to find the
first : and everything after is password (including null in some
cases).

For encoding, the basic auth Base64 header data is neither ASCII nor
UTF-8, but is actually ISO-8859-1. I haven't found this documented
anywhere, but I have done a bunch of testing with non-ASCII characters
and have concluded that IE and IIS both expect that encoding.

I'm also not sure if it is the server's responsibility to stop sending
www-authenticate headers after x number of failed attempts, or if that
behavior is baked into the browser (I think the latter...).

Joe K.

a basic auth module could look like this:

namespace LeastPrivilege
{
class BasicAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest += new
EventHandler(OnAuthenticate);
}
void OnAuthenticate(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;
bool authenticated = false;

string authHeader = ctx.Request.Headers["Authorization"];

if (authHeader != null && authHeader.StartsWith("Basic"))
{
string encodedUserPass = authHeader.Substring(6).Trim();
string userPass = Encoding.ASCII.GetString(
Convert.FromBase64String(encodedUserPass));
string[] credentials = userPass.Split(':');

if (Membership.ValidateUser(credentials[0],
credentials[1]))
{
GenericIdentity id = new
GenericIdentity(credentials[0], "CustomBasic");
GenericPrincipal p = new GenericPrincipal(id, null);
ctx.User = p;
authenticated = true;
}
}
if (authenticated == false)
{
ctx.Response.StatusCode = 401;
ctx.Response.AddHeader(
"WWW-Authenticate",
"Basic realm=\"localhost\"");
ctx.Response.End();
return;
}
}
public void Dispose()
{}
}
}
---------------------------------------
Dominick Baier - DevelopMentor
http://www.leastprivilege.com
You don't even need to do that though. You can just use the
UrlAuthorizationModule with the allow and deny tags to allow
role="SomeGroup" and deny user="*" (in that order) and you'll get
that behavior declaratively.

Either way works.

Joe K.

Thanks - this is exactly what I did.

In IIS check "Enable anonymous access" and "Basic Authentication".
In my code I can check for the principals role like this

if (!context.User.Identity.IsAuthenticated ||
!context.User.IsInRole("SomeGroup")) {
context.Response.StatusCode = 401;
context.Response.End();
}
If the user isnt authenticated or is member of a specific group (or
whatever criteria) then IIS will take care of the authentication.
In this way I think that other authentication-schemes will work as
well.

Regards
Klaus Andersen, Teknologisk Institut
Dominick skrev:
You could emit a 401 authorize header - if IIS is configured for
basic
auth
this will trigger the authentication handshake...
response.StatusCode=401;
responser.header.add("Authorize") - dunno the exact format at the
moment
for the exact format lookup the RFC for basic auth.
---------------------------------------
Dominick Baier - DevelopMentor
http://www.leastprivilege.com
I have wriiten a HttpHandler-class that works as a dispatch layer
- the class analyses the incomming url and creates the appopriate
classes to serve the request.

I some cases I want the user to authenticate himself (fx. if the
url ends with the word "edit") - how do I force the user to
authenticate himself - fx. via basic authentication - when he
fires a request containing such a virtual folder - only expressed
internally in my application.

Hope someone can give me a hint - or maybe a solution - on this
topic.

Regards Klaus
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top