Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Securing a web service?

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

like image 374
Stefan Steiger Avatar asked Oct 20 '10 08:10

Stefan Steiger


2 Answers

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
like image 116
Stefan Steiger Avatar answered Sep 19 '22 10:09

Stefan Steiger


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:

  1. Configure it just like you would for any other web site but set it to allow access for everyone:

    <authorization>
        <allow users="*"/>
    </authorization>
    
  2. 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.

  3. 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;
}
like image 24
BrokenGlass Avatar answered Sep 23 '22 10:09

BrokenGlass