如何使用 PHP 清理用户输入?

How can I sanitize user input with PHP?

提问人:Brent 提问时间:9/25/2008 最后编辑:TylerHBrent 更新时间:5/2/2023 访问量:674105

问:

某处是否有一个包罗万象的函数可以很好地清理 SQL 注入和 XSS 攻击的用户输入,同时仍然允许某些类型的 HTML 标记?

php 安全 xss sql 注入 user-input

评论


答:

82赞 Daniel Papasian 9/25/2008 #1

不。一般而言,无法在没有任何数据用途上下文的情况下筛选数据。有时您希望将 SQL 查询作为输入,有时您希望将 HTML 作为输入。

您需要过滤白名单上的输入 -- 确保数据符合您期望的某种规范。然后,您需要在使用它之前对其进行转义,具体取决于您使用它的上下文。

转义 SQL 数据的过程 - 防止 SQL 注入 - 与转义数据的过程 (X)HTML 的过程非常不同,以防止 XSS。

26赞 jasonbar 9/25/2008 #2

要解决 XSS 问题,请查看 HTML Purifier。它是相当可配置的,并且有一个不错的记录。

至于SQL注入攻击,解决方案是使用预准备语句。PDO 库和 mysqli 扩展支持这些。

评论

0赞 paan 9/25/2008
没有“最佳方法”可以做一些事情,比如对输入进行消毒。使用一些库,html净化器就好了。这些库已经多次受到冲击。因此,它比您自己想出的任何东西都要防弹得多
0赞 Steve Clay 6/7/2010
另请参阅 bioinformatics.org/phplabware/internal_utilities/htmLawed 。据我了解,WordPress 使用旧版本,core.trac.wordpress.org/browser/tags/2.9.2/wp-includes/kses.php
0赞 drtechno 10/2/2019
wordpress 的问题在于,它不一定是导致数据库泄露的 php-sql 注入攻击。错过存储 xml 查询泄露机密的数据的编程插件问题更大。
52赞 Peter Bailey 9/25/2008 #3

不,没有。

首先,SQL注入是一个输入过滤问题,而XSS是一个输出转义问题,所以你甚至不会在代码生命周期中同时执行这两个操作。

基本经验法则

  • 对于 SQL 查询,绑定参数
  • 使用 strip_tags() 过滤掉不需要的 HTML
  • 使用 htmlspecialchars() 转义所有其他输出,并注意此处的第 2 个和第 3 个参数。

评论

1赞 Robert Mark Bram 10/29/2012
因此,只有当您知道输入具有要分别删除或转义的 HTML 时,您才使用 strip_tags() 或 htmlspecialchars() - 您没有将其用于任何安全目的,对吧?另外,当你做绑定时,它对 Bobby Tables 之类的东西有什么作用?“罗伯特”);DROP TABLE Students;--“ 它只是转义了引号吗?
2赞 jbo5112 4/30/2014
如果您的用户数据将进入数据库,然后显示在网页上,那么读取的次数通常不会比写入的次数多得多吗?对我来说,在存储它之前过滤一次(作为输入)更有意义,而不是每次显示它时都必须过滤它。是我遗漏了什么,还是一群人投票支持不必要的性能开销,而这个答案是公认的?
2赞 Jo Smo 7/14/2014
对我来说最好的答案。如果你问我,它很短,可以很好地解决这个问题。是否有可能通过注入 _POST 美元或 _GET 美元以某种方式攻击 PHP,或者这是不可能的?
0赞 drtechno 9/19/2019
哦,是的,$post 和 $get 数组接受所有字符,但如果允许在发布的 PHP 页面中枚举该字符,则可以使用其中一些字符来对付您。因此,如果您不转义封装字符(如“,'和'),它可能会打开攻击媒介。' 字符经常被遗漏,可用于形成命令行执行技巧。卫生可以防止用户输入黑客攻击,但不会帮助您解决 Web 应用程序防火墙黑客攻击。
1304赞 troelskn 9/25/2008 #4

一个常见的误解是,用户输入可以被过滤。PHP甚至有一个(现在已经不复存在)的“功能”,称为魔术引号,它建立在这个想法之上。这是无稽之谈。忘记过滤(或清洁,或人们称之为什么)。

为了避免出现问题,你应该做的很简单:每当你在外来代码中嵌入一段数据时,你必须根据该代码的规则来格式化它。但是您必须明白,这些规则可能太复杂了,无法尝试手动遵循它们。例如,在 SQL 中,字符串、数字和标识符的规则都是不同的。为方便起见,在大多数情况下,有一个专门的工具用于此类嵌入。例如,当必须在 SQL 查询中使用某些数据时,必须使用预准备语句通过查询中的参数来完成,而不是直接向 SQL 字符串添加变量。它将处理所有正确的格式。

