The Smeg It Was

Over the course of April 10 through 12, untold millions of people worldwide observed the resurrection of something that once had died but now is risen, in perpetual hope and expectation of its eventual return in glory.

I am talking, of course, about Red Dwarf: Back to Earth, a three-part miniseries that marked the first new Red Dwarf to be made since 1999.

I don’t need to tell you I’m a Red Dwarf fan — my three computers are named holly, kryten, and queeg, after all. I first discovered and fell in love with the series back in high school, when the local PBS station would air Red Dwarf, Red Green, and two episodes of Monty Python’s Flying Circus in an epic two-hour block on Sunday nights. I believe they eventually stopped doing that because federal law prohibits PBS from airing anything that awesome.

Back in 1999, Red Dwarf season 8 ended (after Rimmer kicks Death in the groin and tells him that “only the good die young”) with the words “The End”, followed a few seconds later with “THE SMEG IT IS”. Alas, until this month it was the end. There was no season 9, and the movie never got made.

All hope for new Red Dwarf was lost, until I stumbled across by chance the Red Dwarf page on TV Tropes a couple days after Easter, and noticed the tiny little paragraph closing the write-up:

A three-part story Back To Earth was recently aired across the Easter Weekend of 2009 on digital channel Dave, putting an end to the complete lack of any new TV or book output since 1999.

The next step was obvious.

So, was Back to Earth worth a 10-year wait? Sort of. (minor spoilers ahead)

Part 1 starts off is textbook Red Dwarf, with things having reverted back to the premise of the pre-seasion 6 episodes (though taking place 9 years after season 8): just Lister, Rimmer (a hologram again), The Cat, and Kryten aboard the titular Red Dwarf (with Holly’s absence given a brief hand wave). Nothing great by any means, but not bad either.

The transition from the fight with the giant squid in the water tank to Katerina‘s appearance is very clumsy, and though the abruptness of it can be explained in light of the reveal in Part 3, surely there could’ve been a cleaner transition between the two. Even ignoring the impossibility of the Red Dwarf supporting two holograms at one time (especially seeing how it happened in the last two episodes of season 1), the rest of Part 1 does little other than to set up the premise of Parts 2 and 3.

The storyline that fills most of Parts 2 and 3 is sort of weird. Remember that scene in Spaceballs where the bad guys watch a VHS copy of Spaceballs to figure out where the heroes are? It’s sort of like that, but stretched out over half an hour.

More specifically, the crew get sucked through a swirly thing into the “real world”, which is eagerly awaiting the premiere of Back to Earth. They find a promotional DVD case of the three-parter, and while there’s no actual disc inside, they learn from the back of the case that Back to Earth is the end of Red Dwarf. Since in the “real world” universe they’re just fictional characters, they’ll cease to exist once Red Dwarf has ended, and so they seek out their creator to plead for more life. Hilarity ensues.

This level of self-referentiality is tough to do well, especially as the backbone of the entire plot. While Back to Earth manages well enough, being set in the “real world” loses some of the feel of being Red Dwarf. Which isn’t to say there isn’t excellence to be found within; take, for example, this scene that mercilessly spoofs the magical image enhancement capabilities found in TV shows:

Also, you just know someone in Britain is making a real-life Carbug.

Oh, and lest you think my constant scare-quoting of the “real world” is some sort of spoiler, I’m doing it because of the one infuriating difference between the real real world and the “real world” as shown in Back to Earth: the “real world” apparently got ten seasons of Red Dwarf instead of eight. Lucky smeggers.

In any event, it’s obvious that just as Back to Earth is about the Red Dwarf crew asking the show’s creator to keep making episodes, Back to Earth has the ulterior purpose of testing the market for interest in, well, new Red Dwarf episodes, and not-so-subtly asking The Powers That BBC to fund it.

Maybe the whole “ten seasons” thing wasn’t an error but a promise — the half of Grant Naylor still working on Red Dwarf has apparently said he’s not interested in making season 9 but is interested in making season 10, whatever that might mean.

And we all saw how well season 8′s promise of more Red Dwarf turned out. But one can hope for new life, and isn’t that what the Easter season is all about?

Well, that and Peeps.

Comments Off

Error monads for fun and profit

Last time, we corrected the security flaws in our simple Happstack.State demo program, but were left with some stinky error propagation logic. In particular:

  1. Using Maybe to carry error information, instead of using it to carry the result of a successful computation, violating the usual convention with its use.
  2. Unwieldy tangles of conditionals to check for each possible error, obfuscating the normal path of execution.

Neither of these is insurmountable. For the first, Haskell already provides a type for results that may contain detailed error information: Either a b. As you might guess, a value of that type is either something of type a or something of type b. By convention, a is the error type and b is the result type. The mnemonic is that the right type is what you get if everything goes right.

In fact, our previous code used Either to return either an error message or a meaningful result from hashPasswordFor:

hashPasswordFor :: MonadReader UserDirectory m => String -> String -> m (Either UserError PasswordHash)
hashPasswordFor name pass = do
    UserDirectory dir <- ask
    return $ case M.lookup name dir of
                Nothing   -> Left NoSuchUser
                Just user -> Right $ hashPassword (pwSalt $ usPassword user) pass

If successful, this function returns a PasswordHash via Either‘s Right type constructor. If the user couldn’t be found, it returns a UserError via Either‘s Left type constructor.

You might think that we could use Either UserError as a monad in much the same way we could use Maybe as a monad: executing a series of computations until the first error. Sadly, Either a isn’t defined to be a monad, so this doesn’t work.

Fortunately, the Monad Transformer Library has the next best thing: the ErrorT monadic transform. In a nutshell, ErrorT lets us transform any monad into something that adds error propagation. Specifically, any computation within the transformed monad can throw an error, skipping the remaining computations; the code using the transformed monad can then get an Either a b out of it with either the result of successful computation (of type b), or an error (of type a).

If this sounds a lot like try/catch-style exception handling, that’s sort of the idea. And in case you cleverly scrolled down to the Haskell section of that Wikipedia page to see that Haskell has some support for this via the IO monad, that’s true, but ErrorT is a lot more powerful, not the least of which because there’s no need to use the IO monad at all.

This might be clearer in an example. To use ErrorT, we merely have to declare whatever error type we wish to use as an instance of the typeclass Error, like so:

instance Error UserError

Looking up a user in the user directory is something our code does all the time, and each time we run the risk of failure if the user we’re looking for doesn’t exist. Let’s make a lookupUser function that tries to get the UserInfo for a user, or throws a UserError if it failed:

lookupUser :: Monad m => String -> UserDirectory -> ErrorT UserError m UserInfo
lookupUser name (UserDirectory dir) = maybe (throwError NoSuchUser) return $ M.lookup name dir

