c# - password - wcf security
Catch-22 previene el servicio TCP WCF transmitido asegurable por WIF; arruinando mi Navidad, salud mental (1)
Tengo un requisito para asegurar un punto final del servicio net.tcp de WCF transmitido usando WIF . Debe autenticar las llamadas entrantes contra nuestro servidor token. El servicio se transmite porque está diseñado para transferir grandes cantidades de datos n cosas.
Esto parece ser imposible. Y si no puedo evitar el problema, mi Navidad se arruinará y me beberé hasta la muerte en una cuneta mientras los compradores alegres pisan mi cuerpo que se está enfriando lentamente. Totes en serio, chicos.
¿Por qué es esto imposible? Aquí está el Catch-22.
En el cliente, necesito crear un canal con GenericXmlSecurityToken que obtengo de nuestro servidor token. No hay problema.
// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();
¿Dije "no hay problema"? Problemo. De hecho, el problema del estilo NullReferenceException
.
"Hermano", le pregunté a Framework, "¿incluso no verificaste?" El Framework estaba en silencio, así que lo desarmé y encontré que
((IChannel)(object)tChannel).
GetProperty<ChannelParameterCollection>().
Add(federatedClientCredentialsParameter);
fue la fuente de la excepción y que la llamada GetProperty
devolvía null
. Entonces, ¿WTF? Resulta que si IssuedToken
Message security y establezco el tipo de credencial del cliente en IssuedToken
, esta propiedad ahora existe en ClientFactory
(protip: no hay equivalente de "SetProperty" en IChannel, el bastardo).
<binding name="OMGWTFLOL22" transferMode="Streamed" >
<security mode="Message">
<message clientCredentialType="IssuedToken"/>
</security>
</binding>
Dulce. No más NREs. Sin embargo, ahora mi cliente tiene una falla al nacer (aún lo amo, aunque). Excavando a través de los diagnósticos de WCF (prototipo: haz que tus peores enemigos hagan esto después de aplastarlos y conducirlos delante de ti, pero justo antes de disfrutar los lamentos de sus mujeres y niños), veo que es por una falta de seguridad entre el servidor y el cliente.
La actualización solicitada no es compatible con ''net.tcp: // localhost: 49627 / MyService''. Esto podría deberse a enlaces incompatibles (por ejemplo, seguridad habilitada en el cliente y no en el servidor).
Comprobando los diags del anfitrión (otra vez: aplastar, conducir, leer registros, disfrutar las lamentaciones), veo que esto es cierto
Tipo de protocolo application / ssl-tls se envió a un servicio que no admite ese tipo de actualización.
"Bien, sí", le digo, "¡Voy a activar la seguridad de los mensajes en el host!" Y lo hago Si desea saber cómo se ve, es una copia exacta de la configuración del cliente. Buscar.
Resultado: Kaboom.
El enlace (''NetTcpBinding'', '' http://tempuri.org/ '') admite la transmisión que no se puede configurar junto con la seguridad del nivel de mensaje. Considere elegir un modo de transferencia diferente o elegir la seguridad del nivel de transporte.
Por lo tanto, mi host no puede ser transmitido y asegurado a través de tokens . 22 capturas.
tl; dr: ¿Cómo puedo asegurar un punto final Net.tcp WCF transmitido usando WIF?
WCF tiene problemas en algunas áreas con la transmisión (Te estoy mirando, MTOM 1 ) debido a un problema fundamental en la forma en que no realiza la autenticación previa de la manera en que la mayoría de la gente piensa que debería funcionar (solo afecta las solicitudes posteriores para ese canal) , no es la primera solicitud) Bien, este no es exactamente su problema, pero por favor siga al pie de la letra y llegaré al suyo al final. Normalmente, el desafío HTTP funciona así:
- el cliente acceda al servidor de forma anónima
- el servidor dice, lo siento, 401, necesito autenticación
- cliente golpea el servidor con token de autenticación
- servidor acepta.
Ahora, si alguna vez intentas habilitar la transmisión MTOM en un punto final WCF en el servidor, no se quejará. Pero, cuando lo configura en el proxy del cliente (como debería, deben coincidir con los enlaces) explotará en una muerte ardiente. La razón de esto es que la secuencia de eventos anterior que WCF intenta evitar es la siguiente:
- el cliente transmite un archivo de 100MB al servidor de forma anónima en una única POST
- servidor dice lo siento, 401, necesito autenticación
- el cliente vuelve a transmitir un archivo de 100MB al servidor con un encabezado de autenticación
- servidor acepta.
Observe que acaba de enviar 200MB al servidor cuando solo necesitaba enviar 100MB. Bueno, este es el problema. La respuesta es enviar la autenticación en el primer intento, pero esto no es posible en WCF sin escribir un comportamiento personalizado. De todos modos, estoy divagando.
Tu problema
Primero, déjame decirte que lo que estás intentando es imposible 2 . Ahora, para que dejes de girar tus ruedas, déjame decirte por qué:
Me sorprende que estés vagando en una clase similar de problema. Si habilita la seguridad de nivel de mensajes, el cliente debe cargar toda la secuencia de datos en la memoria antes de que pueda cerrar el mensaje con la función hash habitual y la firma xml requerida por ws-security. Si tiene que leer toda la secuencia para firmar el mensaje individual (que no es realmente un mensaje, pero es una transmisión continua única), entonces puede ver el problema aquí. WCF tendrá que transmitirlo una vez "localmente" para calcular la seguridad del mensaje, luego transmitirlo nuevamente para enviarlo al servidor. Esto es claramente una tontería, por lo que WCF no permite seguridad a nivel de mensajes para la transmisión de datos.
Entonces, la respuesta simple aquí es que debe enviar el token como un parámetro para el servicio web inicial, o como un encabezado SOAP y usar un comportamiento personalizado para validarlo. No puede usar WS-Security para hacer esto. Francamente, esto no es solo un problema de WCF: no veo cómo podría funcionar prácticamente para otras pilas.
Resolviendo el problema MTOM
Esto es solo un ejemplo de cómo resolví mi problema de transmisión de MTOM para la autenticación básica, así que quizás podría tomar las agallas de esto e implementar algo similar para su problema. El quid de la cuestión es que para habilitar su inspector de mensajes personalizado, debe deshabilitar toda noción de seguridad en el proxy del cliente (permanece habilitado en el servidor), además del nivel de transporte (SSL):
this._contentService.Endpoint.Behaviors.Add(
new BasicAuthenticationBehavior(
username: this.Settings.HttpUser,
password: this.Settings.HttpPass));
var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding;
binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.None; // Do not provide
Tenga en cuenta que he desactivado la seguridad del transporte aquí porque lo proporcionaré utilizando un inspector de mensajes y un comportamiento personalizado:
internal class BasicAuthenticationBehavior : IEndpointBehavior
{
private readonly string _username;
private readonly string _password;
public BasicAuthenticationBehavior(string username, string password)
{
this._username = username;
this._password = password;
}
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
var inspector = new BasicAuthenticationInspector(
this._username, this._password);
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
internal class BasicAuthenticationInspector : IClientMessageInspector
{
private readonly string _username;
private readonly string _password;
public BasicAuthenticationInspector(string username, string password)
{
this._username = username;
this._password = password;
}
public void AfterReceiveReply(ref Message reply,
object correlationState) { }
public object BeforeSendRequest(ref Message request,
IClientChannel channel)
{
// we add the headers manually rather than using credentials
// due to proxying issues, and with the 101-continue http verb
var authInfo = Convert.ToBase64String(
Encoding.Default.GetBytes(this._username + ":" + this._password));
var messageProperty = new HttpRequestMessageProperty();
messageProperty.Headers.Add("Authorization", "Basic " + authInfo);
request.Properties[HttpRequestMessageProperty.Name] = messageProperty;
return null;
}
}
Por lo tanto, este ejemplo es para cualquier persona que padezca el problema de MTOM, pero también como un esqueleto para que implemente algo similar para autenticar su token generado por el servicio de token seguro de WIF principal.
Espero que esto ayude.
(1) Datos grandes y transmisión continua
(2) Seguridad de mensajes en WCF (ver "desventajas")