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