Secure RESTful Web Service by WCF Web API, No HTTPS, Seriously?

What if you want to build a secure RESTful web service without using HTTPS? Simple, you just need to implement your secure way to transfer data from endpoints to endpoints. This article written by @Jeff Atwood can definitely give you some hints on how to ensure your message transfer safe. So I will not explain it in the post and suppose you have the basic knowledge of cryptography.

RESTful Web Service

WCF Web API is really handy to build a RESTful web service. Your service class looks as simple as following

[ServiceContract]
public class PlayerResource
{
    private readonly IPlayerRepository _playerRepository;

    public PlayerResource(IPlayerRepository playerRepository)
    {
        _playerRepository = playerRepository;
    }

    [WebInvoke(Method = "POST", UriTemplate = "Create")]
    public Player CreatePlayer(Player player)
    {
        return _playerRepository.Save(player);
    }
}

And with a little bit configuring in your global.asax file

public class MvcApplication : HttpApplication
{
    protected void Application_Start()
    {
        RouteTable.Routes.MapServiceRoute("api/players");
    }
}

And your RESTful web service is ready for up and running.

Secure RESTful Web Service

In order to make sure the incoming and outgoing messages are safe. I will first introduce Message and EncryptedMessage and a helper class EncryptionHelper to encrypt and decrypt the messages.

public class Message
{
    public string AppId { get; set; }

    public string Data { get; set; }

    public string Id { get; set; }

    public string TimeStamp { get; set; }

    public byte[] GenerateFingerprint();

    public bool ValidateHash(byte[] fingerprint);
}
public class EncryptedMessage
{
    public string AppId { get; set; }

    public byte[] Fingerprint { get; set; }

    ///<summary>
    /// The 3DES key used to encrypt/decrypt the message 
    /// </summary>
    public byte[] Key { get; set; }

    ///<summary>
    /// Encrypted message
    /// </summary>
    public byte[] Message { get; set; }
}
public interface IEncryptionHelper
{
    EncryptedMessage Encrypt(Message message);

    Message Decrypt(EncryptedMessage encryptedMessage);
}

Then, we need to change our service class a little bit to support secure data transfer.

[ServiceContract]
public class PlayerResource
{
    private readonly IPlayerRepository _repository;
    private readonly IEncryptionHelper _helper;

    public PlayerResource(IPlayerRepository repository, IEncryptionHelper helper)
    {
        _repository = repository;
        _helper = helper;
    }

    [WebInvoke(Method = "POST", UriTemplate = "Create")]
    public EncryptedMessage CreatePlayer(EncryptedMessage encryptedMessage)
    {
        Message message = _helper.Decrypt(encryptedMessage);

        Player player = Json.Decode(message.Data);

        player = _repository.Save(player);

        message = new Message
        {
            AppId = "Our App Id",
            Id = Guid.NewGuid().ToString(),
            Data = Json.Encode(player),
            TimeStamp = DateTime.UtcNow.ToString()
        }

        return _helper.Encrypt(message);
    }
}

Now, your messages are secured.

Message Handler

Maybe you have noticed that passing in and out encrypted message is not comfy at all. Can we keep the same simplicity of the service function before introduce secure implementation?

Well, the answer is positive. WCF encourage you to implement cross-cutting function (aka AOP) through a couple of handlers (message handler, operation handler). What we need to do is roll out our own message handler to encrypt and decrypt incoming and outgoing messages. So what we need to do is to pass encrypt/decrypt actions to a message handler. The implementation looks as follows,

public class SecuredMessageHandler : DelegatingChannel
{
    private readonly IEncryptionHelper _encryptionHelper;

    public SecuredMessageHandler(HttpMessageChannel innerChannel,
                                 IEncryptionHelper encryptionHelper)
        : base(innerChannel)
    {
        _encryptionHelper = encryptionHelper;
    }

    protected override Task SendAsync(HttpRequestMessage request,
                                      CancellationToken cancellationToken)
    {
        request = DecryptMessageIfTheMessageIsSecuredMessage(request);

        return base.SendAsync(request, cancellationToken)
            .ContinueWith(task =>
                {
                    HttpResponseMessage response = task.Result;

                    response = EncryptMessageIfTheMessageIsSecuredMessage(response);

                    return response;
                });
    }

    private HttpResponseMessage EncryptMessageIfTheMessageIsSecuredMessage(HttpResponseMessage response)
    {
        string data = response.Content.ReadAsString();

        var message = new Message
                          {
                              AppId = "Our App Id",
                              Id = Guid.NewGuid().ToString(),
                              Data = data,
                              TimeStamp = DateTime.UtcNow.ToString()
                          };

        EncryptedMessage encryptedMessage = _encryptionHelper.Encrypt(message);

        var content = new StringContent(Json.Encode(encryptedMessage), Encoding.UTF8,
                                        response.Content.Headers.ContentType.MediaType);

        response.Content.Dispose();

        response.Content = content;

        response.Headers.Add(XMessageType, XMessageTypeSecured);

        return response;
    }

    private HttpRequestMessage DecryptMessageIfTheMessageIsSecuredMessage(HttpRequestMessage request)
    {
        var encryptedMessage = request.Content.ReadAs();

        Message message = _encryptionHelper.Decrypt(encryptedMessage);

        var content = new StringContent(message.Data, Encoding.UTF8,
                                        request.Content.Headers.ContentType.MediaType);
                                        
        request.Content.Dispose();

        request.Content = content;

        return request;
    }
}

And your service function is as simple as it was at first.

    [WebInvoke(Method = "POST", UriTemplate = "Create")]
    public Player CreatePlayer(Player player)
    {
        return _playerRepository.Save(player);
    }
Advertisements

8 thoughts on “Secure RESTful Web Service by WCF Web API, No HTTPS, Seriously?

    • a couple of classes have been changed in WebApi since this post published.

      Based on WebApi Preview 5, you should probably apply your custom DelegatingHandler as follows,

      var config = new WebApiConfiguration
      {
      MessageHandlerFactory = ()=>
      new List{ new SecureMessageHandler()) }
      };

      RouteTable.Routes.MapServiceRoute(“resource”, config);

  1. Great article.. Thanks for the explanation.. May be its just me.. but I couldn’t quite follow how the SecuredMessageHandler gets hooked up to the WebAPI to secure the communication. Would appreciate if you could give me a clue for that. Thanks!

    • Thanks for stopping by Vinay.

      WebApi has been updated to preview 6 since I wrote this. So this post could be a bit outdated…

      Please refer to my previous comment which shows how to configure a Handler. Hope it helps.

      Cheers,

    • Cheers man.
      In fact, this is a preface to my implementation rest api invoking with api key without SSL
      But eventually, this could break the nature of rest api as the api key has to be put in content body to prevent hijacking
      So my 2 cents are, hire SSL if you wanna build serious rest api :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s