Search code examples
c#wcfazurewcf-security

WCF Service TransportWithMessageCredentials Customer validator


I have configured a new Azure hosted WCF service with a custom username validator. The validator class will validate username and passwords from an existing aspnetUsers table in an Azure database.

I have configured the service using TransportWithMessageCredentials binding so the clients will be providing their usernames and passwords in clear text in the request.

My code will then look up the user and get their hashed password from the database and then use this to hash the password sent via the service. If they match then allow the request.

to validate the password I am using this code.

 public static bool checkPassword(string hashedPassword, string password)
    {

        byte[] buffer4;
        if (hashedPassword == null)
        {
            return false;
        }
        if (password == null)
        {
            throw new ArgumentNullException("password");
        }
        byte[] src = Convert.FromBase64String(hashedPassword);
        if ((src.Length != 0x31) || (src[0] != 0))
        {
            return false;
        }
        byte[] dst = new byte[0x10];
        Buffer.BlockCopy(src, 1, dst, 0, 0x10);
        byte[] buffer3 = new byte[0x20];
        Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
        using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(password, dst, 0x3e8))
        {
            buffer4 = bytes.GetBytes(0x20);
        }
        return ByteArraysEqual(buffer3, buffer4);
    }

So my question really is, Is sending the username and passwords in this way secure enough? As everything is going over https I am assuming it is but would like some guidance as I am fairly new to security in general.

The service will also be IP restricted.

Here is my service model config.

 <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceBehaviour">
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyValidatorClass,MyNameSpace" />
          </serviceCredentials>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="HttpBinding" maxReceivedMessageSize="2097152" receiveTimeout="00:02:00" sendTimeout="00:02:00">
        </binding>
        <binding name="HttpsBinding" maxReceivedMessageSize="2097152" receiveTimeout="00:02:00" sendTimeout="00:02:00">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service name="MyService" behaviorConfiguration="MyServiceBehaviour">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="HttpsBinding" contract="MyContract" />
        <host>
          <baseAddresses>
            <add baseAddress="https://MyServiceInAzure.net/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>

And here is what the clients will be using:

Client.Service call = new Client.ServiceClient();
        call.ClientCredentials.UserName.UserName = "MyUsername";
        call.ClientCredentials.UserName.Password = "MyPassword";

        var result = call.PostCall("Hello World");

Thanks


Solution

  • So my question really is, Is sending the username and passwords in this way secure enough?

    Yes. Since everything which send by HTTPS will be encrypted, you could send the user name and password without encrypted manually. Per my opinion, it is not recommend to send user name and password every time when sending request to your service. I suggest you generate a token at the first time and using the token to validate your request for a period of time.