Question: I have a document management system, and I am building a Web-Service interfaces to the database.
Everything works so far, just that right now, it's totally unsecured, everybody can access it.
How can I incorporate password or private-public key authentication ?
I can only find 'best practises' and using 'windows user' or passport authentication. But I need authentication from a user and password stored in the database, or better for an RSA private-key stored for each web-service user in the database...
Edit:
I have to use the .NET Framework 2.0 in an ASP.NET environment
The solution is to write an own http module with a mixture of code provided by MSDN and CodeProject. Including own fixes of MS bugs, and then add this custom soap header to the web service.
<SoapHeader("Authentication", Required:=True)>
This is the module:
Imports System.Web
Imports System.Web.Services.Protocols
' http://msdn.microsoft.com/en-us/library/9z52by6a.aspx
' http://msdn.microsoft.com/en-us/library/9z52by6a(VS.80).aspx
' http://www.codeproject.com/KB/cpp/authforwebservices.aspx
' http://aleemkhan.wordpress.com/2007/09/18/using-wse-30-for-web-service-authentication/
' http://www.codeproject.com/KB/WCF/CustomUserNamePassAuth2.aspx
' http://www.codeproject.com/KB/WCF/CustomUserNamePassAuth2.aspx
' http://www.codeproject.com/KB/webservices/WS-Security.aspx
'Public NotInheritable Class WebServiceAuthenticationModule
Public Class WebServiceAuthenticationModule
Implements System.Web.IHttpModule
Protected Delegate Sub WebServiceAuthenticationEventHandler(ByVal sender As [Object], ByVal e As WebServiceAuthenticationEvent)
Protected _eventHandler As WebServiceAuthenticationEventHandler = Nothing
Protected Custom Event Authenticate As WebServiceAuthenticationEventHandler
AddHandler(ByVal value As WebServiceAuthenticationEventHandler)
_eventHandler = value
End AddHandler
RemoveHandler(ByVal value As WebServiceAuthenticationEventHandler)
_eventHandler = value
End RemoveHandler
RaiseEvent(ByVal sender As Object,
ByVal e As WebServiceAuthenticationEvent)
End RaiseEvent
End Event
Protected app As HttpApplication
Public Sub Init(ByVal context As System.Web.HttpApplication) Implements System.Web.IHttpModule.Init
app = context
context.Context.Response.Write("<h1>Test</h1>")
AddHandler app.AuthenticateRequest, AddressOf Me.OnEnter
End Sub
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
' add clean-up code here if required
End Sub
Protected Sub OnAuthenticate(ByVal e As WebServiceAuthenticationEvent)
If _eventHandler Is Nothing Then
Return
End If
_eventHandler(Me, e)
If Not (e.User Is Nothing) Then
e.Context.User = e.Principal
End If
End Sub 'OnAuthenticate
Public ReadOnly Property ModuleName() As String
Get
Return "WebServiceAuthentication"
End Get
End Property
Sub OnEnter(ByVal [source] As [Object], ByVal eventArgs As EventArgs)
'Dim app As HttpApplication = CType([source], HttpApplication)
'app = CType([source], HttpApplication)
Dim context As HttpContext = app.Context
Dim HttpStream As System.IO.Stream = context.Request.InputStream
' Save the current position of stream.
Dim posStream As Long = HttpStream.Position
' If the request contains an HTTP_SOAPACTION
' header, look at this message.
'For Each str As String In context.Request.ServerVariables.AllKeys
'If context.Request.ServerVariables(Str) IsNot Nothing Then
'context.Response.Write("<h1>" + Str() + "= " + context.Request.ServerVariables(Str) + "</h1>")
'End If
'Next
If context.Request.ServerVariables("HTTP_SOAPACTION") Is Nothing Then
'context.Response.End()
Return
'Else
'MsgBox(New System.IO.StreamReader(context.Request.InputStream).ReadToEnd())
End If
' Load the body of the HTTP message
' into an XML document.
Dim dom As New System.Xml.XmlDocument()
Dim soapUser As String
Dim soapPassword As String
Try
dom.Load(HttpStream)
'dom.Save("C:\Users\Administrator\Desktop\SoapRequest.xml")
' Reset the stream position.
HttpStream.Position = posStream
' Bind to the Authentication header.
soapUser = dom.GetElementsByTagName("Username").Item(0).InnerText
soapPassword = dom.GetElementsByTagName("Password").Item(0).InnerText
Catch e As Exception
' Reset the position of stream.
HttpStream.Position = posStream
' Throw a SOAP exception.
Dim name As New System.Xml.XmlQualifiedName("Load")
Dim ssoapException As New SoapException("Unable to read SOAP request", name, e)
context.Response.StatusCode = System.Net.HttpStatusCode.Unauthorized
context.Response.StatusDescription = "Access denied."
' context.Response.Write(ssoapException.ToString())
'Dim x As New System.Xml.Serialization.XmlSerializer(GetType(SoapException))
'context.Response.ContentType = "text/xml"
'x.Serialize(context.Response.OutputStream, ssoapException)
'Throw ssoapException
context.Response.End()
End Try
' Raise the custom global.asax event.
OnAuthenticate(New WebServiceAuthenticationEvent(context, soapUser, soapPassword))
Return
End Sub 'OnEnter
End Class ' WebServiceAuthenticationModule
If you are still using an ASP.NET SOAP web service then the easiest way that fits your requirements IMO is to use the ASP.NET Forms authentication with a Membership DB. If you are starting out fresh I'd recommend going with WCF - if you can't/or won't do that this post applies to the "classic" ASP.NET SOAP web services.
To add Forms authentication to a web service:
Configure it just like you would for any other web site but set it to allow access for everyone:
<authorization>
<allow users="*"/>
</authorization>
Implement Login/Logout methods and issue the authentication ticket in the Login method. Further requests to the web service then can use the issued authentication ticket.
All other web methods you want to protect you can then decorate with
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
These methods will now throw a Security exception if a client is not authenticated.
Example for a protected method:
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
[WebMethod(Description = "Your protected method")]
public string Foo()
{
return "bar";
}
Example for Login method:
[WebMethod(Description = "Login to start a session")]
public bool Login(string userName, string password)
{
if (!Membership.Provider.ValidateUser(userName, password))
return false;
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(500),
false,
FormsAuthentication.FormsCookiePath);// Path cookie valid for
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent)
cookie.Expires = ticket.Expiration;
// Add the cookie to the list for outgoing response
if(HttpContext.Current !=null)
HttpContext.Current.Response.Cookies.Add(cookie);
FormsAuthentication.SetAuthCookie(userName, true);
return true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With