Some context
I've been tasked with supplying an escaping function to arbitrary CSS values that are entered through a form. The goals and caveats are:
- I know it's bad practice to let users input CSS. Deal with it.
- Data will be injected either to a
style
attribute, or to an external stylesheet. - This is run on PHP 5.1 (Again, I know, deal with it).
- I'm trying to follow this cheat sheet as closely as possible.
The Code
/**
* ord() alternative that works with UTF8 characters
* @param string $c
*
* @return int UTF-8 character code value
*/
function getUTF8CharCode($c) {
$h = ord($c{0});
if ($h <= 0x7F) {
return $h;
} else if ($h < 0xC2) {
return false;
} else if ($h <= 0xDF) {
return ($h & 0x1F) << 6 | (ord($c{1}) & 0x3F);
} else if ($h <= 0xEF) {
return ($h & 0x0F) << 12 | (ord($c{1}) & 0x3F) << 6
| (ord($c{2}) & 0x3F);
} else if ($h <= 0xF4) {
return ($h & 0x0F) << 18 | (ord($c{1}) & 0x3F) << 12
| (ord($c{2}) & 0x3F) << 6
| (ord($c{3}) & 0x3F);
} else {
return -1;
}
}
/**
* Escape a single character for CSS context.
* @param $c
* @return string
*/
function escapeCSSCharacter($c) {
return "\\" . base_convert(getUTF8CharCode($c), 10, 16) . " ";
}
/**
* Escape CSS rule
*
* @param string $data The CSS rule
* @param array $immuneChars Array of immune character. These characters will not be escaped.
*
* @return string Escaped string
*/
function escapeCSSValue($data, array $immuneChars = array()) {
$result = "";
for ($i = 0; $i < mb_strlen($data); $i++) {
$currChar = mb_substr($data, $i, 1);
if (getUTF8CharCode($currChar) < 256 && //Character value is less than 256
!preg_match("/^\w$/", $currChar) && //Character is not alphanumeric (underscore is considered safe too)
!in_array($currChar, $immuneChars) //Character is not immune
) {
$result .= escapeCSSCharacter($currChar);
}
else {
$result .= $currChar;
}
}
return $result;
}
Usage
$colorRule = "color: " . escapeCSSValue("#BADA55;}*{display:none;}/*", array("#")) . ";"; //Will be obviously broken, but will not break the rest of the document.
echo $colorRule;
My worries
- Is this a good way to escape CSS? Is this safe? Will this method be impossible to break out of?
- Am I doing this relatively efficiently? Given that the strings I'm going to be encoding are arbitrary, I'm worried about attacks that involve thousands of characters.
Any review will be welcomed.