When receiving user input on forms I want to detect whether fields like \"username\" or \"address\" does not contain markup that has a special meaning in XML (RSS feeds) or
If you're just "looking for protection for print '<h3>' . $name . '</h3>'
", then yes, at least the
second approach is adequate, since it checks whether the value would be interpreted as markup if it weren't
escaped. (In this case, the area where $name
would appear is element content, and only the characters &
, <
, and >
have special meaning when they appear in element content.) (For href
and similar attributes, the check for "JavaScript: " may be necessary, but as you stated in a comment, that isn't a goal.)
For official sources, I can refer to the XML specification:
Content production in section 3.1: Here, content consists of elements, CDATA sections, processing instructions, and comments (which must begin with <
), references (which must begin with &
), and character data (which contains any other legal character). (Although a leading >
is treated as character data in element content, many people usually escape it along with <
, and it's better safe than sorry to treat it as special.)
Attribute value production in section 2.3: A valid attribute value consists of either references (which must begin with &
) or character data (which contains any other legal character, but not <
or the quote symbol used to wrap the attribute value). If you need to place string inputs in attributes in addition to element content, the characters "
and '
need to be checked in addition to &
, <
, and possibly >
(and other characters illegal in XML).
Section 2.2: Defines what Unicode code points are legal in XML. In particular, null is illegal in an XML document and may not display properly in HTML.
HTML5 (the latest working draft, which is a work in progress, describes a very elaborate parsing algorithm for HTML documents:
<
(which begins a new tag), or &
(which begins a character reference)."
(which ends the attribute value), or &
(which begins a character reference).If string inputs are to be placed in attribute values (unless placing them there is solely for display purposes), there are additional considerations to keep in mind. For example, HTML 4 specifies:
User agents should interpret attribute values as follows:
- Replace character entities with characters,
- Ignore line feeds,
- Replace each carriage return or tab with a single space.
User agents may ignore leading and trailing white space in CDATA attribute values[.]
Attribute value normalization is also specified in the XML specification, but apparently not in HTML5.
EDIT (Apr. 25, 2019): Also, be suspicious of inputs containing—
...assuming htmlspecialchars
doesn't escape those code points already.
I am certainly not a security expert, but from what I gather something like your suggested
if (htmlspecialchars($data, ENT_NOQUOTES, 'UTF-8') === $data)
should work to prevent you from passing on contaminated strings, given you got your encoding right there.
XSS attacks that don't require '<' or '>' rely on the string being handled in a JavaScript block right there and then, which, from how I read your question, is not what you are concerned with in this situation.
filter_input + FILTER_SANITIZE_STRING (there are lots of flag you can chose from)
:- http://www.php.net/manual/en/filter.filters.sanitize.php
HTML Purifier does a good job and is very easy to implement. You could also use a Zend Framework filter like Zend_Filter_StripTags.
HTML Purifier doesn't just fix HTML.
You can make use of the strip_tags function in PHP. This function will strip HTML and PHP tags from given data.
For example, $data is the variable which holds your content then you can use this like this:
if (strlen($data) != strlen(strip_tags($data))){
return false;
}
else{
return true;
}
It will check stripped content against the original content. If both are equal then we can hope there aren't any HTML tags, and it returns true. Otherwise, it returns false as it found some HTML tags.
In a comment above, you wrote:
Just stop the browser from treating the string as markup.
This is an entirely different problem to the one in the title. The approach in the title is usually wrong. Stripping out tags just mangles input and can lead to data loss. Ever tried to talk about HTML on a blog that strips tags? Frustrating.
The solution that is usually the correct one is to do as you said in your comment - to stop the browser from treating the string as markup. This - literally taken - is not possible. What you do instead is encode the content as HTML.
Consider the following data:
<strong>Test</strong>
Now, you can look at this one of two ways. You can look at it as literal data - a sequence of characters. You can look at it as HTML - markup that includes strongly emphasises text.
If you just dump that out into an HTML document, you are treating it as HTML. You can't treat it as literal data in that context. What you need is HTML that will output the literal data. You need to encode it as HTML.
Your problem is not that you have too much HTML - it's that you have too little. When you output <
, you are outputting raw data in an HTML context. You need to convert it to <
, which is the HTML representation of that data before outputting it.
PHP offers a few different options for doing this. The most direct is to use htmlspecialchars() to convert it into HTML, and then nl2br() to convert the line breaks into <br>
elements.