如何防止跨站点脚本

时间:2015-04-20 10:52:46

标签: php security xss

我有以下用户填写的表格:

<form name="form" action="" method="POST">
    <table width="100%" border="0"  cellpadding="2" cellspacing="2">
  <tr>
    <td width="25%" ><div align="right"><strong>Name:</strong></div></td>
    <td width="75%" ><span id="sprytextfield1">
      <input id="Cname"name="Name" type="text" placeholder="Please fill in your name">
      <span class="textfieldRequiredMsg">A value is required.</span></span></td>
  </tr>
  <tr>
    <td><div align="right"><strong>Email:</strong></div></td>
    <td><span id="sprytextfield2">
    <input id="Cemail"name="email" type="text" placeholder="e.g sales@company.co.uk">
    <span class="textfieldRequiredMsg">A value is required.</span><span class="textfieldInvalidFormatMsg">Invalid format.</span></span></td>
  </tr>
  <tr>
    <td><div align="right"><strong>Phone Number:</strong></div></td>
    <td>
    <input id="Cphone" name="Phone" type="text"placeholder="e.g. 5555-6666666">
    </td>
  </tr> 

  <tr>
    <td>&nbsp;</td>
    <td><input name="Manufacturer" type="hidden" value="<?php echo $row_emailProduct['Manufacturer']; ?>">
    <input name="Model" type="hidden" value="<?php echo $row_emailProduct['Model']; ?>">
    <input name="Color" type="hidden" value="<?php echo $row_emailProduct['Color']; ?>">
    <input name="price" type="hidden" value="<?php echo $row_emailProduct['price']; ?>">
    <input name="id" type="hidden" value="<?php echo htmlentities($_GET['id']); ?>">
    <input name="insert" id="insert" type="submit" value="Send Request"></td>
  </tr></tr>

</table>
         </form>

提交表单后会发生以下情况:

if (isset($_POST["insert"])){
    $OK=false;
  $insertSQL = "INSERT INTO Item_intrest (Manufacturer, Model, Color, price, Name, Phone, email) VALUES (:Manufacturer, :Model, :Color, :price, :Name, :Phone, :email)";


  $Result1 = $conn->prepare($insertSQL) or die(errorInfo());
  $Result1->bindParam(':Manufacturer', htmlentities($_POST['Manufacturer']), PDO::PARAM_STR);
  $Result1->bindParam(':Model', htmlentities($_POST['Model']), PDO::PARAM_STR);
$Result1->bindParam(':Color', htmlentities($_POST['Color']), PDO::PARAM_STR);
$Result1->bindParam(':price', htmlentities($_POST['price']), PDO::PARAM_STR);
$Result1->bindParam(':Name', htmlentities($_POST['Name']), PDO::PARAM_STR);
$Result1->bindParam(':Phone', htmlentities($_POST['Phone']), PDO::PARAM_STR);
$Result1->bindParam(':email', htmlentities($_POST['email']), PDO::PARAM_STR);
$Result1->execute();
$OK = $Result1->rowCount();



/*email to shop */
$emailsubject = 'Product Request';
$webmaster = 'sales@company.co.uk';
/*data collection */
$Name = htmlentities($_POST['Name']);
$email = htmlentities($_POST['email']);
$Phone = htmlentities($_POST['Phone']);
$item1 = htmlentities($_POST['Manufacturer']);
$item2 = htmlentities($_POST['Model']);
$item3 = htmlentities($_POST['Color']);
$Price = htmlentities($_POST['price']);

$Body = <<<EOD
<br><hr><br>
Name: $Name<br>
Email: $email<br>
Phone: $Phone<br>
Product:$item1, $item2,$item3<br>
Price: $Price<br>
EOD;
    $headers = "From: $email\r\n";
    $headers .= "content-type: text/html\r\n";
    $succes = mail($webmaster, $emailsubject, $Body, $headers);


if($OK){
  header('Location: /thankyourequest.php?id=' . htmlentities($_GET['id']). '');
  exit;
}else {
    $errorInfo = $Result1->errorInfo();
    if(isset($errorInfo[2])){
        $error = $errorInfo[2];
        }
}
}

出于某种原因,当它是扫描时,它返回

From: &lt [mailto:&lt] 
Sent: 20 April 2015 10:04
To: sales@company.co.uk
Subject: Product Request



Name: <script>alert("xssvuln")</script>
Email: <script>alert("xssvuln")</script>
Phone: <script>alert("xssvuln")</script>
Product:<script>alert("xssvuln")</script>, <script>alert("xssvuln")</script>,<script>alert("xssvuln")</script>
Price: <script>alert("xssvuln")</script>

正如你所看到的那样,我试图用这种方法来防止这种情况,但这似乎还不够。 欢迎任何帮助以防止这种情况

2 个答案:

答案 0 :(得分:3)

请勿在输入过程中尝试阻止XSS攻击。总是逃避输出。

另见:Stored XSS in Wordpress 4.2 caused by MySQL column truncation。对输出进行过滤会阻止这些条件。

相反,您要做的只是使用预准备语句并将数据存储为裸。 (当然,您仍然应该验证数据!确保他们在您要求提供电子邮件地址时等等。)

当您从数据库中提取数据以显示在网页上时,就是您要过滤的时间。你想这样做(假设你不需要允许用户提供一些HTML):

echo htmlentities($row['column'], ENT_QUOTES | ENT_HTML5, 'UTF-8');

为什么ENT_QUOTES | ENT_HTML5'UTF-8'

我假设您的网页使用HTML5(即<!DOCTYPE html>)且您的字符集为UTF-8(即<meta>标记以及HTTP {{1} }标题)。如果你正在使用不同的东西,请调整。

我们指定Content-Type告诉ENT_QUOTES转义引号字符(htmlentities()")。这对于以下情况很有帮助:

'

如果您未指定<input type="text" name="field" value="<?php echo $escaped_value; ?>" /> ,攻击者只需将ENT_QUOTES作为值传递给该表单字段即可!即时客户端代码执行。

我们指定" onload="alert('XSS');,因此'UTF-8'知道要使用的字符集。我们这样做的原因是,demonstrated against mysql_real_escape_string(),不正确的(特别是攻击者控制的)字符编码可能会破坏基于字符串的转义策略。

请注意,这将转义所有HTML特殊字符,并阻止用户提供任何标记。如果您需要允许某些 HTML,我们会概述the best strategies for preventing XSS。简而言之:

  • 使用Twig?以下示例是安全的。请注意使用htmlentities()块来指定默认策略,但使用{% autoescape %}替换|e('html')

    other_variable
  • 如果其他所有方法都失败了,请使用HTML Purifier

答案 1 :(得分:0)

这称为安全问题。跨站点脚本,你有很多方法可以避免它,

What's the best method for sanitizing user input with PHP?

例如,如果您可以选择输入电子邮件地址,则必须如下所示进行验证:

<?php

$email = filter_var($_POST['username'], FILTER_SANITIZE_EMAIL);

?>

如果有输入字符串的选项,那么您需要进行验证,如下所示

<?php
            $password = trim(filter_var($_POST['password'], FILTER_SANITIZE_STRING));

?>

在你的情况下你必须做类似下面的事情

$Name = htmlentities($_POST['Name']);
$email = htmlentities($_POST['email']);

而不是上述方法,请遵循过滤消毒方法:

$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$Name = trim(filter_var($_POST['Name'], FILTER_SANITIZE_STRING));