Let’s unpack that a bit. The return type is Monad m => ErrorT UserError m UserInfo. UserError is the type of errors that could get thrown, and UserInfo is the type of a successful result. m is a type variable for the monad that ErrorT is transforming; here, we don’t care what kind of monad m is, as long as it’s a monad. The function just does a lookup in the Map. If the result of the lookup is Just something, that something is returned (i.e., wrapped in) the monad. Otherwise, if the result of the lookup is Nothing, we throw NoSuchUser, which is of type UserError.

Now let’s rewrite hashPasswordFor to make use of lookupUser:

hashPasswordFor :: MonadReader UserDirectory m => String -> String -> m (Either UserError PasswordHash)
hashPasswordFor name pass = runErrorT $ do
    dir <- ask
    user <- lookupUser name dir
    return $ hashPassword (pwSalt $ usPassword user) pass

The main body of the function is free to ignore errors — there’s no more conditional check to see if the user lookup failed. Note, though, that the do block that specifies the monadic computation is now an argument to runErrorT. runErrorT has a type signature of:

runErrorT :: ErrorT e m a -> m (Either e a)

As you can see from the type signature, it takes a computation in an ErrorT-produced monad and converts it back into the original monad of type m, with the result inside m an Either e a. In other words, it converts a computation where we can throw errors into one that returns Either an error or the computation result.

You might wonder why we don’t just propagate errors out of hashPasswordFor using ErrorT like we did for lookupUser, like this:

-- This doesn't work!
hashPasswordFor :: MonadReader UserDirectory m => String -> String -> ErrorT UserError m PasswordHash
hashPasswordFor name pass = do
    dir <- ask
    user <- lookupUser name dir
    return $ hashPassword (pwSalt $ usPassword user) pass

There’s a very pragmatic reason why not: it doesn’t work. Recall that hashPasswordFor is used to generate the HashPasswordFor query operation in our MACID store. Happstack.State’s template magic crashes and burns if we try to return a computation involving ErrorT:

Users.hs:1:0:
    Exception when trying to run compile-time code:
      Unexpected method type: Control.Monad.Error.ErrorT Users.UserError m_0 Users.PasswordHash
      Code: mkMethods
              'UserDirectory
              ['addUser, 'hashPasswordFor, 'authenticateUser, 'listUsers]

This is unfortunate, since we’re ultimately trying to use HashPasswordFor and AuthenticateUser — each of which can fail — in our implementation of loginUser, and our whole goal is to wait until the very end to convert the result of the computation into an Either. The workaround is to do the opposite of runErrorT after we invoke HashPasswordFor, converting the m (Either UserError PasswordHash) back into a ErrorT UserError m PasswordHash. Luckily, it’s pretty straightforward:

rethrowError :: (Error e, Monad m) => Either e a -> ErrorT e m a
rethrowError (Left error)   = throwError error
rethrowError (Right result) = return result

Now we just need to feed the result of HashPasswordFor and AuthenticateUser into rethrowError inside loginUser:

loginUser :: MonadIO m => String -> String -> m (Either UserError ())
loginUser name pass = runErrorT $ do
    passHash <- rethrowError =<< (query $ HashPasswordFor name pass)
    now <- liftIO getClockTime
    rethrowError =<< (update $ AuthenticateUser name passHash now)

Aside from the minor hassle of needing to use rethrowError, this works quite nicely. Any UserError that gets thrown, regardless of where it happens, get caught by runErrorT and converted into Either UserError () for the result of the monadic computation. The code inside the do block doesn’t have to worry about error checking; ErrorT handles that for us.

hashPasswordFor was a trivial example, but remember this ugly nastiness from the previous post?

authenticateUser :: MonadState UserDirectory m => String -> PasswordHash -> ClockTime -> m (Maybe UserError)
authenticateUser name passHash when = do
    UserDirectory dir <- get
    case M.lookup name dir of
        Nothing -> return $ Just NoSuchUser
        Just user -> if isLocked when user
                        then return $ fmap AccountLocked $ usLocked user
                        else if passHash == usPassword user
                                then do put $ UserDirectory $ M.insert name (unlockUser user) dir
                                        return Nothing
                                else do put $ UserDirectory $ M.insert name (failUser when user) dir
                                        return $ Just PasswordMismatch

Here’s what a ErrorT magic lets us replace that with:

authenticateUser :: MonadState UserDirectory m =>
                    String -> PasswordHash -> ClockTime -> m (Either UserError ())
authenticateUser name passHash when = runErrorT $ do
    dir <- get
    user <- lookupUser name dir
    checkUnlocked when user
    if passHash == usPassword user
       then do insertUser name (unlockUser user)
               return ()
       else do insertUser name (failUser when user)
               throwError PasswordMismatch

Suddenly it’s much easier to see what the code’s supposed to do! Goodbye deep nesting of conditionals; if not for the fact that we need to update the data store differently based on whether we see a password mismatch, we wouldn’t even need the one still there.

Just for completeness’s sake, here’s checkUnlocked, which replaces isLocked from the previous code:

checkUnlocked :: Monad m => ClockTime -> UserInfo -> ErrorT UserError m ()
checkUnlocked asOf user = case usLocked user of
                            Just until -> when (asOf < until) (throwError $ AccountLocked until)
                            Nothing    -> return ()

Technically we could’ve used maybe instead of pattern-matching to turn checkUnlocked into a one-liner like isLocked was, but I think the code becomes too difficult to read in that case, which defeats the whole rationale behind using ErrorT throughout our code in the first place.

As always, here’s the complete program with these changes. The code behaves the exact same way as the previous version, but the implementation is now much easier on the eyes.

Let this be a lesson to you: it’s often said that most programming problems can be simplified by adding another level of indirection. In Haskell, I suspect the equivalent is adding another monad. Monads are a little tricky to get your head around at first, but you can do some neat things with them.

Comments Off

Data migration in Happstack.State

Last time (modulo a distracting interlude), we looked at fixing a password disclosure vulnerability in our simple Happstack.State demo program. However, there are still some security flaws that leave passwords open to attack:

  • There are no checks for password strength — users are free to pick trivially weak passwords.
  • There is nothing preventing an online brute force attack against an account, where an attacker repeatedly guesses passwords and tries to log in until he finds the right one.

These two vulnerabilities are hardly theoretical. As I mentioned in an earlier post, not too long ago an administrator’s account on Twitter was broken into by someone running a dictionary attack. The number of login attempts per second in such an attack is only bounded by the network bandwidth between the attacker and the server, since any halfway competent attacker is going to use a script to do the work for him — it’s not like he’s sitting there trying in passwords as quickly as he can. And people being lazy, they’re liable to pick simple, easily guessed passwords unless we do something to stop them.

So let’s modify our program from last time to add some countermeasures to these attacks:

  • When creating an account, check whether the password is sufficiently strong, and refuse to create the account if it isn’t.
  • If we see repeated failed logins for an account, temporarily lock the account, limiting how quickly an attacker can guess passwords.

First, since we’re introducing even more ways operations can fail, it’d be nice if our program provided more feedback to the user about why an operation failed. A simple algebraic sum type will suffice:

data UserError = UserExists
               | NoSuchUser
               | PasswordMismatch
               | AccountLocked ClockTime
               | PasswordTooShort Int
    deriving (Eq, Ord, Typeable, Data)
 
instance Version UserError
$(deriveSerialize ''UserError)
 
instance Show UserError where
    show UserExists             = "A user by that name already exists."
    show NoSuchUser             = "No user by that name exists."
    show PasswordMismatch       = "Incorrect password."
    show (AccountLocked until)  = "Account is locked until " ++ show until ++ "."
    show (PasswordTooShort min) = "Password must be at least " ++ show min ++ " characters long."

Even if you don’t know Haskell, it should be fairly self-evident what’s going on. I’ll just note two things. First, we still need to implement the Version class and derive a serialization function via deriveSerialize even though we don’t plan to save a UserError in our MACID store, because it’s a requirement for anything we pass in or out of a query or update. Second, we implement Show ourselves instead of letting the compiler do it for us, so we can provide human-readable versions of each error.

The core of checking password strength is almost trivial: a function that takes a proposed password and returns a UserError if it doesn’t meet our exacting standards. Our code that actually creates the account will then call this function to check the strength of the user’s proposed password:

checkPasswordStrength :: String -> Maybe UserError
checkPasswordStrength pass = if length pass < 8
                                then Just $ PasswordTooShort 8
                                else Nothing

Admittedly, this isn’t much of a strength check, since it’s only looking at the length of the password. Heck, even the classic bad password “password” passes with flying colors. But this is good enough for demonstration purposes here; it’s trivial to modify the function to check more things, and presumably add new constructors to UserError accordingly.

The more interesting issue is that of temporarily locking accounts after some number of consecutive login failures. Clearly, for each account we need to keep track of how many failures there have been since the last successful login, whether the account is locked, and if so, when the lock will expire. Adding that information to UserInfo looks easy enough:

data UserInfo = UserInfo { usPassword :: PasswordHash
                         , usJoined   :: ClockTime
                         , usFailures :: Int                -- new
                         , usLocked   :: Maybe ClockTime    -- new
                         }
    deriving (Typeable, Data)

There’s a problem, though. Our MACID store contains records using the original definition of UserInfo, which lacks the last two fields. If that’s all we do, suddenly we’ll be unable to load our old data. That’s bad.

Fortunately, Happstack.State is one step ahead of us. Remember that Version typeclass that UserInfo, and all the other types we use with the MACID store, has to implement? That’s what provides our data migration path. First, we’ll need to keep the definition of the old version of UserInfo around, but with a different name. Ordinarily, we’d create a separate module to contain the old definitions, but here let’s just stick _0 to the names of everything, to denote “version 0″ to ourselves:

data UserInfo_0 = UserInfo_0 { usPassword_0 :: PasswordHash
                             , usJoined_0   :: ClockTime
                             }
    deriving (Typeable, Data)
 
instance Version UserInfo_0
$(deriveSerialize ''UserInfo_0)

The penultimate line there declares that UserInfo_0 is an instance of Version. The default implementation of Version, which we’ve used up until now, says the type is the first version (version 0). Our new and improved UserInfo is version 1 of this type, so its implementation of Version needs to state this explicitly:

instance Version UserInfo where
    mode = extension 1 (Proxy :: Proxy UserInfo_0)

This just says that UserInfo is version 1 of the type, and the previous version is what we’re now calling UserInfo_0. (The Proxy object is essentially no different than unit, but we can associate a particular type with it. Happstack.State uses it to pass type information to a function without needing to pass an instance of that type.)

OK, so UserInfo is the successor to UserInfo_0, but we still need to say how to migrate from the old version to the new version. That’s what the Migrate typeclass is for:

instance Migrate UserInfo_0 UserInfo where
    migrate (UserInfo_0 password joined) = UserInfo password joined 0 Nothing

In other words, to migrate from UserInfo_0 to UserInfo, copy the existing data over, set the login failure count to 0, and note that the account is not locked.

Now our new program will be able to read the data saved from the old one. When it tries to read a UserInfo object (version 1) but sees a UserInfo_0 object (version 0) instead, Happstack.State can automatically figure out how to perform the conversion via the migrate function we defined between the two types. For the curious, this page describes what happens behind the scenes to make this work.

Our data migration worries solved, we can turn our attention to implementing our account locking and unlocking. The easiest way forward is to define a bunch of simple helper functions for doing the basic steps, and then using those when implementing the login operation.

Checking if an account is locked is just a matter of seeing if the locked-until time, if it exists, is on or after than the current time:

isLocked :: ClockTime -> UserInfo -> Bool
isLocked asOf user = maybe False (>= asOf) $ usLocked user

Unlocking an account just means resetting the failure counter and clearing the locked-until time. Just note that, this being Haskell, we return a new UserInfo object instead of trying to modify the existing one (since we couldn’t modify it even if we tried):

unlockUser :: UserInfo -> UserInfo
unlockUser user = user { usFailures = 0, usLocked = Nothing }

Incrementing the failure count is slightly complicated by needing to lock the account if the failure count exceeds the limit. Here, if there’s been three or more consecutive failures, we lock the account for one minute:

failUser :: ClockTime -> UserInfo -> UserInfo
failUser when user = let newFailures = usFailures user + 1
                         lockedUntil = if newFailures >= failureThreshold
                                            then Just $ addToClockTime lockPeriod when
                                            else Nothing
                     in  user { usFailures = newFailures, usLocked = lockedUntil }
    where failureThreshold = 3
          lockPeriod = noTimeDiff { tdMin = 1 }

Finally, we can turn our attention to the actual login process. Login is now an update, since a login attempt, successful or unsuccessful, needs to update the failure count in the UserInfo. This means we need to beware of the no-plaintext-passwords-as-update-parameters rule we discovered previously. The workaround is the same as last time. First, an update operation that does the work:

authenticateUser :: MonadState UserDirectory m => String -> PasswordHash -> ClockTime -> m (Maybe UserError)
authenticateUser name passHash when = do
    UserDirectory dir <- get
    case M.lookup name dir of
        Nothing -> return $ Just NoSuchUser
        Just user -> if isLocked when user
                        then return $ fmap AccountLocked $ usLocked user
                        else if passHash == usPassword user
                                then do put $ UserDirectory $ M.insert name (unlockUser user) dir
                                        return Nothing
                                else do put $ UserDirectory $ M.insert name (failUser when user) dir
                                        return $ Just PasswordMismatch

AuthenticateUser needs a PasswordHash, so here’s a query that takes a plaintext password and hashes it using the salt for a particular user account:

hashPasswordFor :: MonadReader UserDirectory m => String -> String -> m (Either UserError PasswordHash)
hashPasswordFor name pass = do
    UserDirectory dir <- ask
    return $ case M.lookup name dir of
                Nothing   -> Left NoSuchUser
                Just user -> Right $ hashPassword (pwSalt $ usPassword user) pass

