【问题标题】:Potential security issues of this user form此用户表单的潜在安全问题
【发布时间】:2015-11-14 13:30:29
【问题描述】:

我正在尝试创建一个安全的注册表单,这只是前几个字段,接下来还会有大约十几个字段,但这无关紧要。我有点安全意识,我想知道以下代码是否充分保护了 XSS 和 SQL 注入。

  • 我正在使用 htmlentities,因为如果任何条目有误,我会将一些用户输入显示给用户。
  • 我相信我正确地使用了准备好的语句。
  • 我已经包含了表格的开头,只是为了表明因为我要发布到自我,所以需要htmlentities。

    if(isset($_POST['submit'])){
    
    $firstname = htmlentities($_POST['firstname'], ENT_QUOTES, 'UTF-8');
    $lastname = htmlentities($_POST['lastname'], ENT_QUOTES, 'UTF-8');
    $email = htmlentities($_POST['email'], ENT_QUOTES, 'UTF-8');
    $emailrepeat = htmlentities($_POST['emailrepeat'], ENT_QUOTES, 'UTF-8');
    $password = htmlentities($_POST['password'], ENT_QUOTES, 'UTF-8');
    $passwordrepeat = htmlentities($_POST['passwordrepeat'], ENT_QUOTES, 'UTF-8');
    
    
    
    if (empty($firstname)) 
    {   
        $fnError = "Please Enter Your First Name";
    }
    if (empty($lastname)) 
    {
        $lnError = "Please Enter Your Last Name";
    }
    
    
    $getEmail = $mysqli->prepare('SELECT * FROM users WHERE email=?');
    $getEmail->bind_param('s', $email);
    $getEmail->execute();
    $getEmail->store_result();
    $countRows = $getEmail->num_rows;
    if ($countRows > 0) 
    {
        $emError = "Email Address Already Exists";
        $countRows = 0;
    }
    
    
    
    else if (empty($email)) 
    {
    $emError = "Please Enter an Email Address";
    }
    else if (!filter_var($email, FILTER_VALIDATE_EMAIL)) 
    {
    $emError = "Invalid Email Address";
    }
    else if ($email != $emailrepeat)
    {
        $emError = "Emails do not match";
    }
    
    if (empty($password)) 
    {
        $pwError = "Please Enter a Password";
    }
    else if (strlen($password)<6)
    {
        $pwError = "Password must be atleast 6 characters";
    }
    else if ($password != $passwordrepeat)
    {
        $pwError = "Emails do not match";
    }
    
    if (filter_var($email, FILTER_VALIDATE_EMAIL)) 
    {
    
        if ($password == $passwordrepeat and !empty($password) and strlen($password)>5)
        {
            $pwhash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));
    
            if($stmt = $mysqli->prepare("INSERT INTO users (first_name, last_name, email, password) VALUES (?, ?, ?, ?)"))
            {
                $stmt->bind_param("ssss", $firstname, $lastname, $email, $pwhash);
                $stmt->execute();
                $stmt->close();
            }
    
    
        }
    }
    }
    
    <form id="addnewuser" action="<?php echo htmlentities($_SERVER['PHP_SELF']);    ?>" method="POST"> 
    </form>
    

【问题讨论】:

  • 这对我来说看起来不错。我没有看到任何重大问题。除了下面 MonkeyZeus 的担忧,还有什么特别的原因让你选择了PASSWORD_BCRYPT 而不是PASSWORD_DEFAULT?现在它们是等价的,但请记住,PASSWORD_DEFAULT 旨在随着时间的推移随着 PHP 中添加新的和更强大的算法而改变。只是需要考虑...
  • @IanDrake 我可能会误解一些东西,但是如果本周 PASSWORD_DEFAULTAlgorithm1024 下周是 Algorithm2048 那么第一周的用户将不再能够登录在?
  • @MonkeyZeus 根据我的理解,password_hash() 总是在生成的哈希中编码算法信息,所以password_verify() 总是知道如何验证它。文档说,“使用的算法、成本和盐作为散​​列的一部分返回。因此,验证散列所需的所有信息都包含在其中。这允许 password_verify() 函数验证散列而无需单独存储用于盐或算法信息。”这两个函数的文档页面都提到了这一点。
  • @IanDrake 显然是我的误解;有时我们都需要一个好的 RTFM 时刻 ;-)
  • @MattM 一般来说,是的,我会选择PASSWORD_DEFAULT。除非您的系统有某种特殊要求,否则它将有助于使您的代码更加“面向未来”。文档页面上的这条评论重申了这一理念:php.net/manual/en/function.password-hash.php#112495。另外,我以前使用过那个库,我可以确认它可以使用 PASSWORD_DEFAULT 常量,所以我认为这不是问题。

标签: php mysql mysqli xss sql-injection


【解决方案1】:

1.感谢使用准备好的语句!

2。阅读下文

总体而言,从安全角度来看,这看起来相当不错,但在任何地方使用 htmlentities() 肯定会遇到问题。

按照您的逻辑,如果我想使用ThisIsSo&amp;lt;Secu&amp;gt;r 之类的密码,那么您的代码会将其转换为ThisIsSo&amp;lt;Secu&amp;gt;r,因此除非您也使用@,否则我将无法登录您的网站987654324@登录页面的用户名/密码。

将用户数据保存到数据库时,您应该始终保持用户输入内容的完整性,并在回显数据时实施适当的转义。 CKEditor 是个例外;但是编辑器会为您处理转义。

为了更好地说明这一点,您需要像这样使用htmlentities()

<div class="welcome-banner">Welcome <?php echo htmlentities($row['firstname']); ?>!</div>

或者像这样:

<h3>Editing Your Profile</h3>
<label>First Name</label><br>
<input type="text" name="firstname" value="<?php echo htmlentities($row['firstname']); ?>">

【讨论】:

  • 谢谢,好建议。我确实考虑过关于密码的问题。我没有将任何密码反馈给用户,所以我需要在密码字段上进行任何转义吗?
  • 好的,我已经更改了我的代码,因此用户输入直接输入到准备好的语句中,而我只是转义了反馈到输入字段中的内容。
  • 很好的答案 MonkeyZeus。尽管您应该补充一点,$email 上应该有一个主键,否则会出现竞争条件,如果足够快 (@MattM) 可以创建相同的两个用户。
  • @SilverlightFox 好建议!但也许 OP 已经在这样做了?我认为你应该通过评论这个问题来问他。我认为除非他们被标记,否则 OP 不会收到关于答案的 cmets 通知。
  • @MattM 你需要一些关于电子邮件地址的唯一键。
猜你喜欢
  • 2011-10-19
  • 2019-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多