Safe Output in PHP: Display Text, Not HTML

Practical advice from AriZone

In a training catalogue, a product name contains angle brackets and quotation marks. After it is inserted into a PHP template, part of the name becomes bold even though the editor entered plain text. This indicates that the browser received data as markup. Safe output in PHP starts with separating text from HTML correctly: we will test that distinction in a small file without a database, real users or dangerous scenarios.

The exercise has a specific goal: the same string must remain text both in a paragraph and in a field value. We will not create a universal cleaner for every format. Instead, we will produce a repeatable check for two defined output locations, along with the boundaries beyond which a different approach is needed. The training data does not describe an incident involving an AriZone client.

Identify where the value will appear first

Open the template and find the actual operation that outputs the name: echo, a short echo tag or a template engine call. Next to it, note where the browser will read the result. Text between ordinary tags and the value of a quoted, ordinary text attribute are not the same as JavaScript, CSS or a URL. A variable name alone tells you nothing about whether inserting it is safe.

Do not start by replacing every string in the database. One record might be used in an email, a JSON response and an HTML page, each with different rules. Keep the original text and apply the appropriate transformation near the relevant output. Input length and format validation are also necessary, but do not replace this step. For example, a perfectly acceptable name can contain an ampersand.

The exercise requires a configured, supported PHP CLI, an editor and a new test directory outside the website’s public directory. No additional packages, administrator privileges or server connection are required. Do not edit a live theme for this experiment. If the php command is unavailable, use the interpreter already configured in your development environment; installing a different environment is not part of this example.

Keep the string as text in two controlled locations

Create a new file named escape-demo.php in the test directory with the following contents. The filename is an example: if you change it, update the commands below too. The label string contains fictional data and does not need to be replaced with real enquiries. The function is intended only for HTML text and quoted ordinary text attributes. Do not apply it to an arbitrary attribute simply because that attribute is surrounded by quotation marks.

<?php
function escapeHtml(string $text): string
{
    return htmlspecialchars(
        $text,
        ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5,
        'UTF-8'
    );
}

$label = '<strong>Demo & "draft"</strong>';
?>
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Escaping demo</title>
<p><?= escapeHtml($label) ?></p>
<input value="<?= escapeHtml($label) ?>">
</html>

The htmlspecialchars call supplies explicit parameters so that its behaviour does not depend on an accidental encoding setting. ENT_QUOTES covers both kinds of quotation marks, ENT_SUBSTITUTE specifies replacement of invalid sequences, and ENT_HTML5 matches the example’s document type. Details are available in the official PHP manual. Replacing damaged characters does not repair the data source: investigate the cause of incorrect encoding separately.

Notice the double quotation marks around value: the data must not determine the attribute’s boundaries. We fix the attribute name and document structure in the template, leaving only its text value variable. The example does not generate dynamic tag names, attribute names or event handlers. This is deliberately a narrow task with a result that can be described clearly.

Check the HTML, not just the absence of PHP errors

In the terminal, move to the directory containing the new file and run both commands. The first checks syntax without execution; the second runs the local example and prints the document in the terminal. They do not install dependencies or change website data. Do not run this file on a production server, where the check could accidentally involve someone else’s working directory.

php -l escape-demo.php
php escape-demo.php

Expect PHP to finish without a syntax error and the printed document to represent the data’s angle brackets as entities. The template’s own p and input tags should remain ordinary markup. This distinction matters: the value is transformed, not the entire finished document. Do not confuse the terminal’s text with what a user should see in the browser.

For a visual check, use an isolated local site in your development environment without exposing it externally. Once the example is rendered, the paragraph should display literal text containing strong rather than making the word bold; the field should contain the entire string, including quotation marks. In developer tools, confirm that the data has not created a strong child element or additional input attributes. On a narrow screen, also check that the complete field value is accessible.

Next, substitute an empty string, a Ukrainian name, an ampersand and both kinds of quotation marks separately. Record the expectation for each case. If you see a literal & instead of an ampersand, investigate repeated escaping in the output chain. Do not disable the transformation globally to make one example look better. In a template engine with automatic escaping, check its rules before adding your own function on top.

Do not carry an HTML rule into other contexts

This function does not validate a link’s destination or make arbitrary HTML safe. Text with permitted formatting requires a separate HTML sanitisation policy; JavaScript, CSS and URLs need their own rules and restrictions. Do not insert untrusted data into onClick, style or script. The OWASP XSS prevention cheat sheet explains these context boundaries.

Before moving the solution into a project, make a separate version-controlled change and check it on a test copy. It helps to review the Git commit before pushing so that adjacent contexts are not changed accidentally. To roll back, revert the specific template change rather than disabling protection throughout the application. In conclusion, the correct result is predictable text in a defined location. If templates mix different formats, AriZone can help examine output locations and prepare targeted checks.

Discussion

Leave a comment

Add a comment

Your email will not be published. Required fields are marked *.

Cloudflare Turnstile spam protection