Blog Archives

Don’t Be Afraid of Imperfection

Marco Arment’s post about being “addicted to PHP” talked about fear involved in switching languages:

Whichever language I choose to replace PHP will have its own problems that will take me years to master, but by the time I know whether I chose the “right” language, I’ll have invested far too much time in it to practically switch any meaningfully sized project to another language: exactly the situation I’m in now with PHP.

The fear of making the “wrong” choice actually makes the familiar, mastered PHP more attractive. That’s the problem Jeff’s identifying, and it’s very real. If you can get PHP programmers to agree that they need to stop using it, the first question that comes up is what to use instead, and they’re met with a barrage of difficult choices and wildly different opinions and recommendations.

I’d like to respond to that by saying “it’s not that hard.” More specifically, I think that these kinds of fears are blown out of proportion out of some misguided drive for perfection.

Web apps are not driven by perfection; this isn’t kernel code that has to be as optimized as possible. It’s not an electronic voting machine where governmental inspectors are going to be poring over each line of code to check it for legitimacy. No, web apps are driven by making things that work. To make things that work in a given language, you don’t have to be a “master” of that language. You don’t have to know the intricacies of how MRO works in Python to write a web app. If you want to write SQLAlchemy? Sure, then knowing how MRO works might be useful to write the best code for something that’s going to be used by tons of people – but most things you write aren’t going to be SQLAlchemy.

Essentially, people like Marco are letting the perfect become the enemy of the good. They’re so focused on “mastering such-and-such language” that they don’t realize that mastery isn’t required to reap the benefits of choosing a different language. You don’t have to be as respected as Guido in the Python community to create good things with Python. Heck, you don’t even have to follow PEP8 or any of the other “pythonic” idioms to create good things with Python. Sure, another Python coder looking at your code will think you’re a bit weird, but weird code is not the same as broken code. Weird code can be refactored as desired as you learn more about the language and what’s considered standard in the Python ecosystem.

So the next time you go to start a personal project, Marco, I’d suggest this: try stepping outside of the comfort zone,  trying something new, and not worrying about it being perfect. If you “pick wrong” it’s not actually that bad; you can make a different choice with the next project. The only reason it seems like a huge investment is because your goalpost is set unreasonably high.

Yet Another Way You Shouldn’t Implement Password Recovery

A couple of weeks ago I had the dubious pleasure of calling a company to tell them that they had a security vulnerability in their site that you could drive a truck through. I’m not going to name names, but given that the issue is now patched, I think it’s a good lesson on things to avoid when designing your own application.

I’ll show you the relevant source code first and let you see if you can figure it out.

// forgot.php ("forgot password" functionality)

if(!empty($_POST['username'])) {
    ob_start();
    // send the request
    CURLHandler::Post(LOGIN_APP_URL . 'resettoken', array('username' => $_POST['username']));
    $result = ob_get_contents();
    ob_end_clean();

    $result = json_decode($result);
    if ($result->success == true) {
        $resetUrl = SECURE_SERVER_URL . 'resetpass.php?un=' . base64_encode($_POST['username']) . '&token=' . $result->token;
        $resetUrl = '<a title="Password Recovery" href="' . $resetUrl . '">' . $resetUrl . '</a>';
        sendTemplateEmail($_POST['username'], 'recovery', array('url' => $resetUrl));
        $msg= '<p>Login information will be sent if the email address ' . $_POST['username'] . ' is registered.</p>';
    } else {
        $msg = '<p>Sorry, unable to send password reset information. Try again or contact an administrator.</p>';
    }
}

There’s actually a couple of things wrong here.

The most problematic flaw (and the one that actually caused the security vulnerability) is that LOGIN_APP_URL happens to point to a server that is accessible to the public internet. This means that anyone who wanted to could bypass this script and make a request directly to its resettoken endpoint, supplying any username they want, and get a valid password reset token for that username! Then all they had to do was manually construct the URL for the reset password page with the username and their acquired token to be able to acquire access to that account.

There’s another more subtle issue here, though: why is an HTTP request being made that returns a password reset token in the first place? What should be happening here is that the same code which generates the reset token should also send the email, thus avoiding the need to expose the reset token generator via an external interface in the first place.

(There’s also the slight “wtf” of using PHP’s output buffer to get the results of a cURL call, rather than using CURLOPT_RETURNTRANSFER, but that’s not a security issue.)