About the Author

Chris Shiflett

Hi, I’m Chris: entrepreneur, community leader, husband, and father. I live and work in Boulder, CO.


All posts for Mar 2006

Zend_Filter Reviewed on SitePoint

Maarten Manders graciously took the time to review the Zend_Filter component of the Zend Framework. I think criticism and public discussion are healthy. Unfortunately, I don't have time to offer a very detailed response, but I'll try to remedy that with a followup post later in the week.

One thing worth noting is that Zend_Filter was extracted from Zend_InputFilter shortly before the preview release. Because it provides such a rigid approach to input filtering, some contributors pointed out that Zend_InputFilter does not provide enough flexibility for simple cases such as filtering an argument passed to a method. (Discussions have taken place on the mailing list about whether such granular error checking is beneficial.) To remedy this, I decided to separate the filtering methods from the input filtering framework that uses them.

Zend_Filter is far from impressive, in my opinion, because it's simply a library of static methods for filtering data. (I do think the naming convention is more consistent than alternatives.) Combined with Zend_InputFilter, however, it offers a much more structured approach to input filtering, and I think it's a big step in the right direction toward more secure PHP programming.

I should really explain Zend_InputFilter in more detail, but since I'm pressed for time, here are some quick responses to Maarten's specific comments:

  • isGreaterThan() and isLessThan() are superfluous now.
  • I'd love to see some suggestions (in the form of use cases) for a friendlier isDate(). :-)
  • I'm not sure who wrote isHostname(), but refactoring it is near the top of my TODO list.
  • Concerning the name for isRegex(), the is prefix is important for reasons of consistency, so perhaps isMatch()?
  • isName() is a candidate for removal.
  • isPhone() only supports US numbers, because it's a stub. This is one of the few methods where internationalization is going to affect the requirements substantially from one country to the next. It also brings up the question of where to draw the line.
  • I have considered adding some escaping methods to Zend_Filter and Zend_InputFilter, but I hate to erode the distinction between filtering and escaping. I have much more to say about this, but it will have to wait.

I'd like to thank Maarten for taking the time to compose his thoughts and impressions, and I'd also like to thank everyone who has done the same on the mailing lists.

OSCON 2006

The talks for OSCON 2006 have been selected. For the first time in OSCON's history, there is going to be a PHP testing tutorial. :-) Geoff and I will give it, and it will cover Apache-Test (and test-more.php), Simple-Test, PHPUnit, and phpt.

I'll also be giving a tutorial on PHP security (which seems to be getting more popular every year), and I hope to have a second version of the PHP Security Guide ready in time. Andrew is giving a tutorial on web application security and the OWASP Guide. We plan to make these fit together nicely.

The talks I'm giving are PHP Security Testing and The Truth about XSS. Both are brand new talks and will cover the latest and greatest techniques. They should be a lot of fun.

Adam is going to be speaking about the SOAP extension in PHP 5. Terry is going to be talking about Ajax design patterns as well as giving a case study about Plaxo eCards.

The official announcement, full schedule, and registration should be available soon. OSCON is always a blast, and I hope to see you there.

Who Practices Test-Driven Development (TDD)?

Harry Fuecks maintains a good blog over at Sitepoint and recently wrote a piece on Evaluating PHP Applications.

Noel Darlow, a regular contributor to the Sitepoint forums (and someone whose opinion I respect), comments:

I think testing is a good indicator of the developer's ability. I'll be looking for tests being used to drive the design and not just the odd unit test stuck on after the fact.

I'm a big advocate of testing, and although I won't claim to be an expert on the topic, I have to question whether it's necessary that tests drive the design. It seems plausible that someone could choose to write tests after the implementation, and I don't think ignorance is necessarily the reason.

On the other hand, I find myself taking this approach more and more. For example, when I'm designing a function or class, I start with an example that describes how I want to use it:

<?php 

$auth 
= new myAuth;

$auth->username 'chris';
$auth->password 'mypass';

if (
$auth->checkLogin()) {
    
/* SUCCESS */
} else {
    
/* FAILURE */
}

?>