另一个例子是 HTML:如果在 HTML 标记中嵌入字符串,则必须使用 htmlspecialchars 对其进行转义。这意味着每个 OR 语句都应使用 .echoprinthtmlspecialchars

第三个示例可能是 shell 命令:如果要将字符串(例如参数)嵌入到外部命令中,并使用 exec 调用它们,则必须使用 escapeshellcmdescapeshellarg

此外,一个非常引人注目的例子是 JSON。这些规则是如此众多和复杂,以至于您永远无法手动遵循它们。这就是为什么你永远不应该手动创建JSON字符串,而应该始终使用一个专用的函数json_encode()来正确格式化每个数据位。

依此类推......

唯一需要主动筛选数据的情况是接受预先格式化的输入。例如,如果您让用户发布您计划在网站上显示的 HTML 标记。但是,您应该明智地不惜一切代价避免这种情况,因为无论您过滤得多么好,它始终是一个潜在的安全漏洞。

评论

273赞 Bobby Jack 10/20/2008
“这意味着每个 echo 或 print 语句都应该使用 htmlspecialchars”——当然,你的意思是“每个......输出用户输入的语句“;htmlspecialchars()-ifying “echo 'Hello, world!';” 会很疯狂;)
12赞 Kornel 9/10/2009
我认为过滤是正确的解决方案:UTF-8。您不希望整个应用程序都出现无效的 UTF-8 序列(根据代码路径,您可能会获得不同的错误恢复),并且可以轻松过滤(或拒绝)UTF-8。
7赞 troelskn 10/30/2011
@jbyrd - 不,LIKE使用专门的正则表达式语言。您必须对输入字符串进行两次转义 - 一次用于正则表达式,一次用于 mysql 字符串编码。它是代码中的代码,代码中的代码。
12赞 Marcel Korpel 6/5/2013
目前已弃用。如今,使用预准备语句来防止 SQL 注入被认为是很好的做法。因此,请切换到 MySQLi 或 PDO。mysql_real_escape_string
7赞 troelskn 7/16/2014
因为你限制了攻击面。如果尽早(输入时)进行清理,则必须确保应用程序中没有其他漏洞可以进入不良数据。然而,如果你做得很晚,那么你的输出函数不必“相信”它得到了安全的数据——它只是假设一切都是不安全的。
64赞 SchizoDuckie 9/25/2008 #5

PHP 现在有了新的漂亮的filter_input函数,例如,现在有一个内置类型,让您免于寻找“终极电子邮件正则表达式”FILTER_VALIDATE_EMAIL


我自己的过滤器类(使用 JavaScript 突出显示错误的字段)可以通过 ajax 请求或普通表单发布来启动。 <? /** * 猪肉成型验证器。按正则表达式验证字段并可以清理它们。使用 PHP filter_var内置函数和额外的正则表达式 * @package猪肉 */

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 * 
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;
    

    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }
    
        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;
             
        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }
    
    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       
    


}

当然,请记住,您还需要根据您使用的数据库类型进行 sql 查询转义(例如,mysql_real_escape_string() 对 sql 服务器毫无用处)。您可能希望在适当的应用程序层(如 ORM)上自动处理此问题。另外,如上所述:要输出到 html,请使用其他 php 专用函数,如 htmlspecialchars ;)

要真正允许带有类似剥离类和/或标签的 HTML 输入,取决于专用的 xss 验证包之一。不要编写自己的正则表达式来解析 HTML!

评论

26赞 rjmunro 8/1/2011
这看起来可能是一个用于验证输入的方便脚本,但它与问题完全无关。
1赞 mercury 10/12/2021
我不同意使用 ORM ,这超出了工程 imo。
0赞 Reham Fahmy 12/6/2021
@PHP >= 8.0 给出错误Parse error: syntax error, unexpected '->' (T_OBJECT_OPERATOR)
2赞 SchizoDuckie 7/6/2022
@Reham Fahmy:这段代码来自 2008 年。现在是 2022 年。不要使用这个。使用框架。
247赞 Andy Lester 10/9/2008 #6

不要尝试通过清理输入数据来阻止 SQL 注入。

相反,不允许在创建 SQL 代码时使用数据。使用使用绑定变量的预准备语句(即在模板查询中使用参数)。这是保证不会注入 SQL 的唯一方法。

请参阅我的网站 http://bobby-tables.com/,了解有关防止 SQL 注入的更多信息。

评论

