+ One of the trickiest, and yet most important, areas + of testing web sites is the security. + Testing these schemes is one of the core goals of + the SimpleTest web tester. +
+ +
+
+Basic HTTP authentication
+
+
+ If you fetch a page protected by basic authentication then + rather than receiving content, you will instead get a 401 + header. + We can illustrate this with this test... +
+class AuthenticationTest extends WebTestCase { + function test401Header() { + $this->get('http://www.lastcraft.com/protected/'); + $this->showHeaders(); + } +} ++ This allows us to see the challenge header... +
File test
++HTTP/1.1 401 Authorization Required +Date: Sat, 18 Sep 2004 19:25:18 GMT +Server: Apache/1.3.29 (Unix) PHP/4.3.4 +WWW-Authenticate: Basic realm="SimpleTest basic authentication" +Connection: close +Content-Type: text/html; charset=iso-8859-1 ++
+class AuthenticationTest extends WebTestCase { + function test401Header() { + $this->get('http://www.lastcraft.com/protected/'); + $this->assertAuthentication('Basic'); + $this->assertResponse(401); + $this->assertRealm('SimpleTest basic authentication'); + } +} ++ Any one of these tests would normally do on it's own depending + on the amount of detail you want to see. + +
+ Most of the time we are not interested in testing the + authentication itself, but want to get past it to test + the pages underneath. + As soon as the challenge has been issued we can reply with + an authentication response... +
+class AuthenticationTest extends WebTestCase { + function testAuthentication() { + $this->get('http://www.lastcraft.com/protected/'); + $this->authenticate('Me', 'Secret'); + $this->assertTitle(...); + } +} ++ The username and password will now be sent with every + subsequent request to that directory and subdirectories. + You will have to authenticate again if you step outside + the authenticated directory, but SimpleTest is smart enough + to merge subdirectories into a common realm. + +
+ You can shortcut this step further by encoding the log in + details straight into the URL... +
+class AuthenticationTest extends WebTestCase { + function testCanReadAuthenticatedPages() { + $this->get('http://Me:Secret@www.lastcraft.com/protected/'); + $this->assertTitle(...); + } +} ++ If your username or password has special characters, then you + will have to URL encode them or the request will not be parsed + correctly. + Also this header will not be sent on subsequent requests if + you request a page with a fully qualified URL. + If you navigate with relative URLs though, the authentication + information will be preserved. + +
+ Only basic authentication is currently supported and this is + only really secure in tandem with HTTPS connections. + This is usually enough to protect test server from prying eyes, + however. + Digest authentication and NTLM authentication may be added + in the future. +
+ +
+
+Cookies
+
+
+ Basic authentication doesn't give enough control over the + user interface for web developers. + More likely this functionality will be coded directly into + the web architecture using cookies and complicated timeouts. +
++ Starting with a simple log-in form... +
+<form> + Username: + <input type="text" name="u" value="" /><br /> + Password: + <input type="password" name="p" value="" /><br /> + <input type="submit" value="Log in" /> +</form> ++ Which looks like... + +
+
+ ++ Let's suppose that in fetching this page a cookie has been + set with a session ID. + We are not going to fill the form in yet, just test that + we are tracking the user. + Here is the test... +
+class LogInTest extends WebTestCase { + function testSessionCookieSetBeforeForm() { + $this->get('http://www.my-site.com/login.php'); + $this->assertCookie('SID'); + } +} ++ All we are doing is confirming that the cookie is set. + As the value is likely to be rather cryptic it's not + really worth testing this. + +
+ The rest of the test would be the same as any other form, + but we might want to confirm that we still have the same + cookie after log-in as before we entered. + We wouldn't want to lose track of this after all. + Here is a possible test for this... +
+class LogInTest extends WebTestCase { + ... + function testSessionCookieSameAfterLogIn() { + $this->get('http://www.my-site.com/login.php'); + $session = $this->getCookie('SID'); + $this->setField('u', 'Me'); + $this->setField('p', 'Secret'); + $this->clickSubmit('Log in'); + $this->assertWantedPattern('/Welcome Me/'); + $this->assertCookie('SID', $session); + } +} ++ This confirms that the session identifier is maintained + afer log-in. + +
+ We could even attempt to spoof our own system by setting + arbitrary cookies to gain access... +
+class LogInTest extends WebTestCase { + ... + function testSessionCookieSameAfterLogIn() { + $this->get('http://www.my-site.com/login.php'); + $this->setCookie('SID', 'Some other session'); + $this->get('http://www.my-site.com/restricted.php'); + $this->assertWantedPattern('/Access denied/'); + } +} ++ Is your site protected from this attack? + + + +
+ If you are testing an authentication system a critical piece + of behaviour is what happens when a user logs back in. + We would like to simulate closing and reopening a browser... +
+class LogInTest extends WebTestCase { + ... + function testLoseAuthenticationAfterBrowserClose() { + $this->get('http://www.my-site.com/login.php'); + $this->setField('u', 'Me'); + $this->setField('p', 'Secret'); + $this->clickSubmit('Log in'); + $this->assertWantedPattern('/Welcome Me/'); + + $this->restart(); + $this->get('http://www.my-site.com/restricted.php'); + $this->assertWantedPattern('/Access denied/'); + } +} ++ The WebTestCase::restart() method will + preserve cookies that have unexpired timeouts, but throw away + those that are temporary or expired. + You can optionally specify the time and date that the restart + happened. + +
+ Expiring cookies can be a problem. + After all, if you have a cookie that expires after an hour, + you don't want to stall the test for an hour while the + cookie passes it's timeout. +
++ To push the cookies over the hour limit you can age them + before you restart the session... +
+class LogInTest extends WebTestCase { + ... + function testLoseAuthenticationAfterOneHour() { + $this->get('http://www.my-site.com/login.php'); + $this->setField('u', 'Me'); + $this->setField('p', 'Secret'); + $this->clickSubmit('Log in'); + $this->assertWantedPattern('/Welcome Me/'); + + $this->ageCookies(3600); + $this->restart(); + $this->get('http://www.my-site.com/restricted.php'); + $this->assertWantedPattern('/Access denied/'); + } +} ++ After the restart it will appear that cookies are an + hour older and any that pass their expiry will have + disappeared. + + +