- 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. - - -