22赞 a coder 11/13/2014
或者访问官方文档并学习 PDO 和准备好的语句。学习曲线很小,但如果你非常了解 SQL,你就不会有任何问题地适应。
4赞 Scott Arciszewski 5/30/2015
对于SQL注入的具体情况,这是正确答案!
7赞 Basic 8/16/2015
请注意,预准备语句不会增加任何安全性,参数化查询会增加。它们恰好很容易在PHP中一起使用。
0赞 Ramon Bakker 2/22/2016
这不是唯一有保证的方式。十六进制输入和查询中的取消十六进制也将阻止。此外,如果您正确使用十六进制,则不可能进行十六进制攻击。
1赞 Abraham Brookes 1/9/2017
如果您输入的是专门的内容,例如电子邮件地址或用户名,该怎么办?
15赞 Hamish Downer 3/9/2010 #7

在特定情况下,有一个技巧可以提供帮助,例如,您在 WHERE 子句中使用 id 是确保 id 肯定是一个整数,如下所示:/mypage?id=53

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

但当然,这只会减少一种特定的攻击,所以请阅读所有其他答案。(是的,我知道上面的代码不是很好,但它显示了具体的防御措施。

评论

12赞 Duc Tran 7/22/2013
我使用 $id = intval($id) 代替:)
0赞 test 12/22/2014
转换整数是确保仅插入数值数据的好方法。
1赞 vladkras 11/21/2016
$id = (int)$_GET['id']而且也很好$que = sprintf('SELECT ... WHERE id="%d"', $id)
0赞 mopsyd 1/4/2021
也许可能适合你。如果我能根据传递给某个参数的已知架构确定该参数绝对无效,我宁愿根本不进行数据库调用。if (isset($_GET['id']) { if !( (int) $_GET['id'] === intval($_GET['id'] ) ) { throw new \InvalidArgumentException('Invalid page id format'); } /* use a prepared statement for insert here */ };
10赞 Andrew 7/16/2012 #8

您在这里描述的是两个独立的问题:

  1. 清理/过滤用户输入数据。
  2. 转义输出。

1) 用户输入应始终被认为是错误的。

使用准备好的语句,或/和用mysql_real_escape_string进行过滤绝对是必须的。 PHP 还内置了 filter_input,这是一个很好的起点。

2)这是一个很大的主题,它取决于输出数据的上下文。对于 HTML,有诸如 htmlpurifier 之类的解决方案。 根据经验,请始终转义您输出的任何内容。

这两个问题都太大了,无法在一篇文章中讨论,但是有很多帖子可以更详细地介绍:

方法 PHP 输出

更安全的PHP输出

20赞 dangel 10/15/2012 #9

PHP 5.2 引入了 filter_var 函数。

它支持大量的 、 过滤器。SANITIZEVALIDATE

3赞 user138720 11/18/2014 #10

只是想补充一下输出转义的主题,如果你使用 php DOMDocument 来制作你的 html 输出,它将在正确的上下文中自动转义。属性 (value=“”) 和 <span> 的内部文本不相等。 为了防止 XSS 安全,请阅读以下内容: OWASP XSS 预防备忘单

评论

0赞 site80443 8/11/2023
说到 OWASP 备忘单,我个人的看法是,教育 PHP 开发人员进行安全编码的最佳方式是聘请道德黑客的服务或学习如何使用和运行 Kali Linux / Metasploit,然后演示各种漏洞利用。当开发人员看到一个漏洞的实际运作方式时,其影响远高于十几个SO问答。然后感兴趣的开发人员开始研究文件格式、协议、查询日志记录、需求 Wireshark 等。在那之前,他们遵循关于希望和信仰的问答。同样,这是个人意见。YMMV。
8赞 Alejandro Silva 5/27/2015 #11

如果您使用的是 PostgreSQL,则可以使用pg_escape_literal()

$username = pg_escape_literal($_POST['username']);

文档中:

pg_escape_literal()转义用于查询 PostgreSQL 数据库的文本。它以 PostgreSQL 格式返回转义文本。

评论

2赞 kittycat 5/30/2015
pg_escape_literal() 是推荐用于 PostgreSQL 的函数。
4赞 Ondřej Šotek 7/18/2015 #12

避免在清理输入和转义数据时出错的最简单方法是使用 PHP 框架,如 SymfonyNette 等或该框架的一部分(模板引擎、数据库层、ORM)。

默认情况下,像 Twig 或 Latte 这样的模板引擎具有输出转义功能 - 如果您根据上下文(网页的 HTML 或 Javascript 部分)正确转义了输出,则不必手动解决。

框架会自动清理输入,您不应直接使用 $_POST、$_GET 或 $_SESSION 变量,而应通过路由、会话处理等机制。

对于数据库(模型)层,有像 Doctrine 这样的 ORM 框架或像 Nette Database 这样的 PDO 包装器。

你可以在这里阅读更多关于它的信息 - 什么是软件框架?

18赞 webaholik 12/18/2017 #13

