Typically the way to protect here is to have a timestamp on the message and some expiration policy on the server. This is a very simplified description though, here are all the details.
The sending party attaches this timestamp to the SOAP message:
The receiving party can then decide based on some policy how much after the message was created it is suspected as a replay.
But what is this service policy? We can see an "expires" element in the client message itself, then why does the service need an additional policy?
The answer is that there is a duplicate protection against replay attacks. The client can decide its own policy and express it inside the message. The server should respect this policy and also adds its own policy. For example:
In this case the message is considered valid by the service, but the service is expected to respect the client policy and reject the message. the situation gets more interesting when we add real network latency and clocks synchronization into the picture.
In Wcf there are two somehow confusing settings to control the expiration:
There are various other settings which relate to tolerance for clock differences and I will write a separate post on them some other time.