Search code examples
javascriptphpspecial-characterssanitization

Sanitize strings to avoid special characters break javascript generated by php


I have a php 'search' script that looks for the requested data in a MySQL database and prints a table. Each row of this table can be modified or deleted by clicking on an icon. When you click on one of these icons a javascript function that shows a display is called.

This is the piece of code:

      while ($row = mysqli_fetch_row($result)) {
            // Define $id
            $id = $row[7];

            // Sanitize output
            $user = htmlentities($row[0]);
            $name = htmlentities($row[1]);
            $surnames = htmlentities($row[2]);
            $email = htmlentities($row[3]);
            $role = htmlentities($row[4]);
            $access = htmlentities($row[5]);
            $center = htmlentities($row[6]);

            $message .= "<tr>
                    <td>" . $user . "</td>" .
                    "<td>" . $name . "</td>" .
                    "<td>" . $surnames . "</td>" .
                    "<td>" . $email . "</td>" .
                    "<td>" . $role . "</td>" .
                    "<td>" . $access . "</td>" .
                    "<td>" . $center . "</td>" .
                    "<td>" .
                        "<input type='image' src='../resources/edit.png' id='edit_" . $user . "' class='edit' onclick=edit_user(\"$user\",\"$name\",\"$surnames\",'$email','$role','$access',\"$center\",'$id') title='Editar'></button>" . 
                    "</td>" .
                    "<td>" .
                        "<input type='image' src='../resources/delete.png' id='delete_" . $user . "' class='delete' onclick=delete_user(\"$user\",'$role') title='Eliminar'></button>" . 
                    "</td>
                </tr>";
        }

This is just part of the table I generate. After all this, I encode the table with json_encode and echo it. The echo is captured by an ajax function that decodes it (JSON.parse) and puts it into a div.

The table is correctly rendered and everything works fine with normal characters, but I have detected I can have some problems if I have quotes, slashes and another meaningfull characters. The strings are showed correctly in the table, so there is no problem with php, but the generated javascript doesn't work with some strings.

For example, if I introduce:

</b>5'"

or:

<b>5'6"</b><br><div

as users, when I click on edit or delete icon I get some errors in javascript console:

Uncaught SyntaxError: Invalid regular expression: missing / home.php:1 Uncaught SyntaxError: missing ) after argument list

Uncaught SyntaxError: missing ) after argument list

Uncaught SyntaxError: Unexpected token ILLEGAL

I have tried with several combination of addslash, replace, htmlentites, htmlspecialchars... but I can't get the right one.

What's the right way to work with this in order to avoid any problem?

Thank you.

EDIT:

I have probed this and it seems to work:

In php I use this function:

function javascript_escape($str) {
$new_str = '';

$str_len = strlen($str);
for($i = 0; $i < $str_len; $i++) {
    $new_str .= '\\x' . dechex(ord(substr($str, $i, 1)));
}

return $new_str;
}

and then I use something like

$('<textarea />').html(user).text()

in javascript to decode the string.

Is this safe against XSS attacks?


Solution

  • First, create an HTML-safe JSON string of the array and modify your code to use a data attribute like so:

          while ($row = mysqli_fetch_row($result)) {
                // Define $id
                $id = $row[7];
    
                // Sanitize output
                $user = htmlentities($row[0]);
                $name = htmlentities($row[1]);
                $surnames = htmlentities($row[2]);
                $email = htmlentities($row[3]);
                $role = htmlentities($row[4]);
                $access = htmlentities($row[5]);
                $center = htmlentities($row[6]);
    
                $json_str_edit = htmlentities(json_encode(array($row[0], $row[1], $row[2], $row[3], $row[4], $row[5], $row[6], $id)));
                $json_str_delete = htmlentities(json_encode(array($row[0], $row[4])));
    
                $message .= "<tr>
                        <td>" . $user . "</td>" .
                        "<td>" . $name . "</td>" .
                        "<td>" . $surnames . "</td>" .
                        "<td>" . $email . "</td>" .
                        "<td>" . $role . "</td>" .
                        "<td>" . $access . "</td>" .
                        "<td>" . $center . "</td>" .
                        "<td>" .
                            "<input type=\"image\" src=\"../resources/edit.png\" id=\"edit_$user\" class=\"edit\" data-user=\"$json_str_edit\" title=\"Editar\"></button>" . 
                        "</td>" .
                        "<td>" .
                            "<input type=\"image\" src=\"../resources/delete.png\" id=\"delete_$user\" class=\"delete\" data-user=\"$json_str_delete\" title=\"Eliminar\"></button>" . 
                        "</td>
                    </tr>";
            }
    

    Then create an event listener to catch associated click events in JS like this:

    function edit_user(user, name, surnames, email, role, access, center, id) {
        // `this` will refer to the html element involved in the event
    
        }
    
    function delete_user(user, role) {
        // `this` will refer to the html element involved in the event
    
        }
    
    document.addEventListener('click', function(event) {
        if(event.target.hasAttribute('data-user')) {
            switch(event.target.className) {
                case 'edit':
                    edit_user.apply(event.target, JSON.parse(event.target.dataset.user));
                    break;
                case 'delete':
                    delete_user.apply(event.target, JSON.parse(event.target.dataset.user));
                    break;
                }
            }
        }, false);
    

    Alternatively from the addEventListener method, you can simply add this onclick event listener directly to the element like so (I really don't think it matters in this case):

    onclick="edit_user.apply(this, JSON.parse(this.dataset.user))"
    

    FYI It's more common practice to use single quotes in scripts to avoid having to escape the double quote characters. Makes things both cleaner and more standardized.