Dan Rigsby – Coding Up Style

Developer.Speaker.Blogger

How to throttle a Wcf service, help prevent DoS attacks, and maintain Wcf scalability

Posted by Dan Rigsby on February 20th, 2008

What can you do to prevent denial of service (DoS) attacks on your Wcf services?  A DoS attack occurs when a flood of client requests come into a service and prevent legitimate requests from being made or slow down intended processing.  At the same time, you need to be careful not to stranglehold legitimate requests and activity.

Throttling

Throttling is one mechanism that can be used to help mitigate the load from a DoS attack.  Throttling allows you to "smooth" out the load on the server.
 
Wcf handles throttling through the ServiceThrottlingBehavior class.  This represents a behavior that can be applied to a service.  Behaviors can be applied programmatically:
 
ServiceHost host = new ServiceHost(
    typeof(MyContract),
    new Uri("http://localhost:8080/MyContract"));
host.AddServiceEndpoint(
    "IMyContract",
    new WSHttpBinding(),
    "");
System.ServiceModel.Description.ServiceThrottlingBehavior throttlingBehavior =
    new System.ServiceModel.Description.ServiceThrottlingBehavior();
throttlingBehavior.MaxConcurrentCalls = 16;
throttlingBehavior.MaxConcurrentInstances = Int32.MaxValue;
throttlingBehavior.MaxConcurrentSessions = 10;
host.Description.Behaviors.Add(throttlingBehavior);
host.Open();
 
or through the app.config:
 
<system.serviceModel>
    <services>
        <service
            name="IMyContract"
            behaviorConfiguration="myContract">
            <host>
                <baseAddresses>
                    <add baseAddress="http://localhost:8080/MyContract"/>
                </baseAddresses>
            </host>
            <endpoint
                name="wsHttp"
                address=""
                binding="wsHttpBinding"
                contract="IMyContract">
            </endpoint>
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="myContract">
                <serviceMetadata httpGetEnabled="True" />
                <serviceThrottling
                    maxConcurrentCalls="16"
                    maxConcurrentInstances="2147483647"
                    maxConcurrentSessions="10"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>
 
There are 3 properties supported on the ServiceThrottlingBehavior:
  1. MaxConcurrentCalls (default = 16) [Per-message] The maximum number of messages that can actively be processed.  Each client channel can have one pending message that does not count against this total until the service begins to process it.  Increase this value if you want your service to be able to process a larger message load.
  2. MaxConcurrentInstances (default = Int32.Max)  The maximum number of InstanceContext objects in a service that can execute at one time.
    • What this setting translates into depends on the InstanceContextMode that is set on the ServiceBehaviorAttribute on the service.  If this is set to "PerSession", then this would represent the maximum number of sessions.  If this is set to "PerCall", then this is the maximum number of concurrent calls.  If this is set to "Single", then this value doesn’t really mean anything anymore.
    • When a message comes into the service and the maximum number of InstanceContext objects already exists, then the message goes into a wait pattern until an existing InstanceContext is closed.  This could cause a client to receive a timeout if no connection could be created in the allotted amount of time.
  3. MaxConcurrentSessions (default = 10) [Per-channel] The maximum number of sessions that a service can accept at one time.  This setting only affects "session" enabled channels, so a channel like BasicHttpBinding will not be affected.  Once this threshold is reached, no channels will not be accepted to the service.  Each listener object can have one pending channel session that does not count against this total until the service beings to process it.  Increase this value if you want to allow more than 10 concurrent sessions to be connected at any given time.
    • If you get exceptions like this, you may need to increase this value:

"The open operation did not complete within the allotted timeout of 00:00:59.9989999. The time allotted to this operation may have been a portion of a longer timeout."


"The socket transfer timed out after 00:00:59.9979998. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout."

You could set all the these values to Int32.Max.  This is probably a good think when you really want to perform some load testing of the server. (This is how I discovered the usefulness of these settings)  However it is important to understand what consequences there are to doing this.

Recommendations:

If your InstanceContext is set to "PerCall" you should set maxConcurrentSessions and maxConcurrentCalls to the same value since each call is its own session.  You should target this value to be at least 25-30.  However you shouldn’t need to go higher than 64.

If your InstanceContext is set to "PerSession" you should set maxConcurrentCalls to be at least 25-30.  Your maxConcurrentSessions should be the number of users you want to have concurrently connected.

If your InstanceContext is set to "Single" you should use the same settings as "PerSession", but the maxConcurrentSessions will only apply if session support is active (this is set with the SessionMode attribute on the ServiceContractAttribute).

Quotas

Another potential area that is vulnerable to a DoS attack is where the client may force the server to allocate a significant amount of memory over what should be used.   To help prevent this you can use Quotas.  When a quota is exceeded a QuotaExceededException is thrown.  Without Quotas a malicious message could attempt to access all available memory can create an OutOfMemoryException, or access all available stacks to cause a StackOverflowException.  The best part of the QuotaExceededException is that the message causing it can just be discarded and the service can keep on running.  Without quotas the resulting exceptions could cause the service to terminate.

The most useful quota to work with to mitigate DoS attacks is the maxRecievedMessageSize on the binding itself.  This setting restricts the maximum messages size so that a client can’t send messages that are too large and flood the system.  The default value is 65536 bytes.

