One of the most common flaws I am seeing in applications is the lack of mutual authentication. Most systems authenticate that the user connecting to their system is a valid user. What is lacking is that the client verifies that it is connecting to who it thinks it is connecting to.
One of the big examples of this is WiFi. If I have a wireless router named "myrouter" and set my laptop to use that router things work great. If I go down the street and someone else has a router named "myrouter", my laptop automatically tries to connect to it. This is because the WiFi implementation does not verify that I am connecting to who I think I am connecting to.
This happens a lot on the service level of applications. The client app resolves a DNS name like www.myservice.com to it's IP address and then sends over some form of authentication. Now what happens if I somehow manage to change the DNS record for www.myservice.com to point to my IP address which contains a service with the same name and same methods exposed to it? The client will connect and divulge its authentication data to this fake service. I can now use the authentication data that you gave me authenticate against the real system and do whatever nefarious things I desire.
The solution for this is for clients to verify that the service it is talking to is actually the service you want to be talking to. This is best accomplished by using some form of shared secret. This takes on many forms but a few methods:
- Ask the server to answer a shifting question. i.e. ask the server to hash a certain string. If the hash the server returns matches what you expect then the server might be the one you want (or an attacker managed to replicate the algorithm)
- Use windows authentication. For WCF the mutual authentication is implied in this case.
- Use certificate authentication. This is the best way to do this task but can be a pain to implement.