Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Introducing Cupcake

The greatest risk to the security of a web application is when it either accepts user input and processes it or renders data in a comprehensible format (HTML, JSON, etc.) to the user.

For software written in PHP, this usually means generating and validating HTTP form inputs. The specific risk factors aren’t exactly a mystery.

  • If you misuse data when constructing SQL queries, you get SQL injection.
  • If you fail to process data correctly when rendering HTML or JSON, you get cross-site scripting (XSS).
  • If you allow forms to be submitted with disregard to the Same Origin Policy (sadly, this is the default behavior), you get cross-site request forgery (CSRF).
  • If you only enforce input validation on the client-side, modestly clever people will completely disregard your requirements. This happened to Twitter! (See linked article.)

If your goal is to prevent insecure software from being developed, the worst thing you can do is to dictate a bunch of increasingly arcane-sounding requirements to Developers working against a tight deadline then shame them when they fuck it up.

A much better strategy is to give developers a tool, with minimal fuss and dependencies, that’s tuned for security out-of-the-box and always does The Right Thing for them.

Even better is if you introduce it as a write-less, do-more tool that saves developers time and frustration. This incentivizes them to use it over a quick-and-dirty, error-prone, artisanal approach to solving the same problems.

And thus, I made Cupcake.

Art: Harubaki

Cupcake: Sweet HTML Forms in PHP 8

Here’s a working example of a login form with Cupcake. This includes an optional “remember me?” checkbox.

        (new DivWithPurifiedHtml(''))
        (new Grouping())
') ->setAfterEach('
') ->append( (new Text('username')) ->setId('form1-username') ->setRequired(true) ->setPattern('^[a-z0-9_\-]{2,}$') ) ->createAndPrependLabel('Username') ->append( (new Password('password')) ->setId('form1-password') ->setRequired(true) )->createAndPrependLabel('Password') )->append( CheckboxWithLabel::create( 'remember-me', '1', 'form1-remember-me', 'Remember me on this computer?' ) ); // This is optional; by default, it includes at the time // of form rendering. This can cause issues with the Cookie-backed // Anti-CSRF implementation. Custom implementations may be unaffected. $form->finalizeCsrfElement(); return $form;

This looks mildly verbose, but it gives you very explicit control over the rendered form.

© Harubaki

In actuality, you’d want to use Cupcake to render forms programmatically rather than manually writing boilerplate code like this.

Input validation with Cupcake looks like this:

        // Pass the validated post data to another function
    } catch (InvalidDataException $ex) {
        /** @var DivWithPurifiedHtml $el */
        $el = $form->getChildById('form-feedback');
        // Populate the form elements with $_POST data      

// To print the form into a web page, just print it:
echo '';
echo $form;
echo '';

Seems Straightforward; What’s the Catch?

Cupcake’s first priority is security!

Remember above, when we set some rules about the username field?

                (new Text('username'))


The field was marked as required and had an input validation pattern (regular expression) attached. This will populate the relevant HTML attributes in the rendered form.

But most importantly, these rules are enforced server-side too.

        // Pass the validated post data to another function
    } catch (InvalidDataException $ex) {


If you provided a username less than 2 characters long, or containing a character outside the permitted range, Cupcake will throw an InvalidDataException.

If you want to handle it gracefully, you can catch this exception. Otherwise, you can simply let it stop your PHP application from continuing with invalid data.

Cupcake prevents you from ever recreating Twitter’s mistakes.

Why “Cupcake”?

Wordplay! The German word for a cupcake mold is förmchen.

© Harubaki

Cupcake’s Security Features

How Does Cupcake Deal With XSS Attacks?

Cupcake uses context-aware escaping rules to prevent attributes from being escaped, and arbitrary HTML is processed with HTMLPurifier to prevent invalid HTML (which includes XSS exploits).

More importantly, Cupcake leverages Psalm’s taint analysis feature to help developers recognize insecure uses of the RawHtmlBlock class.

How Does Cupcake Deal With Injection Attacks?

As discussed in the previous section, Cupcake uses Ionizer to ensure all form inputs adhere to a strict contract; which encompasses both type-safety and input validation. This prevents all sorts of invalid inputs from being accepted.

Ionizer’s type strictness turns out to be sufficient for preventing NoSQL Injection attacks.

However, you should not rely on this for stopping SQL Injection. Instead, use parametrized queries (also known as prepared statements) to prevent user input from mangling your query strings.

Is CSRF Mitigated Too?

You bet! The default implementation checks a form value against a cryptographically secure random value stored in a SameSite cookie.

If the HTTPS Request carrying form data wasn’t initiated by the same site, it won’t include the cookie value. If the cookie value is absent, it aborts. If the cookie value doesn’t match the one provided by the request body, it aborts. This cookie is only ever sent over secure connections.

Cupcake’s Usability Features

Fetching An Element (Recursively)

Let’s say you have a Cupcake container (e.g. Form object), and you want to access a specific child element (or container), and you only have its ID.

$element = $form->getChildById('element-id-goes-here', true);
// The second parameter enables recursive search (disabled by default)

It’s that easy.

Populating User Data

If you’d like to repeat user data in order to inform the user that the data they provided was not accepted, Cupcake makes this easy too:


This is documented on GitHub.

Cupcake Project Status

Cupcake is currently alpha software, and it only runs on PHP 8. It’s not quite ready for production yet.

In the coming months, I plan to flesh out the documentation a bit better (with plenty of examples) and add more robust security testing.

Art: LvJ

Until then, feel free to hack on the code and send any changes you think are important.

This post first appeared on Dhole Moments - Software, Security, Cryptography, And The Furry Fandom, please read the originial post: here

Share the post

Introducing Cupcake


Subscribe to Dhole Moments - Software, Security, Cryptography, And The Furry Fandom

Get updates delivered right to your inbox!

Thank you for your subscription