2010-10-20 25 views
7

Pregunta: Tengo un sistema de administración de documentos y estoy creando interfaces de servicios web para la base de datos.¿Garantizar un servicio web?

Todo funciona hasta ahora, solo que ahora mismo, es totalmente seguro, todos pueden acceder a él.

¿Cómo puedo incorporar la contraseña o la autenticación de clave privada-pública?

Solo puedo encontrar las "mejores prácticas" y el uso de "usuario de Windows" o autenticación de pasaporte. Pero necesito la autenticación de un usuario y una contraseña almacenada en la base de datos, o mejor para una clave privada RSA almacenado para cada usuario de servicios web en la base de datos ...

Editar:
tengo que usar .NET Framework 2.0 en un entorno ASP.NET

Respuesta

6

La solución es escribir un módulo http propia con una mezcla de código proporcionado por MSDN y CodeProject. Incluyendo correcciones propias de errores de MS, y luego agrega este encabezado de soap personalizado al servicio web.

<SoapHeader("Authentication", Required:=True)> 

Este es el módulo:

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 
2

Si está trabajando con WCF, existe una manera fácil de implementar la seguridad mediante certificados X509. Implementando un enlace con el modo de seguridad 'Mensaje' y clientCredentialType 'Nombre de usuario' es posible garantizar esta seguridad de forma automatizada.

La validación se puede realizar a través de una clase que anula un método Validar.

http://msdn.microsoft.com/en-us/library/aa702565.aspx

+1

+1, pero desafortunadamente, estoy limitado a ASP.NET con framework 2.0, entonces no WCF. –

+0

Operaciones, estoy perdido en ese caso, entiendo que la comunicación en el nivel de transporte se puede hacer también con certificados X509. Tal vez el proceso de validación contra SQL Server será más 'manual'. De todos modos, puede encontrar más información en: http://msdn.microsoft.com/en-us/library/ms996415.aspx – IoChaos

5

Si todavía está utilizando un servicio web ASP.NET SOAP, la forma más sencilla que se ajusta a sus necesidades IMO es utilizar la autenticación de formularios ASP.NET con un DB de membresía. Si está empezando de nuevo, le recomiendo ir con WCF. Si no puede/no va a hacer eso, esta publicación se aplica a los servicios web ASP.NET SOAP "clásicos".

Para añadir autenticación de formularios a un servicio web:

  1. Configurar al igual que lo haría con cualquier otro sitio web, pero la pusieron para permitir el acceso para todo el mundo:

    <authorization> 
        <allow users="*"/> 
    </authorization> 
    
  2. Implemente los métodos de inicio de sesión/cierre de sesión y emita el ticket de autenticación en el método de inicio de sesión. Las solicitudes posteriores al servicio web pueden usar el ticket de autenticación emitido.

  3. Todos los otros métodos web que desea proteger a continuación, puede decorar con

    [PrincipalPermission (SecurityAction.Demand, autenticados = true)]

Estos métodos ahora lanzará una excepción de seguridad si un cliente no está autenticado

Ejemplo de un método protegido:

[PrincipalPermission(SecurityAction.Demand, Authenticated = true)] 
[WebMethod(Description = "Your protected method")] 
public string Foo() 
{ 
    return "bar"; 
} 

Ejemplo de método de ingreso:

[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; 
} 
+0

Y luego deberá llamar al inicio de sesión y a la salida cada vez que quiera usar el servicio web, con el sistema de transmisión blackbox y un comportamiento indefinido si el cliente o el servidor fallan en el medio. Y quién sabe si olvida agregar PrincipalPermission a un solo método ... Nono, cualquier cosa que quiera ser seria debe denegar el acceso por defecto, y solo tomar los datos de inicio de sesión como un parámetro de servicio, y no como un método. –

+0

No es diferente de la autenticación de formularios de IU habitual, es aún más fácil hacerlo bien con las desventajas mencionadas que con las suyas propias. Además, obtienes funciones Roles/Membresía gratis. Punto tomado en forma segura por defecto, sin embargo. – BrokenGlass

0

Si WS va a ser consumido a través del protocolo SOAP, el que puede poner en práctica el Seguridad a través del encabezado SOAP:

using System.Web.Services; 
using System.Web.Services.Protocols; 

namespace Domain.WS 
{ 
    [Serializable] 
    public class SoapWSHeader : System.Web.Services.Protocols.SoapHeader, ISoapWSHeader 
    { 
     public string UserId { get; set; } 
     public string ServiceKey { get; set; } 
     public ApplicationCode ApplicationCode { get; set; }   
    }  

    [WebService(Namespace = "http://domain.some.unique/")]   
    public class MyServices : System.Web.Services.WebService 
    { 
     public SoapWSHeader WSHeader; 
     private ServicesLogicContext _logicServices; 

     public MyServices() { _logicServices = new ServicesLogicContext(new LogicInfo() {...}); } 

     [WebMethod, SoapHeader("WSHeader", Direction = SoapHeaderDirection.InOut)] 
     public Result WSMethod1(Int32 idSuperior) 
     { 
      _logicServices.ThrowIfNotAuthenticate(WSHeader); 
      return _logicServices.WSMethod1(idSuperior) as Result; 
     } 
    } 
} 

namespace Domain.Logic 
{ 
    [Serializable]  
    public class ServicesLogicContext : ServicesLogicContextBase 
    { 
     protected ISoapWSHeader SoapWSHeader { get; set; } 
     public ServicesLogicContext(LogicInfo info) : base(info) {} 

     public IResult WSMethod1(Int32 idSuperior) 
     { 
      IResult result = null; 
      //-- method implementation here... 
      return result; 
     } 

     public void ThrowIfNotAuthenticate(ISoapWSHeader soapWSHeader) { 
      this.SoapWSHeader = soapWSHeader; 
      if (SoapWSHeader != null) 
      { 
       if (!ValidateCredentials(soapWSHeader)) 
       { 
        throw new System.Security.SecurityException(Resources.ValidationErrorWrongCredentials); 
       } 
      } 
      else { throw new System.Security.SecurityException(Resources.ValidationErrorWrongWSHeader); } 
     } 
     private bool ValidateCredentials(ISoapWSHeader soapWSHeader) { 
      return (SoapWSHeader.UserId.Equals("USER_ID") && SoapWSHeader.ServiceKey.Equals("PSW_1")); 
     } 
    } 
} 

Nota: este código no está completo, esto solo muestra los aspectos principales sobre cómo usar el encabezado SOAP.

+0

Si hubiera prestado atención, entonces se habría dado cuenta de que esto es exactamente lo que hice. Solo que he movido la autenticación de incorporar en cada WebMethod, que es bs, a dicho módulo HTTP. Con la ventaja añadida de que niega el acceso a cada llamada SOAP sin encabezado. –

+0

Ok, significa que ha resuelto su problema. Saludos, estamos aplicando esa técnica para asegurar algunos WS, también, dependiendo de su aplicación, podría usar algún certificado a través de HTTPS. – ArBR