(Sorry if my ad hoc example doesn't live up to your standards.)

I'll usually type this out at least once, so it's not just imagined. In order to implement myAuth, I can choose to keep this example in mind as I write the code, or I can take this simple example and turn it into a real test or two:

<?php 

include 'test-more.php';

plan(2);

$auth = new myAuth;

{
    
/* Test Valid Credentials */
    
$auth->username 'chris';
    
$auth->password 'mypass';

    
ok($auth->checkLogin(), 'test valid credentials');
}

{
    
/* Test Invalid Credentials */
    
$auth->username 'chris';
    
$auth->password 'notmypass';

    
ok(!$auth->checkLogin(), 'test invalid credentials');
}

?>

It's so easy to write tests, I might even write some for a blank username, blank password, etc. I can do all this without writing a single line of code, and when I begin writing the code, I already have a simple test suite to run - I don't have to write some quick ad hoc tests just to see whether things are working as planned. When all the tests pass, I know I've accomplished my initial design goals.

How many of you test? How many of you write your tests first?

Note: If you happen to be attending PHP Quebec later this week, I'm giving a talk that will discuss some simple approaches to testing.

Easy Cookie Hacking

When penetration testing a web app, it's hard to avoid a few manual tests. For example, you might try a simple cross-site scripting (XSS) exploit:

<script>alert('XSS')</script>

Or, perhaps its cousin:

"><script>alert('XSS')</script><"

Testing with GET and POST is easy enough, because you can use the web app's own forms or create one yourself. Manipulating cookies isn't quite as easy, but you don't actually need to send your own raw HTTP requests or use a Firefox extension. You just need a bit of JavaScript.

You've probably used the javscript: URL scheme in some way - for example, entering javascript: into the location bar of Firefox brings up the JavaScript console. It's pretty easy to use this to manipulate document.cookie - just enter something like the following into your browser's location bar:

javascript:document.cookie='comment_name=Your+Name;path=/'

Just change Your+Name to be your own name, and this will set the cookie my blog uses to recognize you when you're posting a comment. (You need to already be on my web site, of course.) This is just a simple demonstration - the usefulness of this technique is clearer when you use it to inject malicious data in order to make sure cookie values are being filtered properly. There is some hassle involved, because you need to escape the value to be preserved in the context of JavaScript and escape it again to be preserved in the context of a URL:

javascript:document.cookie='comment_name=%22%3E%3Cscript%3Ealert%28\%27XSS\%27%29%3C%2Fscript%3E%3C%22;path=/'

Luckily, with a small collection of common injections, it's easy to perform some mild penetration testing. You can even bookmark them.

del.icio.us RSS Feeds

I've been using del.icio.us since 2004, and it has served me well. For those who still haven't tried it, it's basically a bookmark manager with a social twist. You can get a (nearly) real-time list of everyone's PHP bookmarks, everyone's MySQL bookmarks, my bookmarks, or the blogs I read.

At the bottom of each page, there is a convenient link to an RSS feed for that page. For example, you can get a feed of the blogs I read. There's only one problem - the feeds are limited to 30 items. That's why the list of blogs on my web site is truncated. A few people have noticed this, wondering why they're not on my list. Of course, I can fix this problem easily enough - I just need to start using the del.icio.us API.

This limitation isn't as easy to work around in other contexts. For example, the PHPSC Library is an organized collection of links related to PHP security. In order to keep the library current and relevant, we aggregate the bookmarks of the various members using del.icio.us. Unfortunately, if a member adds more than 30 resources to the library, some are truncated from that member's feed. The API requires HTTP basic authentication, so that's not a good solution, because I don't want to ask members to share their access credentials.

It's still possible to work around this. I can revert back to the days of screen scraping (which would be easier if the pages were valid XHTML), but doesn't that seem lame, considering RSS and friends are supposed to eliminate this need? I could use a tag naming scheme that uses a counter to split a tag into groups, so that no single tag has more than 30 items. There are probably a number of other hacks that would solve the problem, but none seem particularly elegant.

Hopefully I'm just missing something obvious, but my quick search of the del.icio.us mailing list uncovered this thread that mentions browsing del.icio.us directly as a solution. Ugh.

Agile PHP Testing at PHP Quebec

Next week, I'll be speaking at PHP Quebec in Montreal about testing PHP applications. Agile PHP Testing is a new talk that focuses on really simple approaches to testing, and I hope to demonstrate how creating a test suite is close to what you're probably already doing anyway.

Most PHP testing frameworks don't seem to follow the "PHP way" of solving problems as simply and directly as possible. Isn't there a better way? I think so. You won't learn a lot of testing theory in this talk - there's no time for that. Instead, I show you how to get right down to business and test your PHP applications with a simple PHP testing library and some straightforward testing practices.

PHP Quebec has a grassroots feel to it, and I had a blast last year. Toby's photos can give you a feel for the conference.

I hope to see you there.

php|architect: March 2006 Edition

Another edition of php|architect has been published. I was especially excited to read this one, because it's Ilia's first month writing Security Corner. It's nice to see a topic explained from a different point of view, and there is still too little interest in security within the PHP community. (In other words, this is a rare opportunity.)

He discusses cross-site request forgeries, an attack first mentioned in php|architect back in 2003. Sadly, it remains one of the most dangerous, yet relatively unknown attacks. It deserves more attention.

I was a bit concerned that Ilia might liken it to cross-site scripting (XSS), because he has in the past:

As far as CSRF, I don't really consider it to be separate from XSS [...] CSRF is a subset of cross site scripting in my opinion.

(I didn't debate this point at the time, because the Myspace worm had caused a fair amount of confusion by combining both attacks, an approach I had been researching). My concern was quickly put to rest:

Closer examination reveals that CSRF and XSS are entirely different beasts.

There are still a few minor points where Ilia and I seem to disagree, but that's the real beauty of having different points of view. Hopefully Ilia will forgive me for it. :-) My points of contention are:

  • There are many different attack vectors for CSRF - it's best to find the commonality between them, so you can eliminate the root cause of the problem. Focusing on one attack vector can yield weak solutions.
  • An attack can be launched from any web site, so preventing your own site from being a platform for CSRF does not protect you from the attack itself.

In order to explain these items further, I want to share a secret with you. The comment form on my blog is vulnerable to CSRF. No images are allowed, and no XSS vulnerability exists (to my knowledge), but it's still vulnerable. This was a design decision I made when I first decided to allow comments, because:

  • I already allow anonymous comments, so there's nothing to gain by forging a request from someone else. In other words, I'm not putting readers at risk.
  • I was curious to see whether CSRF's lack of popularity applies to exploits as well as vulnerabilities.

Surprisingly, no one has exploited the vulnerability, despite the fact that there have been a countless number of XSS attempts. That may all change now that I've mentioned it, but when it does, I'll implement a simple and effective safeguard. Can you guess what it is?

Scalable Internet Architectures

From Mark Taber's Blog, I just found out that Theo Schlossnagle's book, Scalable Internet Architectures is scheduled for July.

I've been anticipating this book for a few years. It's based on a very popular tutorial Theo has given at both ApacheCon and OSCON. (The tutorial placed 5th in the OSCON Top Ten last year.)

IBM's PHP Reading List

Daniel Krook and Carlos Hoyos of IBM have created a PHP reading list covering the following topics:

  1. Overview
  2. Getting Started
  3. Development
  4. Integration
  5. Extension
  6. Migration
  7. Security
  8. Community and News
  9. Other Resources

Like Laura, I'd like to thank them for including my book as well as my blog and talks.

Thanks, IBM!

SERVER_NAME Versus HTTP_HOST

A question was asked on the New York PHP mailing list concerning $_SERVER['SERVER_NAME'] and $_SERVER['HTTP_HOST']:

Aren't these the same thing?

There were several informative replies within the first few minutes, but there's more to this question than most people realize. In fact, I'm reminded of a blog post from Zeev last year where he warns against $_SERVER['SERVER_NAME']:

And how about $SERVER_NAME ($_SERVER['SERVER_NAME']), which actually depends on the Host header sent by the remote user, and can therefore be spoofed under certain circumstances?

Rasmus disagreed:

No, that isn't affected by the Host header.

If you want to get to the bottom of this debate, follow along with this brief example. Imagine your web server has a default host set up as follows:

UseCanonicalName Off
ServerName example.org

The ServerName directive might seem like the only thing that affects $_SERVER['SERVER_NAME'], but is this a safe assumption? To determine what affect the Host header has, if any, create an index.php in the document root of the default host with the following code:

<?php 
 
echo "HTTP_HOST [{$_SERVER['HTTP_HOST']}]\n"; 
echo "SERVER_NAME [{$_SERVER['SERVER_NAME']}]"; 
 
?>

You can test several different values for Host easily enough with telnet:

telnet example.org 80

Here are a few of my tests and corresponding results. For each test, I show the exact request and the content of the response, so please feel free to test this yourself.

1. No Host, HTTP/1.0

Request:

GET / HTTP/1.0

Result:

HTTP_HOST []
SERVER_NAME [example.org]

With no Host, SERVER_NAME is the value from Apache.

2. Empty Host, HTTP/1.0

Request:

GET / HTTP/1.0
Host:

Result:

HTTP_HOST []
SERVER_NAME []

With an empty Host, SERVER_NAME is empty.

3. Empty Host, HTTP/1.1

Request:

GET / HTTP/1.1
Host:

Result:

HTTP_HOST []
SERVER_NAME []

With an empty Host, SERVER_NAME is also empty with HTTP/1.1.

4. XSS Host, HTTP/1.1

Request:

GET / HTTP/1.1
Host: <script>alert('XSS')</script>

Result:

HTTP_HOST [<script>alert('XSS')</script>] 
SERVER_NAME [&lt;script&gt;alert('XSS')&lt;/script&gt;]

With a non-empty Host, SERVER_NAME is the HTML-escaped host value.

5. SQL Injection Host, HTTP/1.1

Request:

GET / HTTP/1.1
Host: chris' --

Result:

HTTP_HOST [chris' --] 
SERVER_NAME [chris' --]

As you can see by the results, Zeev was right. Under certain circumstances, the Host header can affect $_SERVER['SERVER_NAME']. The ServerName directive is used when the Host header is absent, and apparently $_SERVER['SERVER_NAME'] is escaped with something like htmlentities().

Sometimes, it's hard to tell whether a particular element in $_SERVER can be affected by the HTTP request (ask Sean about PHP_SELF), so I find it easier to treat everything from $_SERVER just as if it were something like $_GET or $_POST.

As Dan Cech points out, the UseCanonicalName directive affects this behavior. From httpd.conf, "With this setting off, Apache will use the hostname:port that the client supplied, when possible. This also affects SERVER_NAME and SERVER_PORT in CGI scripts."

ZF Tutorial Update

I just updated my Zend Framework Tutorial. It now works with version 0.1.2 of the framework. I also fixed a few minor bugs and improved my explanation of several topics, particularly the front controller.

This tutorial barely scratches the surface, so be sure to check out the other resources already available:

I'm sure there are other resources I've missed (some I just can't find at the moment), so please let me know if you've written some notes of your own, and I'll update this entry.

The mailing list has been very active, and many bugs have been discovered and corrected as a result of developer feedback. Mike has some good commit messages, such as "speling eror" and "tix fypo," but my favorite commit is this:

--- trunk/NEWS.txt	2006-03-12 21:12:50 UTC (rev 598)
+++ trunk/NEWS.txt	2006-03-12 21:21:36 UTC (rev 599)
@@ -8,7 +8,10 @@
   to 4.0. Reported by Greg Neustaetter (Mike)
 - Fixed bug in Zend_Controller_Dispatcher_Token::setParams(). Reported by Rob
   Allen. (Chris)
+- Fixed bug in Zend_Controller_Dispatcher_Token::setParams().
+  Reported by Rob Allen. (Mike)

Within 15 minutes of a bug being reported on the list, it has been fixed twice. If Mike broke his lines at 80 characters, even our explanations in NEWS.txt would have been identical. :-)

Gosling Didn't Get the Memo

I just read a smart critique of James Gosling's recent interview. It's worth a read.

For those who don't have the time or inclination to read the interview, it's basically the creator of Java trying to explain why PHP, Perl, Python, and Ruby do not pose a serious threat. One of his first statements is, "PHP and Ruby are perfectly fine systems, but they are scripting languages." In the critique I just mentioned, Ryan Tomayko dismantles this argument with a style reminiscent of The Daily Show:

James pulled this directly out of "Effective Java Advocacy Beans," section 6.8.3 "Dealing with questions on dynamic languages."

Ryan provides an excerpt:

First, call anything not statically compiled a "scripting language." Attempt to insinuate that all languages without an explicit compilation step are not to be taken seriously and that they are all equivalently shitty. Best results are achieved when you provide no further explanation of the pros and cons of static and dynamic compilation and/or typing and instead allow the reader to simply assume that there are a wealth of benefits and very few, if any, disadvantages to static compilation.

The entire response is both entertaining and informative.

Note: Jeff Moore also provides some good commentary on the interview.

Zend Framework Tutorial

I'm a little late announcing this, but I wrote a quick Zend Framework Tutorial for php|architect over the weekend. It was written in haste and likely contains some errors as a result, but I've arranged to have the tutorial updated frequently. Therefore, it should gradually improve as well as continue to remain relevant as the framework evolves.

The demo app from the tutorial is also available:

I hope you enjoy it.

Zend Framework Preview

A preview of the Zend Framework is now available:

Please download it, read the manual, and give it a go. If you find any problems, please report them, so they can be fixed.

Congrats to everyone who worked on this, especially Mike. Our journey has just begun.

Note: I'll have more to say soon.

Another Google XSS Vulnerability

I don't want to provide any links or details before it is fixed, but Google has another cross-site scripting (XSS) vulnerability. It is more serious than the previous one, because:

  • It works with any character encoding. (You can be a victim even if you don't use a vulnerable browser.)
  • It exists in multiple domains (www.google.com and mail.google.com).
  • It is much easier to exploit. (It was discovered by accident.)

I highly recommend that you do not use Google's personalized features or access your Gmail account with JavaScript enabled until this vulnerability has been fixed.

Brain Bulb Webcasts

I've been playing around with Snapz Pro lately. I originally intended to use it to help spice up some of my talks by offering prepared demos directly in Keynote, but I have also decided that it would be useful to offer various talks and demos to the PHP community.

The first Brain Bulb Webcast is PHP Security Audit HOWTO, a short video of one of my conference talks:

It's not nearly as professional as Terry's talk, but hopefully the quality will improve with time.

Thanks for watching!