Finally, the loginUser function hashes the password using the HashPasswordFor query, and if it succeeds (meaning the user does indeed exist), runs an AuthenticateUser update using the result:

loginUser :: MonadIO m => String -> String -> m (Maybe UserError)
loginUser name pass = do
    hashResult <- query $ HashPasswordFor name pass
    case hashResult of
        Left error     -> return $ Just error
        Right passHash -> do now <- liftIO getClockTime
                             update $ AuthenticateUser name passHash now

And let’s not forget to update createUser to check the strength of a password:

createUser :: MonadIO m => String -> String -> m (Maybe UserError)
createUser name pass =
    case checkPasswordStrength pass of
        Nothing -> do salt <- liftIO newSalt
                      now <- liftIO getClockTime
                      update $ AddUser name (hashPassword salt pass) now
        excuse  -> return excuse

All that’s left is to adjust the command loop to expect to maybe get a UserError (or rather, definitely get a Maybe UserError), and print the error message if the command failed:

-- in commandLoop:
          processCommand state ["add", user, pass] = do
                    result <- createUser user pass
                    putStrLn $ maybe "Success" show result
                    commandLoop state
          processCommand state ["login", user, pass] = do
                    result <- loginUser user pass
                    putStrLn $ maybe "Success" show result
                    commandLoop state

Here’s the complete program. Let’s try it out, starting with the result of the run with the old version, to demonstrate that the migration worked:

> list
bobby (joined Sat Apr 11 15:31:54 EDT 2009)
cowboy (joined Sat Apr 11 14:57:28 EDT 2009)
pmk (joined Sat Apr 11 14:57:12 EDT 2009)
> login pmk notthepassword
Incorrect password.
> login pmk alsonotthepassword
Incorrect password.
> login pmk maybethisisit
Incorrect password.
> login pmk keeptryinganyway
Account is locked until Sat Apr 18 18:29:57 EDT 2009.
> time              
Sat Apr 18 18:29:08 EDT 2009
> login pmk swordfish
Account is locked until Sat Apr 18 18:29:57 EDT 2009.
> time
Sat Apr 18 18:30:10 EDT 2009
> login pmk swordfish
Success
> add bobby letstryagain
A user by that name already exists.
> login alice xyzzy
No user by that name exists.
> add alice xyzzy
Password must be at least 8 characters long.
> add alice aaaaaaaa
Success
> list
alice (joined Sat Apr 18 18:31:05 EDT 2009)
bobby (joined Sat Apr 11 15:31:54 EDT 2009)
cowboy (joined Sat Apr 11 14:57:28 EDT 2009)
pmk (joined Sat Apr 11 14:57:12 EDT 2009)
> checkpoint
> quit

Everything works as expected. Trying to brute-force a password temporarily locks the account, preventing even legitimate logins until the lock times out. New accounts are forced to have a sufficiently long password. Meaningful error messages are displayed. And we didn’t lose any of the data from the old version of the program.

Are we done? Everything does indeed work like we want it to, but it’s still not ideal. Let’s take another look at the definition of authenticateUser:

authenticateUser :: MonadState UserDirectory m => String -> PasswordHash -> ClockTime -> m (Maybe UserError)
authenticateUser name passHash when = do
    UserDirectory dir <- get
    case M.lookup name dir of
        Nothing -> return $ Just NoSuchUser
        Just user -> if isLocked when user
                        then return $ fmap AccountLocked $ usLocked user
                        else if passHash == usPassword user
                                then do put $ UserDirectory $ M.insert name (unlockUser user) dir
                                        return Nothing
                                else do put $ UserDirectory $ M.insert name (failUser when user) dir
                                        return $ Just PasswordMismatch

It works, but even if you don’t know Haskell, it still smells bad. There’s a lot of nesting going on, as seen by the ever-increasing level of indentation. In fact, every time there’s a way to fail (no such user exists, the account is locked, etc.), we need another conditional to handle the error case, forcing the rest of the computation another level deeper.

Less obvious is the bad-smelling use of Maybe UserError. Typically, the Maybe monad is used to carry the result of a successful computation — in fact, when Maybe is used as a monad, that’s precisely what it does. This is the opposite of how we’re using it: to carry the result of an unsuccessful computation. This code is violating the principle of least astonishment and is liable to confuse anyone expecting Maybe to be used the way it normally is.

Still, we want to carry detailed information about errors so the user can be informed. Is there a better way to handle errors?

Of course there is. That’s the subject of the next post.

Comments Off

Your daily dose of distraction

First: in case you ever complained that Mega Man 2 didn’t have enough rap in it, here you go [thanks to Josh for alerting me to this]:

(Their Final Fantasy rap is also pretty good, if you’re into that sort of thing.)

Next: in case you ever complained that Super Mario World didn’t have enough stuff-happening-even-though-you’re-too-lazy-to-press-any-of-the-buttons, take a look at this ROM hack:

Even with the level-editing tools that are out there, it’s impressive to imagine how much work must’ve gone into the level design to pull that off.

Finally, it’s unfortunate that I hadn’t been reading MS Paint Adventures until now. It’s what you’d get if you crossed a webcomic with an old-school adventure game. Is it weird if what’s sold me on it is how the character’s inventory system in the current “game” is explicitly based on a stack implemented in a circular buffer? And how it’s suggested it’s possible to upgrade to something more featureful:

EB: it’s so frustrating.
TG: whats your modus
EB: what?
TG: how do you retrieve artifacts from it
EB: oh. like one at a time i guess. and if i put too much in, something falls out.
TG: stack?? hahahahahaha
EB: what is yours?
TG: hash map
TG: my bro taught me a few tricks he basically knows everything and is awesome
EB: what the hell is that?
TG: you should probably brush up on your data structures

While I’d probably deque anyone who made a real game with such an obnoxious inventory system, in comic form it’s awesome. It may be the character’s birthday, but he won’t be LIFO the party with just that.* Hopefully whatever upgrades are in store will let him skip lists entirely and explore the rest of the wide array of options out there. Because dude, a stack? With that limited interface he’ll be in a heap of trouble. As What’s Her Face would say, DAG, yo.

Of course, in real life we have data structure based inventory systems too. We’re typically limited to a pair of five-element finger trees, sometimes augmented with a bag.

* Yes, I know that technically having a stack would very much make him LIFO the party almost by definition, but I’m trying to make a series of data structure puns here. If you trie it yourself, you’ll find it’s harder than it looks.

Protecting passwords for fun and profit

Last time, we looked at a simple way to use Happstack to store a bunch of user names and passwords. In fact, it was a bit too simple — an attacker with access to the disk we’re storing the data on can trivially recover each user’s password! How can this be, when we were so careful to only store hashes of passwords in our user directory, instead of the passwords themselves?

