ASP.NET Applicaties Beveiligen met Forms Beveiliging
Door Michiel van Otegem
29 juni 2006
De beveiliging die een operating systeem biedt is voor een webapplicatie vaak te
ingewikkeld, onhandig, en overbodig. In de meeste gevallen is een simpele beveiliging
op basis van een database, XML- of ander bestand met gebruikersgegevens wel genoeg.
ASP.NET biedt voor dit soort applicaties een standaardmechanisme dat gebruik maakt
van HTML-formulieren en cookies.
Hoewel dit artikel ASP.NET 1.x betreft, ligt hetzelfde
mechanisme ten grondslag aan het Membership mechanisme van ASP.NET 2.0, waardoor dit
artikel ook inzicht biedt in hoe het Membership mechanisme werkt wanneer gebruik gemaakt
wordt van een database voor het opslaan van logingegevens.
In het vorige artikel in deze serie
Applicaties beveiligen met Windows beveiliging
(Windows & .NET Juni 2002) heb ik de beveiliging die Windows en Internet Information
Server (IIS) bieden voor webapplicaties uitvoerig besproken. Die vorm van beveiliging
is veruit het beste als het gaat om de veiligheid. Applicaties die waterdicht afgesloten
moeten worden omdat ze bedrijfskritieke gegevens bevatten, zoals bijvoorbeeld een
intranet, zijn derhalve aangewezen op deze vorm van beveiliging. Voor applicaties met
minder strenge veiligheidseisen is deze vorm van beveiliging echter onhandig. Neem
bijvoorbeeld een dating-applicatie. Hierin moet je gebruikers van elkaar onderscheiden
en in principe moet de ene gebruiker geen toegang krijgen tot de gegevens van een andere.
Er is echter geen sprake van bedrijfskritieke gegevens. Het is dus niet nodig om delen
van de applicatie af te schermen voor een gedeelte van de gebruikers. Daar komt bij dat
de dating-applicatie zeer waarschijnlijk veel meer gebruikers heeft dan een intranet.
Om voor al die gebruikers een Windows gebruikersaccount aan te maken is onwenselijk.
Dit is lastig te beheren en bovendien moet je zeker weten dat alle onderdelen van de
server waartoe de gebruikers geen toegang mogen hebben goed afgeschermd zijn voor alle
gebruikers. Dat is een stuk gemakkelijker als alle gebruikers via hetzelfde Windows
gebruikersaccount de webapplicatie gebruiken. Om dat te kunnen bewerkstelligen, moet
er een alternatieve manier zijn om gebruikers te kunnen identificeren. In veel gevallen
is dit een database of een tekst (XML) bestand met gebruikersgegevens. Een dergelijke
aanpak is veel makkelijker te beheren, omdat gebruikers programmatisch toegevoegd,
gewijzigd en verwijderd kunnen worden. Met Windows gebruikersaccounts is dat lastiger,
omdat voor dergelijke opdrachten administratierechten nodig zijn, en die wil je nu juist
niet verlenen aan een webapplicatie vanwege het hoge risico op misbruik. Krijgt iemand
toegang tot gebruikersgegevens die opgeslagen zijn in de database, dan is dit natuurlijk
vervelend, maar de kraker heeft daarmee nog geen toegang tot de rest van het systeem.
Omdat beveiliging die niet gebruik maakt van Windows/IIS geen onderdeel was van ASP en
andere technologieën, zijn hiervoor allerlei verschillende oplossingen bedacht. ASP.NET
biedt echter een gestructureerde standaardoplossing die makkelijk te gebruiken is:
Forms Beveiliging.
Inloggen bij Forms beveiliging gaat via een HTML-formulier (vandaar de naam). Hiermee
log je in op een gebruikersaccount dat vastgelegd staat in web.config, het
configuratiebestand van ASP.NET. Omdat er geen Windows gebruikersaccount gebruikt wordt,
gaat de beveiliging van afzonderlijke mappen op basis van de URL. Dit mechanisme heb ik
in mijn vorige artikel reeds besproken, dus wordt hier geen aandacht meer aan besteed.
Forms beveiliging zorgt ervoor dat als de gebruiker voor het eerst een beveiligde
pagina oproept de gebruiker een loginpagina te zien krijgt. Via deze pagina kan de
gebruiker inloggen, waarna de gebruiker teruggestuurd wordt naar de pagina die hij/zij
oorspronkelijk heeft opgevraagd. Heeft de ingelogde gebruiker niet voldoende rechten,
dan krijgt de gebruiker een pagina te zien die dit vermeld. Dit proces is weergegeven
in figuur 1.

