如何防止 PHP echo 结束一个 JS 字符串?

问题描述

我有这个代码

<!DOCTYPE html>
<html>
    <head>
        <title>Sign up page</title>
        <Meta charset="UTF-8"/>
    </head>
    <body>
        <h1>Sign up page</h1>
        <form action="test.PHP" method="post">
            <input placeholder="Enter your username" name="username"/><br/>
            <input type="password" placeholder="Enter your password" style="margin-bottom:5px" name="password"/><br/>
            <button type="submit" name="submit">Sign up</button>
        </form>
        <?PHP
        if(isset($_POST["username"],$_POST["password"],$_POST["submit"])){
            if(mb_strlen($_POST["username"])<8){
            ?>
            <script>
                alert("Your username must be at least 8 characters long.");
                var i=document.getElementsByTagName("input")[0];
                i.value="<?PHP echo $_POST['username']; ?>";
                i.focus();
            </script>
            <?PHP
            }else if(mb_strlen($_POST["password"])<8){
            ?>
            <script>
                alert("Your password must be at least 8 characters long.");
                var i=document.getElementsByTagName("input")[1];
                i.value="<?PHP echo $_POST['password']; ?>";
                i.focus();
            </script>
            <?PHP
            }else{
                echo "Successfully signed up!";
            }
        }
        ?>
    </body>
</html>

它在大多数情况下都能正常工作,但如果您尝试在用户名字段中输入 ";f();,您会在控制台中收到错误消息并且没有警报。

很明显,这是因为当 PHP 接收到输入时,它会在 JS 字符串中回显它。 "; 结束字符串和语句,而 f(); 会导致阻止输入聚焦的错误。这发生在 8 个字符以内,因此导致它属于 mb_strlen($_POST["username"])<8

通常我只会使用 htmlspecialchars,但是如果您尝试添加它,那么如果您输入 ";<!--,它会出现 &lt; 而不是 <。一些用户可能希望在他们的用户名中包含 <(或其他 &*; 字符),并且(如果他们不是开发者)会惊讶于 &lt; 的含义。

那么如何在保持用户友好性的同时防止发生 JavaScript 注入?

解决方法

您应该使用 json_encode 来输出您的值(并注意您不再需要封闭的 "):

i.value=<?php echo json_encode($_POST['username']); ?>;

这将确保字符串中的引号被转义,并且整个值被 JavaScript 视为字符串。对于您的示例数据,这将产生

"\";f();"
,

问题是这个例子中的双引号。试试addslashes

i.value="<?php echo addslashes($_POST['username']); ?>";

虽然 Nick's 答案可能对 JS 更好。我比 JS 更懂 PHP。

,

你永远不应该将未经消毒的代码注入任何东西。这至少会导致 XSS 攻击。

我认为在保持用户友好的同时清理代码的最简单方法是使用 json_encode()

<?php
    }else if(mb_strlen($_POST["password"])<8){
$data = json_encode(['password' => $_POST['password']]);
?>

<script>
    alert("Your password must be at least 8 characters long.");
    var user_data = <?php echo $data; ?>;
    var i=document.getElementsByTagName("input")[1];
    i.value=user_data.password;
    i.focus();
</script>

但是!

你真的应该把它放在 Input 元素中:

<input type="password" value="<?=htmlspecialchars($_POST['password'] ?? '',ENT_QUOTES); ?>" placeholder="Enter your password" style="margin-bottom:5px" name="password"/>