How to preform whitelist-based CSS filtering in PHP


Tags: php,css,validation,user-input

Problem :

I am working on a site and I would like to make a user able to enter custom CSS into that will be publicly displayed.

However, seeing as a good deal of XSS attacks can be preformed through CSS, I would like to be able to find a way to "clean" the CSS output, similar to how HTML Purifier works, by parsing the CSS, running the parsed CSS against a whitelist, and then outputting a new stylesheet based on the parsed and whitelisted CSS.

Is there already a library like this out there? If not, is there a CSS parsing library that can be used to create a custom implementation?



Solution :

I guess you're going to write your own CSS parser and filter, so here's what I'd consider, although I've never done such a thing:

  • Make a (white) list of acceptable CSS properties that your users can use. Like: color, font-family.
  • I believe it might be better to not allow short-hand forms such as background, at least in the beginning, so that you can easily parse the values. Require that they explicitly write background-color, background-image.
  • If you want URLs, only allow relative URLs, and discard everything that doesn't even closely look like a URL. Log these issues anyway, so that you can improve your parser and validator.
  • Be very strict in your parsing, discard everything that your parser doesn't understand even if it would be valid CSS. In other words, make your own CSS subset.

When parsing, the hardest part would be the parsing of complex CSS selectors. But you can impose your own subset here too.

Here's some (pseudo)code, maybe it will help you somehow:

<?php

function tokenizeCSS() {
    return array(
        array(
            'selector'   => '#foo .bar',
            'properties' => array(
                'background-color' => 'transparent',
                'color'            => '#fff',
            ),
        );
    );
}

function colorValidator($color)
{}

/**
 * This is basically the white list. Keys are accepted CSS properties
 * and values are the validator callbacks.
 */
$propertyValidators = array(
    'background-color' => 'colorValidator',
    'color'            => 'colorValidator',
);

$filteredRules = array();

foreach (tokenizeCSS() as $rule) {
    if (! validSelector($rule['selector'])) {
        continue;
    }

    foreach ($rule['properties'] as $property => $value) {
        /**
         * Check property is in white list
         */
        if (! isset($propertyValidators[$property]) {
            continue;
        }

        /**
         * Check property is valid
         */
        if (! $propertyValidators[$property]($value)) {
            continue;
        }

        /**
         * Valid rule
         */
        $filteredRules[$rule['selector']][$property] = $value;
    }
}

    CSS Howto..

    I want to override some CSS that says border:none to have border bottom. How can I do this?

    how to apply css style only to homepage

    How to toggle the sidebar menu for every browser size?

    How to get drop down menu sub lists to line up vertically

    CSS: how to layout 3 columns, 2 are optional

    How to make agile layout?

    How do I enable SASS line numbers in CSS output?

    How can I format all td elements contained in a table with class myclass in CSS?

    How to add stylesheet to toolbar

    Show image when hovering on a span

    How to apply css animation to angular view when the view is called

    How do I get my body to the bottom of my page OR further if necessary?

    Block grid shows horizontal scroll bar in Foundation

    How to showing expanding text with javascript and css

    how do I properly position & scale these elements in CSS?

    How to avoid calling “fonts.googleapis.com/css?family=..” for my CSS files

    How to make a background using CSS gradient with flat color?

    How to get the correct mouse position in relation to a div that has has a scale transform applied

    Why does a div with background-color show fixed elements below?

    How to remove the bottom border of a box with CSS

    How to to make a gap between rows in FlowPanel in Css in Gwt?

    How to access dynamic controls having same properties in the Javascript

    how to make sure that that grid layout row adjusts the height automatically as per content

    How to use font-face with cssless?

    How to add formatting to my pagination

    How to create CSS heart? / Why is this CSS creating a heart shape?

    Dropdown menu. make it show onclick and not onmouseover

    How to use Bootstrap Glyphicons in Holder.js images

    How to put text in the center of box while using border-box in CSS?

    CSS Lining up images. Always 1px difference. Why/How?