没有包罗万象的功能,因为有多个问题需要解决。

  1. SQL注入 - 今天,一般来说,每个PHP项目都应该通过PHP数据对象(PDO)使用准备好的语句作为最佳实践,防止杂散引用的错误以及针对注入的全功能解决方案。这也是访问数据库的最灵活、最安全的方式。

查看(唯一合适的)PDO 教程,了解您需要了解的有关 PDO 的几乎所有信息。(衷心感谢顶级 SO 贡献者 @YourCommonSense 提供有关该主题的重要资源。

  1. XSS - 在进入过程中清理数据...
  • HTML Purifier 已经存在了很长时间,并且仍在积极更新。您可以使用它来清理恶意输入,同时仍然允许大量且可配置的标签白名单。适用于许多所见即所得的编辑器,但对于某些用例来说可能很重。

  • 在其他情况下,我们根本不想接受 HTML/Javascript,我发现这个简单的函数很有用(并且已经通过了针对 XSS 的多次审核):

    /* Prevent XSS input */
    function sanitizeXSS () {
        $_GET   = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
        $_POST  = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
        $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;
    }
    
  1. XSS - 在出路时清理数据...除非您保证在将数据添加到数据库之前已正确清理数据,否则您需要在将其显示给用户之前对其进行清理,否则我们可以利用以下有用的 PHP 函数:
  • 调用或显示用户提供的值时,请使用 htmlspecialchars,除非数据已正确清理安全并允许显示 HTML。echoprint
  • json_encode 是将用户提供的值从 PHP 提供给 Javascript 的安全方法
  1. 您是使用 exec() 或 system() 函数调用外部 shell 命令,还是使用反引号运算符?如果是这样,除了SQL注入和XSS之外,您可能还需要解决其他问题,即用户在您的服务器上运行恶意命令。如果要转义整个命令,则需要使用 escapeshellcmdescapeshellarg 来转义单个参数。

评论

0赞 drtechno 10/2/2019
可以用mb_encode_numericentity代替吗?既然它编码了一切?
0赞 webaholik 10/3/2019
@drtechno - 在 #3 XSS 的链接中讨论mb_encode_numericentityhtmlspecialchars
0赞 Abel LIFAEFI MBULA 10/12/2020
据我所知,XSS是一个输出问题,而不是一个输入问题。
0赞 webaholik 10/14/2020
@bam - 你是对的,只是不要错过任何一个地方!幸运的是,如果使用得当,大多数框架都会为我们处理。
6赞 symcbean 2/19/2018 #14

您从不清理输入。

您始终对输出进行清理。

您应用于数据以使其可以安全地包含在 SQL 语句中的转换与您申请包含在 HTML 中的转换完全不同,与您申请包含在 Javascript 中的转换完全不同,与您申请包含在 LDIF 中的转换完全不同,与您应用于包含在 CSS 中的转换完全不同,与您应用于包含在电子邮件中的转换完全不同。

一定要验证输入 - 决定是否应该接受它进行进一步处理,或者告诉用户它是不可接受的。但是,在数据即将离开 PHP 领域之前,不要对数据的表示形式进行任何更改。

很久以前,有人试图发明一种一刀切的数据转义机制,我们最终得到了“magic_quotes”,它不能正确转义所有输出目标的数据,并导致不同的安装需要不同的代码才能工作。

评论

0赞 drtechno 10/2/2019
这样做的一个问题是,它并不总是数据库攻击,所有用户输入都应该受到系统保护。不只是一种语言类型。因此,在您的网站上,当您枚举 _POST 美元数据时,即使使用绑定,它也可能逃脱到足以执行 shell 甚至其他 php 代码。
0赞 symcbean 10/2/2019
“it not always a database attack” : “您应用于数据以使其可以安全地包含在 SQL 语句中的转换与那些完全不同......”
0赞 symcbean 10/2/2019
“所有用户输入都应受到系统保护”:否,系统应受到用户输入的保护。
0赞 drtechno 10/4/2019
好吧,我用完了话,但是是的,需要防止输入影响系统操作。为了澄清这一点......
1赞 Jsowa 5/6/2020
输入和输出都应进行清理。
18赞 Mark Martin 2/25/2018 #15

PHP 中安全数据库交互的方法

使用现代版本的 MySQL 和 PHP

1. 显式设置字符集:

MySQLi的

$mysqli->set_charset("utf8mb");

PDO的

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', $user, $password);

2. 使用准备好的语句

MySQLi 预准备语句:

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $param = "' OR 1=1 /*";<br>$stmt->bind_param('s', $param);
$stmt->execute();

PDO准备的声明

与 MySQLi 预准备语句相比,PDO 支持更多的数据库驱动程序和命名参数:

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);