Before I get to that, let’s spend a modicum of time refactoring the code we’re working with into two modules: one that implements our user directory and one that uses our user directory. The Users module will only expose the interface for the operations we’re providing, hiding the implementation details:

module Users ( UserDirectory
             , AddUser (..)
             , CheckPassword (..)
             , ListUsers (..)
             ) where

Here, the interface only consists of the queries and updates that clients can make, as well as the top-level type of the data being saved in the MACID store. The Main module implements our command loop and imports Users to do the actual work:

module Main ( main
            ) where
 
import Users

One last change: to make it easier for successive versions of the program to share the same files backing our MACID store, we’ll explicitly set the name of the program — Happstack.State names the directory where it stores the data according to this name:

main :: IO ()
main = withProgName "state-demo" $ do
    state <- startSystemState macidProxy
    commandLoop state

For convenience, here’s a tarball containing the complete program. Let’s give it a try:

> list
> add pmk swordfish
User added
> list
pmk (joined Sat Apr 11 14:57:12 EDT 2009)
> add cowboy sourMilk7
User added
> list
cowboy (joined Sat Apr 11 14:57:28 EDT 2009)
pmk (joined Sat Apr 11 14:57:12 EDT 2009)
> login cowboy swordfish
Bad account or password
> login pmk whatever
Bad account or password
> login pmk swordfish
Success
> checkpoint
> quit

Since we named our program state-demo, Happstack.State stores its files in the directory _local/state-demo_state/ under the current directory. If we look at the checkpoint we made, which will contain the contents of the UserDirectory we created, we can see there aren’t any passwords in it, just as we expected:

0000000: 0000 0000 0000 0001 0000 0000 0000 0013  ................
0000010: 5573 6572 732e 5573 6572 4469 7265 6374  Users.UserDirect
0000020: 6f72 7900 0000 0000 0001 8100 0000 0000  ory.............
0000030: 0000 0000 0000 0000 0000 0004 a57b 6cd0  .............{l.
0000040: 6cbc 5d4d 0000 0120 968a e2ad 0000 0000  l.]M... ........
0000050: 0000 0000 0000 0000 0000 0014 3139 3238  ............1928
0000060: 3338 3631 3537 2035 3739 3432 3634 3530  386157 579426450
0000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000080: 0200 0000 0000 0000 0663 6f77 626f 7900  .........cowboy.
0000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000a0: 0000 0000 0000 0000 402f 2709 fa39 c1a1  ........@/'..9..
00000b0: 7f91 1c05 4ae1 f1b3 3260 e5ee 28b8 1963  ....J...2`..(..c
00000c0: a455 122d f04a 8235 1147 fe5c 92a3 0b4f  .U.-.J.5.G.\...O
00000d0: 7540 0754 3be1 939f 2d66 272f 0f09 ffdf  u@.T;...-f'/....
00000e0: 8c9d 05e2 3b8e b5b4 db00 0000 0000 0000  ....;...........
00000f0: 0a23 cde5 ac26 17c4 6019 a700 0000 0000  .#...&..`.......
0000100: 0000 0000 0049 e0e8 1801 0100 0000 0000  .....I..........
0000110: 0000 0500 3cb8 192e 0000 0000 0000 0003  ....<...........
0000120: 706d 6b00 0000 0000 0000 0000 0000 0000  pmk.............
0000130: 0000 0000 0000 0000 0000 0000 4035 6924  ............@5i$
0000140: cb51 f37b f7ae af8d 1953 6d44 e90b 5d4a  .Q.{.....SmD..]J
0000150: 200d 2925 8e2d 4ed7 9aa4 7b59 9d47 ed5d   .)%.-N...{Y.G.]
0000160: 8626 bef6 1e8d 6e9b 1bf7 7689 daeb facb  .&....n...v.....
0000170: a9c0 ab09 ae76 272c 3b26 ce22 de00 0000  .....v',;&."....
0000180: 0000 0000 0af1 4a1c a2dd ef72 1ea8 8e00  ......J....r....
0000190: 0000 0000 0000 0000 0049 e0e8 0801 0100  .........I......
00001a0: 0000 0000 0000 0500 9c69 30dd            .........i0.

However, when we look at the transaction log from before we made the checkpoint, we’re in for a nasty surprise:

0000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000010: 0000 0000 0000 0000 023f cf48 bc4d aa07  .........?.H.M..
0000020: 1400 0001 2096 8a62 f600 0000 0000 0000  .... ..b........
0000030: 0000 0000 0000 0000 1531 3438 3835 3032  .........1488502
0000040: 3432 3820 3138 3234 3336 3735 3531 0000  428 1824367551..
0000050: 0000 0000 0000 0000 0000 0000 000d 5573  ..............Us
0000060: 6572 732e 4164 6455 7365 7200 0000 0000  ers.AddUser.....
0000070: 0000 2400 0000 0000 0000 0000 0000 0000  ..$.............
0000080: 0000 0370 6d6b 0000 0000 0000 0009 7377  ...pmk........sw
0000090: 6f72 6466 6973 6800 0000 0000 0000 0000  ordfish.........
00000a0: 0000 0000 0000 0000 0000 0000 0000 0004  ................
00000b0: a57b 6cd0 6cbc 5d4d 0000 0120 968a 9e86  .{l.l.]M... ....
00000c0: 0000 0000 0000 0000 0000 0000 0000 0014  ................
00000d0: 3139 3238 3338 3631 3537 2035 3739 3432  1928386157 57942
00000e0: 3634 3530 0000 0000 0000 0000 0000 0000  6450............
00000f0: 0000 000d 5573 6572 732e 4164 6455 7365  ....Users.AddUse
0000100: 7200 0000 0000 0000 2700 0000 0000 0000  r.......'.......
0000110: 0000 0000 0000 0000 0663 6f77 626f 7900  .........cowboy.
0000120: 0000 0000 0000 0973 6f75 724d 696c 6b37  .......sourMilk7

The passwords, in the clear where anyone can see them! What happened?

In retrospect, it’s obvious. Between checkpoints, Happstack.State saves a transaction log of all updates, so that if the program quits prematurely it can recover and not lose any data. While we were careful to hash the passwords in the MACID store itself, the AddUser update operation didn’t, so when those are saved to the transaction log, so are the passwords.

The moral is this: Never pass any arguments to an update that you don’t mind being written to disk.

Now that we’ve learned our lesson, let’s rewrite addUser so that it takes a PasswordHash as an argument, instead of the password itself:

addUser :: MonadState UserDirectory m => String -> PasswordHash -> ClockTime -> m Bool
addUser name passHash when = do
    UserDirectory dir <- get
    if M.member name dir
        then return False
        else do put $ UserDirectory $ M.insert name (UserInfo passHash when) dir
                return True

That’ll work, but we’d rather not force our client to know how passwords get hashed — PasswordHash should be an implementation detail, not part of the interface. So let’s make a function that hashes the password, and then calls the update:

createUser :: MonadIO m => String -> String -> m Bool
createUser name pass = do
    salt <- liftIO newSalt
    now <- liftIO getClockTime
    update $ AddUser name (hashPassword salt pass) now

But wait, since createUser is taking the password as an argument, doesn’t it have the same saving-passwords-to-the-transaction-log flaw that we just tried to fix in AddUser? No, because createUser isn’t an update operation — it’s just a function that calls the update operation. More specifically, it sets up the arguments needed for AddUser (hashing the password and getting the current time), and then invokes AddUser with those arguments. Those are the arguments that get saved to the transaction log, not the ones that createUser gets.

If you aren’t convinced, look at the type signature for createUser again, as compared to the one for addUser:

createUser :: MonadIO m => String -> String -> m Bool
 
addUser :: MonadState UserDirectory m => String -> PasswordHash -> ClockTime -> m Bool

addUser operates in a monad that implements MonadState UserDirectory, which is what makes it an update operation. Our original code used Update UserDirectory as the monad, which is the concrete type of the monad that Happstack.State uses. Really, though, our code doesn’t care what the specific monad is, as long as it lets us read and write a state value of type UserDirectory, which is what MonadState UserDirectory guarantees.

createUser, on the other hand, operates in a monad that implements MonadIO, a generalization of the IO monad that lets Haskell programs interact with the outside world. This is why createUser can call IO operations like getClockTime, which would be illegal inside a query or update, since the Update UserDirectory monad doesn’t implement MonadIO.

And if you still don’t believe me, observe that we aren’t creating any query or update operations using the template deep magic:

$(mkMethods ''UserDirectory ['addUser, 'checkPassword, 'listUsers])

See? createUser doesn’t appear anywhere in there; it’s just an ordinary function.

Since we want clients to call createUser instead of using AddUser, let’s change the list of exports from our module:

module Users ( UserDirectory
             , createUser
             , CheckPassword (..)
             , ListUsers (..)
             ) where

And, since we changed the interface, we’ll need to update part of the Main module that uses it:

-- inside commandLoop:
      processCommand state ["add", user, pass] = do
                success <- createUser user pass
                putStrLn $ if success then "User added" else "User already exists"
                commandLoop state

Clients can be blissfully unaware of the hashing that’s going on behind the scenes inside createUser.

Here’s a tarball of version 2 of our program, with all the changes above (and a few others) applied to it. Starting with the same state from version 1, let’s try creating a new user and prove once and for all we’ve abolished plaintext passwords:

> list
cowboy (joined Sat Apr 11 14:57:28 EDT 2009)
pmk (joined Sat Apr 11 14:57:12 EDT 2009)
> add bobby nowsecure
User added
> list
bobby (joined Sat Apr 11 15:31:54 EDT 2009)
cowboy (joined Sat Apr 11 14:57:28 EDT 2009)
pmk (joined Sat Apr 11 14:57:12 EDT 2009)
> login bobby nowsecure
Success
> checkpoint
> quit

And the transaction log from this session:

0000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000010: 0000 0000 0000 0000 0656 a5f9 68e9 e949  .........V..h..I
0000020: 8300 0001 2096 aa27 f300 0000 0000 0000  .... ..'........
0000030: 0000 0000 0000 0000 1531 3839 3231 3631  .........1892161
0000040: 3433 3820 3230 3531 3836 3033 3930 0000  438 2051860390..
0000050: 0000 0000 0000 0000 0000 0000 000d 5573  ..............Us
0000060: 6572 732e 4164 6455 7365 7200 0000 0000  ers.AddUser.....
0000070: 0000 9500 0000 0000 0000 0000 0000 0000  ................
0000080: 0000 0562 6f62 6279 0000 0000 0000 0000  ...bobby........
0000090: 0000 0000 0000 0000 40c4 a004 6a7d 0468  ........@...j}.h
00000a0: dd6a aef6 f047 b48d fe4d 03b9 6d42 daf2  .j...G...M..mB..
00000b0: d647 fe57 ba4a c04a c67d a008 5abc 44fe  .G.W.J.J.}..Z.D.
00000c0: 1ee0 d21e 7140 e619 7a13 db8c 0060 4ee2  ....q@..z....`N.
00000d0: 6d05 dd16 7f0f 4493 0000 0000 0000 0000  m.....D.........
00000e0: 0a36 b95d 8dd4 1919 9fcf c200 0000 0000  .6.]............
00000f0: 0000 0000 0049 e0f0 2a01 0100 0000 0000  .....I..*.......
0000100: 0000 0540 e032 c9e7                      ...@.2..

Success! We can clearly see the AddUser for bobby, but can’t see his password. Even the transaction log only stores a hash of the password.

By the way, if you’re wondering why we never had to make any changes to checkPassword, which also takes a plaintext password as an argument, that’s because checkPassword is a query, and queries don’t get saved to the transaction log. That’s because queries are guaranteed not to change the contents of the MACID store, and thus can be ignored for recovery purposes. It’s safe to pass sensitive information as the arguments to a query, but not to an update.

Unfortunately, the no-sensitive-information-in-updates rule is something I only learned after writing The Button, so if you signed up for an account, the password you chose was exposed in the transaction log. I’ve taken the liberty of wiping those files, but in the unlikely event that you specified a password you use for other accounts too, I highly suggest you change those passwords, unless for some reason you trust me not to do anything nefarious with the passwords I at one time had access to.

Of course, there are still ways we could improve our program’s security, namely by enforcing that users pick strong passwords and preventing online brute force attacks. That will lead us into exploring the data migration features of Happstack.State. Stay tuned.

Happstack.State – the basics

One of the surprising things about using Happstack for developing web apps is that, unlike most other frameworks out there, it doesn’t use a conventional database to save information. Instead, it implements something it calls a MACID store: an in-memory user-defined data store that provides ACID guarantees (atomicity, consistency, isolation, and durability).

There are several advantages to using MACID instead of a relational database with perhaps an object-relational mapper on top of it. The biggest is that the program can use whatever data structures and data types are most convenient, instead of being forced to stick with things that map nicely to database tables. It also simplifies setup of the application: there’s no need to configure a separate database server to let the application talk to it.

As an example of using Happstack’s MACID store, implemented in the Happstack.State module, the following is a simplified version of the user account directory that I wrote as part of The Button.

Before we get to that, though, let’s take a moment to consider how the program is going to store passwords. Being security-conscious, we aren’t going to store the passwords themselves — if an attacker were to get access to the files backing the MACID store, he would learn the passwords for all users in the system. Instead, we’ll store a one-way hash of each user’s password. Given a password, it’s easy to compute the corresponding hash, but extremely difficult to take a hash and compute the password it was derived from. That way, the program can check if a user’s password matches the one for the account, without ever having to remember what the password actually is.

Actually, it’s not quite that simple. Just using a hash still lets an attacker perform a dictionary attack, perhaps using a rainbow table, if he gets a copy of the hashed passwords. The basic idea is that the attacker would guess a password, hash it, and see if the hash matches the password hash for any users. To foil this, we salt passwords before hashing them, appending some random data before running the hash function. The salt values don’t need to be secret; they just need to be different for each user, to guarantee that even if two users have the same password, the hashes will be different.

And just to ruin the attacker’s day even more, we do key strengthening by running the hash function over and over instead of just once, to increase the amount of work an attacker needs to do to brute-force a password. Since a hash can be computed fairly quickly, the user doesn’t notice if we run the function one time or 100 times. The extra work only becomes noticeable if you’re trying to guess lots of passwords, which slows down an attacker.

OK, enough about password security. Here’s how we’ll represent a hashed password in our program:

data PasswordHash = PasswordHash { pwHash :: [Word8]
                                 , pwSalt :: [Word8]
                                 }
    deriving (Typeable, Data)
 
instance Version PasswordHash
$(deriveSerialize ''PasswordHash)

A PasswordHash is the hash itself, along with the salt used when computing it. The deriving (Typeable, Data) and the rest is part of Happstack.State’s deep magic, which we don’t need to worry about for this example.

The actual hashing of a password is performed by this function, which takes a salt value and the password, and computes its hash:

hashPassword :: [Word8] -> String -> [Word8]
hashPassword salt password =
        let passBytes = listToOctets $ map ord password
        in  (iterate step passBytes) !! iterationCount
    where iterationCount = 100
          step chain = SHA512.hash (chain ++ salt)

That function converts the password into a series of bytes and repeatedly appends the salt and runs it through the SHA-512 hash function, returning the result after doing that 100 times.

That’s enough about passwords for now. Next, let’s consider what information we want to store about each user. For this example, we’ll only care about the user’s password (hashed, of course), along with the time they first registered an account:

data UserInfo = UserInfo { usPassword :: PasswordHash
                         , usJoined   :: ClockTime
                         }
    deriving (Typeable, Data)
 
instance Version UserInfo
$(deriveSerialize ''UserInfo)

The user directory itself will just be a map from user names to the information for each user:

newtype UserDirectory = UserDirectory (M.Map String UserInfo)
    deriving (Typeable, Data)
 
instance Version UserDirectory
$(deriveSerialize ''UserDirectory)
 
instance Component UserDirectory where
    type Dependencies UserDirectory = End
    initialValue = UserDirectory M.empty

Since the user directory is the thing we’re actually going to put into the MACID store, there’s a few extra lines of boilerplate at the bottom to say what other MACID stores it depends on (none) and what the contents of a brand-new store is (empty).

That’s about all there is to defining what goes into the MACID store itself. Next, we need to define the operations that either query the store without changing it, or update the store to a new value.

Let’s start with an easy one: a query that returns a list of users, paired with the date their account was created:

listUsers :: Query UserDirectory [(String, ClockTime)]
listUsers = do
    UserDirectory dir <- ask
    return $ M.toList $ M.map usJoined dir

The type signature is perhaps the most interesting part: Query UserDirectory [(String, ClockTime)]. This is a read-only query (Query) that operates on a UserDirectory and returns a list of (String, ClockTime) pairs. The implementation of the function is simple: get the current value of the store using ask, then iterate over the map to get the user names and dates.

Here’s a more sophisticated query, one that takes a user name and password and sees if the password is correct:

checkPassword :: String -> String -> Query UserDirectory Bool
checkPassword name pass = do
    UserDirectory dir <- ask
    case M.lookup name dir of
        Nothing       -> return False
        Just userInfo -> let PasswordHash hash salt = usPassword userInfo
                         in  return $ hash == hashPassword salt pass

Again, the type signature (String -> String -> Query UserDirectory Bool) shows this is a query on our UserDirectory, this time taking two strings (user name and password) as arguments and returning a boolean value. The code looks up the UserInfo, if any, associated with the user, gets the salt, hashes the password provided as an argument to the query, and returns whether the two match.

Of course, it would help if we had a way to add users to the store. That’s what this update operation does:

addUser :: String -> String -> Update UserDirectory Bool
addUser name pass = do
    UserDirectory dir <- get
    if M.member name dir
        then return False
        else do salt <- newSalt
                now <- liftM fixEventClockTime getEventClockTime
                let passwordHash = PasswordHash (hashPassword salt pass) salt
                let userInfo = UserInfo passwordHash now
                put $ UserDirectory $ M.insert name userInfo dir
                return True

Since this is an update, the type signature has Update in it instead of Query. This means instead of using ask to get the current state, we use get to get the current state and put to set it to a new value. The code itself first checks if a user by the name already exists, and if not, creates a new UserInfo structure with the user’s hashed password and the time that the update was made.

A couple things are worth pointing out. First, inside a Query or Update, we can use the MACID store as a random number generator, which is how a new salt is generated for the user:

instance Random Word8 where
    randomR (lo, hi) rng = let (val, rng') = randomR (fromIntegral lo, fromIntegral hi) rng
                               val :: Int
                           in  (fromIntegral val, rng')
    random rng = randomR (minBound, maxBound) rng
 
newSalt :: AnyEv [Word8]
newSalt = sequence $ take saltLength $ repeat getRandom
    where saltLength = 10

AnyEv is a monad that both Query and Update work with. The newSalt function generates 10 random bytes by first generating an infinite list of monadic computations that return random numbers (repeat getRandom), throwing away all but the first ten of them (take saltLength), and finally combining them into a single monadic computation that returns a list of random bytes (sequence) (instead of a list of monadic computations that each returns one random byte).

Annoyingly, Haskell doesn’t provide a direct way to generate random bytes (of type Word8), so we have to manually specify how Word8 implements the Random typeclass. The implementation is straightforward: to generate a random byte, first generate a random integer, and then convert it to a byte.

Returning to the update function, it’s also worth noting that getEventClockTime returns the time at which the update was logged, not the time the update is executing! This is because the MACID store doesn’t save a new copy of the state to disk every time it changes. Instead, it saves a log of what updates were made. If the program terminates abnormally before the state can be checkpointed, the MACID store recovers by loading the most recent checkpoint and re-executing all the updates since then. This is how the MACID store provides durability without completely thrashing the disk.

The astute reader will note that, if we ever do need to replay the addUser update, newSalt will be executed again! Does this mean that an update that uses the RNG could get different values during recovery? As it turns out, the MACID store also saves the state of the RNG, so replaying the update during recovery will generate the same random numbers that were obtained from the original execution of the update. The particularly astute reader will conclude that the MACID store’s RNG is unsuitable for generating cryptographic keys, since an attacker with access to the MACID store’s backing files can predict what the RNG will return. Fortunately, we don’t care if the salt is predictable, just that it’s different for each user.

OK, enough of that. There’s just one little piece of deep magic we need to convert those functions into honest-to-goodness transaction handlers for the MACID store:

$(mkMethods ''UserDirectory ['addUser, 'checkPassword, 'listUsers])

That bit of magic generates a type constructor for each transaction function. When invoking a transaction, instead of calling the functions themselves, we create an object to represent the request, and then pass it to query or update as appropriate.

Here’s an example of using our new MACID store. The following implements a simple command interpreter that lets us manipulate our user directory:

commandLoop :: MVar TxControl -> IO ()
commandLoop state = do
        putStr "> "
        hFlush stdout
        command <- liftM words getLine
        processCommand state command
    where processCommand state ["list"] = do
                    people <- query ListUsers
                    mapM_ (putStrLn . showUser) people
                    commandLoop state
          processCommand state ["add", user, pass] = do
                    success <- update $ AddUser user pass
                    putStrLn $ if success then "User added" else "User already exists"
                    commandLoop state
          processCommand state ["login", user, pass] = do
                    success <- query $ CheckPassword user pass
                    putStrLn $ if success then "Success" else "Bad account or password"
                    commandLoop state
          processCommand state ["checkpoint"] = do
                    createCheckpoint state
                    commandLoop state
          processCommand _     ["quit"] = return ()
          processCommand state _        = do
                    putStrLn "Unrecognized command"
                    commandLoop state
          showUser (name, joined) = name ++ " (joined " ++ show joined ++ ")"
 
macidProxy :: Proxy UserDirectory
macidProxy = Proxy
 
main :: IO ()
main = startSystemState macidProxy >>= commandLoop

Note how, for example, instead of calling addUser user pass, we do update $ AddUser user pass, where the AddUser type constructor was generated automatically and takes the same arguments as addUser. The update function does all the work of invoking addUser and making sure the result gets saved to the MACID store which enforcing all the ACID guarantees.

Instead of walking through how the command interpreter works, here’s a sample session, which should give you the idea:

> list
> add paul swordfish
User added
> list
paul (joined Sun Apr  5 20:00:01 EDT 2009)
> add cowboy sourMilk7
User added
> list
cowboy (joined Sun Apr  5 20:00:13 EDT 2009)
paul (joined Sun Apr  5 20:00:01 EDT 2009)
> login paul notHisPassword
Bad account or password
> login nobody foobar
Bad account or password
> login paul swordfish
Success
> quit

The files backing the MACID store are, by default, in the _local directory under where you ran the program from: current-0000000000, the initial checkpoint of the (empty) user directory we started with; and events-0000000000, the log of all the updates that were applied. If we had created a checkpoint, it would be in the directory too.

Now, quiz time. Not counting all the security problems I already mentioned in The Button that apply equally well to this example, there is an additional security flaw in the above code that could let an attacker steal passwords with little effort on his part. Your quiz has two questions:

  1. How can the attacker steal passwords from the MACID store?
  2. How can we change the program to prevent him from doing so?

For your convenience, here is Vulnerable.hs, the complete example described in the above post. Assuming you’ve installed GHC 6.10 and Happstack on your computer, just do “runghc Vulnerable.hs” to try the program out yourself.

Next time: the answers to the quiz.

Comments Off

The Button is down

As some of you have already noticed, The Button is down and not coming back anytime soon, for several reasons.

First, and most obviously, it was in part an April Fools’ Day prank making fun of Twitter: a microblogging platform with a zero-character limit. I was hoping that “femtoblogging” would be a unique name, but as it turns out for each of the sub-micro SI prefixes, there are plenty of hits for prefixblog, from nanoblog down to yoctoblog.

Second, and primarily, I wrote it to get a feel for developing web apps in Happstack, a Haskell-based application server development framework. The Button was trivial enough to be implemented over a weekend (plus a little polishing the following Monday evening), but nontrivial enough to let me play around with several features and get a better understanding of why the examples are organized the way they are. I definitely learned some things in writing The Button, which I’ll regale you with in the next few posts.

Third, and most pragmatically, the actual hosting of The Button was an ugly ugly hack. Happstack requires GHC 6.10 (the Haskell compiler), and although my web host does indeed have GHC pre-installed, it only had version 6.6. I tried compiling the latest version of GHC from source, but that failed once it exhausted the remaining 200 MB of my disk quota. Downloading precompiled binaries was also impossible since the unpacked tarballs for those also required more than 200 MB of disk. While I don’t foresee any issues in getting my quota increased, since I was trying to do this the evening of March 31, I couldn’t count on the turnaround time of the request being quick enough.

In short, The Button was running off queeg, my laptop. The domain button.kuliniewicz.org was pointing to my home connection. (It doesn’t anymore; it’s currently acting as a synonym for www.kuliniewicz.org, which I need to fix.) In case you were wondering why it was running on a high-numbered port, that’s why — there’s no way I was going to run a largely untested server as root on my home machine and open it to the world! Naturally, using my laptop as a web server was hardly a long-term solution, so once April 1 passed I took it down.

Fourth, and most security-consciously, The Button’s password security was a joke. Other than storing them with strong, randomly salted, strengthened hashes, it was bad. Passwords were transmitted to the server in the clear. There were no checks whatsoever for strong passwords. Nor was there any protection against online brute force attacks (which, incidentally, Twitter fell victim to earlier this year, with little “happiness” to be had by that compromised admin account).

So, I hope those of you who did actually register accounts with The Button didn’t use the same password you use for anything important.

If I had had more than a weekend to work on The Button, I would’ve addressed those issues, but I simply ran out of time. I couldn’t in good conscience continue running a server with that many security vulnerabilities once the joke had passed. That’s also why I’m reluctant to post the code that implemented The Button unless someone really wants to see it. It’s not of good enough quality for someone to use as the basis of something real.

If for some reason you actually think The Button, or femtoblogging in general, actually has potential (I can actually think of one or two legitimate use cases for it, though I can also come up with better solutions for those use cases), I’ve demonstrated you can implement the core functionality over a weekend, even if you aren’t particularly well-versed in the framework or the language you’re using.

The Button now in Open Beta

I am pleased to announce that The Button is now in open beta. The Button is a femtoblogging platform I’ve been developing; I expect it to eclipse Twitter by this time next year. Give it a try and let me know if you run into any problems.

From the The Button website:

Femtoblogging?

First there was ordinary blogging, dominated by walls of text that clutter your screen. Then there was microblogging, which replaced the wordiness with social networking, but still requires you to write “content” for other people to “read”. Who has time for that?

Not us.

Femtoblogging reduces microblogging’s character count by a factor of 10-9, getting out of your way so you can post femtoblogs without all that bothersome typing.

Do I need an account?

You’ll need a free account to leverage all the social features of The Button. Yes, you’ll have to type a user name and password first, but we’re working on fixing that soon.

But even without signing up, you can still try The Button out. Browse around, post anonymously, and see what everyone else is up to. When you’re ready to dive in to the world of femtoblogging, the sign up form is up at the top.

Can I push the button?

Of course! You know you want to.