Figuur 1, Schematische weergave Forms beveiliging
Het eerste dat je moet doen om Forms beveiliging te gebruiken is web.config aanmaken of
aanpassen. In het eerste geval kun je de code uit figuur 2 gebruiken.
<configuration>
<system.web>
<authentication mode="Forms">
<forms name=".ASPXLOGIN"
loginUrl="login.aspx"
path="/"
protection="All"
timeout="60" />
</authentication>
</system.web>
</configuration>
Figuur 2, web.config voor Forms beveiliging
Bestaat web.config al in de applicatie, dan moet je op zoek naar het authentication
element en het vervangen door de waarde van het authentication element zoals weergegeven
in figuur 2. Als het element niet bestaat, kun je het direct onder system.web plaatsen
zoals weergegeven in figuur 2. Het mode attribuut van het authentication element geeft
aan dat de applicatie Forms beveiliging dient te gebruiken. Voor Windows beveiliging
was de waarde van dit attribuut "Windows". Het forms element bevat vervolgens de
instellingen die de Forms beveiliging moet gebruiken. Het name attribuut bevat de naam
van de cookie die op de browser geplaatst moet worden om aan te geven dat de gebruiker
is ingelogd. Deze cookie wordt gecontroleerd bij elke pagina die opgevraagd wordt. Is
de cookie niet aanwezig of heeft deze niet de juiste waarde, dan wordt de gebruiker
verwezen naar de login pagina. Dit is de pagina die aangegeven wordt in het loginUrl
attribuut. Dit is een virtueel pad naar de login pagina, dus als deze pagina via de
browser opgevraagd wordt met http://www.voorbeeld.nl/aspnet/beveiliging/login.aspx, dan
moet de waarde van dit attribuut /aspnet/beveiliging/login.aspx zijn. Het path
attribuut heeft hier, in tegenstelling tot wat je zou denken, niets mee te maken. Het
pad dat je daarin opgeeft bepaalt in welke submap van de applicatie de cookie geldig is.
In 99 van de 100 gevallen moet dit gewoon de hoofdmap zijn, zoals in figuur 2. Het
protection attribuut bepaalt hoe de gegevens in de cookie beschermd worden tegen
misbruik. De standaard waarde is All, waarbij de gegevens zowel versleuteld worden als
gecontroleerd op de server. Andere mogelijke waardes voor het protection attribuut zijn
None (geen versleuteling, geen controle), Encryption (alleen versleuteling, geen
controle), en Validation (alleen controle, geen versleuteling). Tenzij de server zeer
zwaar belast wordt is het niet verstandig een andere instelling te gebruiken dan All.
De laatste instelling, timeout bepaalt na hoeveel tijd de cookie verloopt. Wacht de
gebruiker langer dan het daar opgegeven aantal minuten met het opvragen van een nieuwe
pagina, dan moet de gebruiker opnieuw inloggen. Iedere keer dat de gebruiker een nieuwe
pagina opvraagt, wordt deze cookie bijgewerkt, zodat de tijd opnieuw gaat lopen.
Figuur 2 zorgt er alleen voor dat de applicatie Forms beveiliging kan gebruiken. Naast
die instelling heb je nog een aantal dingen nodig, zoals een bron met gebruikers en
wachtwoorden, en een pagina die het inlogproces afhandelt. Verder moet je natuurlijk
aangeven voor welke mappen de beveiliging moet gelden, en welke gebruikers wel/geen
toegang hebben tot die mappen. In z'n simpelste vorm kun je de gebruikers en wachtwoorden
opslaan in web.config. Omdat web.config beveiligd wordt door ASP.NET is deze niet te
downloaden, dus is dit minder onveilig dan je zou denken. Bovendien heb je de keuze om
wachtwoorden versleuteld met SHA1 of MD5 encryptie op te slaan in web.config. In het
onwaarschijnlijke geval dat iemand dan alsnog toegang krijgt tot web.config, zijn de
wachtwoorden dusdanig versleuteld dat ze van weinig nut zijn. Als je de gebruikersnamen
en wachtwoorden opslaat in web.config, dan ziet het forms element uit figuur 2 eruit
als in weergegeven in figuur 3.
<forms name=".ASPXLOGIN"
loginUrl="login.aspx"
path="/"
protection="All"
timeout="60">
<credentials passwordFormat="SHA1">
<user name="michiel"
password="2EBED033C1EF2CB59723B8AC1CF4FF2846751ECA" />
<user name="peter"
password="C4A042B60DC04B84572B00E96EB716C90DE8FE13" />
</credentials>
</forms>
Figuur 3, web.config met gebruikersnamen en wachtwoorden
In figuur 3 zijn de wachtwoorden versleuteld met SHA1 encryptie. Je kunt ook passwordFormat="Clear"
gebruiken en de wachtwoorden in web.config zetten zonder dat ze versleuteld zijn. Dit
is echetr niet aan te raden. Om web.config te gebruiken met SHA1 (of MD5) encryptie
heb je wel een methode nodig om wachtwoorden te versleutelen. Figuur 4 laat de broncode
van een pagina zien waarmee je een met SHA1 of MD5 versleutelde versie van een
wachtwoord op kunt vragen. Deze en alle andere voorbeeld code is te downloaden van
http://www.aspnl.com/aspnl/nl/artikelen/formssecurity.aspx.
<%@ Page Language="VB" %>
<%@ Import Namespace="System.Web.Security" %>
<script runat="server">
Sub Bereken_Click(src As Object, e As EventArgs)
Hash.Text = FormsAuthentication.HashPasswordForStoringInConfigFile(Password.Text, _
Algorithm.SelectedItem.Text)
End Sub
</script>
<html>
<body>
<form runat="server">
Password:
<asp:textbox id="Password" runat="server"/>
<br>
<asp:radiobuttonlist id="Algorithm"
runat="server"
RepeatDirection="Horizontal">
<asp:listitem text="SHA1" Selected="True"/>
<asp:listitem text="MD5"/>
</asp:radiobuttonlist>
<br>
<asp:button id="Bereken"
runat="server"
text="Bereken"
onclick="Bereken_Click"/>
<hr>
Resultaat:
<asp:label id="Hash" runat="server"/>
</form>
</body>
</html>
Figuur 4, Versleutelen van wachtwoorden
Figuur 4 maakt gebruik van de System.Web.Security namespace. Hierin zit alle functionaliteit
die te maken heeft met het beveiligen van een webapplicatie. Onderdeel van die
namespace is het FormsAuthentication object. De HashPasswordForStoringInConfigFile
methode van dit object kun je gebruiken om een wachtwoord te versleutelen, zoals in
figuur 4. Om het versleutelde wachtwoord te gebruiken moet je dit in web.config zetten.
Dat is over het algemeen nog een handmatige taak, aangezien het onwenselijk is om
web.config zeer regelmatig te moeten aanpassen. Het versleutelde wachtwoord wordt in
de pagina van figuur 4 weergegeven. Het spreekt daarom voor zich dat de pagina in
figuur 4 alleen toegankelijk dient te zijn voor de beheerder(s) van de webapplicatie.
In de te downloaden voorbeeldapplicatie staat deze in de admin map, die gebruik maakt
van de web.config in figuur 5.
<configuration>
<system.web>
<authorization>
<allow users="michiel" />
<deny users="*" />
</authorization>
</system.web>
</configuration>
Figuur 5, web.config voor beheermap
web.config in figuur 5 zorgt er voor dat de map waarin dit staat alleen toegankelijk is
voor de gebruiker michiel. Alle andere gebruikers krijgen een bericht te zien dat ze
niet voldoende rechten hebben om pagina's in de betreffende map (en submappen daarvan)
te bekijken.
Met alle instellingen en de mogelijkheid om gebruikers toe te voegen, rest er nog één
ding: het loginscherm. Doordat we hier gebruik maken van een standaard mechanisme, is
ook dit betrekkelijk simpel, zoals je kunt zien in figuur 6.
<%@ Page Language="VB"%>
<%@Import Namespace="System.Web.Security"%>
<script runat="server">
Sub DoLogin(src As Object, e As EventArgs)
If FormsAuthentication.Authenticate(txtLogin.Text, txtPassword.Text) Then
FormsAuthentication.RedirectFromLoginPage(txtLogin.Text, False)
Else
txtMsg.Text = "Login onjuist"
End If
End Sub
</script>
<html>
<body>
<form runat="server">
Loginnaam:
<asp:TextBox id="txtLogin" runat="server" />
<br>
Wachtwoord:
<asp:TextBox id="txtPassword" runat="server"
TextMode="Password" />
<br>
<asp:Button id="btnLogin" runat="server"
Text="Login" OnClick="DoLogin" />
<p>
<asp:Label id="txtMsg" runat="server"
ForeColor="Red"
EnableViewState="False" />
</form>
</body>
</html>
Figuur 6, Loginpagina
Ook de loginpagina maakt gebruik van het FormsAuthetication object van de System.Web.Security
namespace. De Authenticate methode vergelijkt de gegeven gebruikersnaam en wachtwoord
met die zijn opgeslagen in web.config. Zijn de gebruikersnaam en het wachtwoord correct,
dan wordt met de RedirectFromLoginPage methode de gebruiker doorverwezen naar de pagina
die hij/zij oorspronkelijk heeft opgevraagd. Hierbij wordt de gebruikersnaam meegegeven
om te zorgen dat de gebruiker voorlopig niet meer hoeft in te loggen. De tweede parameter
van deze methode geeft aan of er gebruik gemaakt kan worden van een zogenaamde
Persistent Cookie, een cookie die actief blijft, ook als de gebruiker de browser sluit.
In dat geval kan iemand later terug komen zonder opnieuw te moeten inloggen. In dit
geval is de meegegeven waarde False, dus wordt een dergelijke cookie niet gebruikt.
Wil je dit wel, dan moet de waarde True worden. Een andere oplossing is om de gebruiker
de keuze te geven middels een checkbox.
Het standaardmechanisme dat ik hier besproken heb, is aan te passen naar eigen wens.
In een applicatie voor veel gebruikers is het bijvoorbeeld onhandig om gebruikers in
web.config op te slaan, en kun je beter een XML bestand of een database gebruiken. Je
kunt hiervoor de structuur zoals die nu is hetzelfde laten. De gebruikers moeten echter
in de database opgeslagen worden en je moet een eigen functie schrijven ter vervanging
van Authenticate in figuur 6. De code hiervoor is onderdeel van de eerder genoemde
download, maar ik ga er hier niet verder op in. De implementatie van database gestuurde
beveiliging in de download maakt het ook mogelijk om rollen te definiëren, vergelijkbaar
met Windows gebruikersgroepen. Wanneer dat geïmplementeerd is (wat helaas niet mogelijk
is als de gebruikers worden opgeslagen in web.config), kun je web.config uit figuur 5
ook zo aanpassen dat alleen toegang wordt verleend op basis van de groep Beheerders,
zoals weergegeven in figuur 7.
<configuration>
<system.web>
<authorization>
<allow roles="Beheerders" />
<deny users="*" />
</authorization>
</system.web>
</configuration>
Figuur 7, web.config voor beheermap op basis van rollen
Forms beveiliging is makkelijk en snel toe te passen. Je hoeft je niet te verdiepen in
de complexiteit van Windows beveiliging, omdat deze volkomen omzeild wordt. De gebruiker
waaronder de ASP.NET pagina's worden uitgevoerd is daarom ook altijd dezelfde: de
ASPNET gebruiker. Als je Forms beveiliging eenmaal ingesteld hebt in web.config, kun
je verschillende submappen makkelijk anders beveiligen door een web.config bestand in
de betreffende map te plaatsen waarin andere gebruikers toegang wordt verleend. Dit
is veel makkelijker en handiger dan werken op basis van Windows gebruikersaccounts.
Forms beveiliging zal daarom in veel gevallen de voorkeur hebben boven Windows beveiliging.
Dit artikel is eerder verschenen in Windows & .NET Magazine Benelux, september 2002 (huidige naam: NetOpus)
|