<bindings>
    <netTcpBinding>
        <binding name="netTcp"
            maxReceivedMessageSize="2147483647"
            maxConnections="2147483647">
            <readerQuotas
                maxDepth="64"
                maxStringContentLength="2147483647"
                maxArrayLength="2147483647"
                maxBytesPerRead="4096"
                maxNameTableCharCount="16384"/>
        </binding>
    </netTcpBinding>
</bindings>
 

Other quotas exist on the ReaderQuotas property that can be used to restrict message complexity.  This can help mitigate excessive use of endpoint processing resources like CPU and memory. These are:

  1. MaxDepth (default = 32) The maximum nested node depth.
  2. MaxStringContentLength (default = 8192)  The maximum string length allowed by the reader.  You may need to increase this value if you anticipate potentially large messages.
  3. MaxArrayLength (default = 16384)  The maximum allowed array length.  How many items can be in a single array in the message.
  4. MaxBytesPerRead (default = 4096) The maximum allowed bytes returned for each read.  This is the number of bytes consumed by a single call from the reader to Read().  In practice this is used to limit the size of start tags, since a start tag must be completely buffered for the message to be processed.  This can be a common attack area.  It is recommended to keep this value at its default.
  5. MaxNameTableCharCount (default = 16384) The maximum number of characters in a table name.

Some bindings such as NetTcpBinding offer a maxConnections property.  This setting on the client indicates the maximum number of connections to be pooled for subsequent reuse.  On the server, this setting is the maximum number of connections allowed to be pending dispatch.  The default is 10.

10 Responses to “How to throttle a Wcf service, help prevent DoS attacks, and maintain Wcf scalability”

  1. Leonid Ganeline Says:

    Thanks, Dan!It resolved my issues!

  2. Marc Desjardins Says:

    Thanks for this article, I was suspecting BizTalk all the way but finally theses settings solve my issue.

    Regards,

  3. Ofer Says:

    Thanks Dan,
    I solved a problem of sending a FileStream on files of size 13MB form the client to the WCF server.
    The value of “2147483647″ you wrote help me solve it!

    A question: How to set readerQuotas.maxArrayLength in C#?
    The intellisense shows a ReaderQuotas property in a var of type WsHttpBinding, but the intellisense doesn’t show any properties.
    Can you write any code snippet showing how to set the readerQuotas.MaxArrayLength?

    Ofer

  4. Sean Says:

    Q: How to set readerQuotas.maxArrayLength in C#?
    The intellisense shows a ReaderQuotas property in a var of type WsHttpBinding, but the intellisense doesn’t show any properties.
    Can you write any code snippet showing how to set the readerQuotas.MaxArrayLength?

    A: You will need to type-cast the binding instance from System.ServiceModel.Channels.Binding to the class of the binding you are using, such as WSHttpBinding,
    then you will be able to see MaxArrayLength property through intellisense.

  5. Dave Says:

    Thanks Dan,

    You’ve saved me from what’s caused a weeks worth of headaches with WCF services randomly timing out for now apparent reason!

  6. abraham Says:

    I would like to verify the URL of applications calling my WCF web service. In .NET 1.1(.asmx) days, I could use HttpContext.Current.Request.Url for this purpose. How do I do this in a WCF world?

    Is there a better alternative to this?

  7. Dan Rigsby Says:

    Check out this blog post to get the client’s address: http://www.danrigsby.com/blog/index.php/2008/05/21/get-the-clients-address-in-wcf/

  8. David Says:

    I am new to WCF, so there maybe questions here that should be obvious. The service that I’ve created is fairly simple– nothing fancy and much of it auto-gen’ed by the project template.

    I am seeing winsock resource issues in a WCF service and found your post to a forum (http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/1b65fa82-763a-4851-85e5-5063ce65d3ee/) when I searched on the error message, “Insufficient winsock resources available to complete socket connection initiation.” That post lead me to this article and this looks like something that will help me resolve the problem, but there appears to be insufficient details to apply this infomration to my specific usage.

    In my fairly simple service, there is no ServiceHost instance created and there are no calls to .Open(). As a result, I am not sure where to set the thottling behavior. I can set the thottling in the web.config file, but even that is a bit unclear to me. Your recommendations are to look to the InstanceContext setting… I can’t find that set explicitly in my code, so I assume that it’s using a default value, but I am unsure what that is.

  9. Alagar rajan Says:

    Thanks Dan! This article solved my one week headache!

  10. Mohammad Yousri Says:

    Dan,
    Kindly help me in fixing this issue. I didn’t sleep for two days trying to fix this exception.

    let me explain the scenario exactly:
    1) A client application calls the WCF service, the WCF service is straightforward just call from the DB.
    2) The WCF service is SSL
    3) The client actually, calls the WCF 150 times. according to business needs.

    and finally, give me the following exception:
    Exception has been thrown –> Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

    Okay…and the service at this moment is down….. I must restart the IIS and then the service is up and working again, but the service can answer if the calls is less than 90 or 80 and may be 60

    I’ve tried everything, I’ve put the binding in the client side:

    and then I’ve put the following in the server side…

    and finally, the same result….poor WCF …..
    1) I don’t know may be DOS attack
    2) Or because the service calls the DB many times.
    :) …..need your help to SLEEP

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>