Flexibel ApiKey-gebruik bij WCF Data Services

Een APIKey is een unieke identificatie die meegestuurd wordt bij iedere
aanroep van een service. Hiermee kan op server niveau de toegang gereguleerd
worden. Een APIKey is niet zo zeer voor security bedoeld, de key wordt namelijk
onversleuteld meegestuurd in de url van de aanroep, maar het is een eenvoudige
maar krachtige bescherming voor de beschikbaarheid van de service.

Flexible gymnist

Door een API-Key te eisen van de gebruikers van jouw service kun je de
‘heavy’ users eruit pikken en zij die echt een bedreiging zijn voor de
beschikbaarheid van de service, kunnen geblokkeerd worden. Ik denk niet dat dit
ook gelijk de oplossing voor DDos aanvallen zijn, maar hiermee wordt intensief
gebruik/misbruik voorkomen.

Nu is dit op zich niet nieuw. Ik wilde het toepassen en kwam op de
volgende link uit: http://blogs.msdn.com/b/rjacobs/archive/2010/06/14/how-to-do-api-key-verification-for-rest-services-in-net-4.aspx . Voor iedereen die in WCF een APIKey moet implementeren, is dit een prima startpunt. Ik vat hier even samen hoe dit we hier eenvoudig gebruik van kunnen maken:

  • Download het voorbeeld project van Ron Jacobs
  • Rip de APIKeyAuthorization.cs class en plaats die in jouw eigen WCF Service solution in een apart project.
  • Leg een referentie naar dit project.
  • Rip de APIKeys.xml en plaats die in de App_Data map. Bij voorkeur pas je ook de guid’s aan.
  • Als je gebruik wilt maken van de mogelijkheid om de APIKey verficatie uit en aan te schakelen, implementeer dan de Global.APIKeyVerification property in de
    global.asax. Anders moet verwijzing uit de APIKeyAuthorization.cs class weggehaald worden
  • Voeg aan de WCF Behaviour de volgende verwijzing toe (let op de namespace en dll-verwijzing):
<serviceAuthorization
  serviceAuthorizationManagerType="WCFWebHttpLibrary.APIKeyAuthorization,
  WCFWebHttpLibrary" />

De volgende call wordt nu dus goedgekeurd:

http://localhost/AspNetWebSite/WcfDataService/$metadata/?APIkey=918704ec-4811-45b6-a169-16bae3df69a8

Hiermee is de service nu dus van een API-key beveiliging voorzien. Alleen
gebruikers die een APIKey uit het lijstje meegeven, kunnen gebruikmaken van de
dienst. Dus mocht een gebruiker de server laten roken, haal dan zijn API-Key uit
de xml in de App-Data map.

Bekijk hier een filmpje over de implementatie:
http://channel9.msdn.com/shows/Endpoint/endpointtv-How-to-do-API-Key-Verification-with-a-WCF-WebHttp-REST-service/

Ik heb dit zelf ook zo geïmplementeerd en in eerste instantie werkte het
prima. De (Restful) service werkt perfect icm. Javascript (Lees: jQuery). Maar
helaas liep ik later tegen een basaal probleem aan. Ik wilde de WCF Data Service
gaan consumeren via een Service Reference in een C# Client applicatie. De
aanroep ziet er dan zo uit:

Uri serviceUri = new Uri("htt p://localhost/AspNetWebSite/WcfDataService");
ServiceReference.Entities service = new ServiceReference.Entities(serviceUri);
var items = (from item in service.Clients select  item).ToList();

Ok, hoe geef ik nu mijn APIKey door, waar laat ik ‘m? Want bij iedere
aanroep moet deze APIKey doorgegeven worden. Misschien werkt het ook wel als ik ‘m in de Uri doorgeef? Maar de aanroep met:

Uri serviceUri =new
Uri("htt p://localhost/AspNetWebSite/WcfDataService?
     APIkey=918704ec-4811-45b6-a169-16bae3df69a8");

Resulteerde in:

{“Expected an absolute, well formed http URL without a query or
fragment.\r\nParameter name: serviceRoot”}

Het probleem is dus dat ik de request wil verrijken met de APIKey. Tot nu
toe hebben we geprobeerd met de querystring, is die op een andere manier uit te
breiden? Na wat verder zoeken kwam ik uit op het SendingRequest event van de
proxy uit:

service.SendingRequest += new
  EventHandler<System.Data.Services.Client.SendingRequestEventArgs>
              (service_SendingRequest);

Hierbij wordt de mogelijkheid geboden om de request te bekijken en te
manipuleren. Helaas blijkt de querystring hier readonly te zijn. Dit viel dus
tegen maar het bleek wel mogelijk om de header van request te manipuleren! Dus we kunnen wel:

static void service_SendingRequest(object
  sender,System.Data.Services.Client.SendingRequestEventArgs e)
{
  // when using api in the header...
  e.Request.Headers.Add("APIkey", "918704ec-4811-45b6-a169-16bae3df69a8");
}

Is het dan ook mogelijk om de header aan de serverkant uit te lezen?

Ja, dat is dus mogelijk. Naast de querystring kan ook de header
uitgelezen worden. Ik heb dus de volgende kleine aanpassing gedaan in de code
van Ron Jacobs:

public string GetAPIKey(OperationContext operationContext)
{
  // Get the request message
  var request = operationContext.RequestContext.RequestMessage;
  // Get the HTTP Request
  var requestProp =
    (HttpRequestMessageProperty)request.
      Properties[HttpRequestMessageProperty.Name];
  // Get the query string
  NameValueCollection queryParams =
  HttpUtility.ParseQueryString(requestProp.QueryString);
  // Return the API key (if present, null if not)
  string apiKey = queryParams[APIKEY];
  // Is the API Key available in the querystring?
  if (apiKey == null)
  {
    // Is the API Key available in the header?
    apiKey = requestProp.Headers[APIKEY];
  }
  return apiKey;
}

Mocht de APIKey niet in de querystring voorkomen, dan onderzoeken we de
header… Hiermee is het toepassen van de APIKey heel flexibel geworden.

Hoewel minder elegant is er ook een eenvoudiger mogelijkheid om een APIKey te implementeren, zonder de ServiceAuthorization implementatie. Bij een WCF
DataService kan ook de methode OnStartProcessingRequest ge-override worden:

protected override void OnStartProcessingRequest(ProcessRequestArgs args)
{
  var queryParams = HttpUtility.ParseQueryString(
                args.OperationContext.AbsoluteRequestUri.Query);
  string apiKey = queryParams[APIKEY];
  if (apiKey == null)
  {
    apiKey = args.OperationContext.RequestHeaders[APIKEY];
  }
  if (CheckValidAPIKey(apiKey)) // TODO : Implement your own check
  {
    base.OnStartProcessingRequest(args);
  }
  else
  {
    throw new System.Web.Services.Protocols.SoapException();
  }
}

.Net ontwikkelaars kunnen nu via de header de APIKey doorgeven aan de WCF
Data Service zonder dat hun framework roet in het eten gooit. En ook
ontwikkelaars die de OData service aanroepen met bv. Javascript of Objective C
(IPhone) kunnen prima uit de voeten met de “fat url” met daarin de APIKey.

Advertenties