{"id":12907,"date":"2016-06-07T16:15:58","date_gmt":"2016-06-07T13:15:58","guid":{"rendered":"http:\/\/www.webcodegeeks.com\/?p=12907"},"modified":"2018-01-09T10:52:24","modified_gmt":"2018-01-09T08:52:24","slug":"php-sql-injection-tutorial-protect","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/","title":{"rendered":"PHP SQL Injection Tutorial: How To Protect"},"content":{"rendered":"<p class=\"brush:bash\">SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application. In other words: an attacker could read, write and\/or delete data that is supposed to have access to.<\/p>\n<p>This is really scaring because, in the worst case, which is quite common, is incredibly easy to inject SQL instructions. <strong>Not without reason is in the first place of top 10 vulnerabilities by OWASP<\/strong> (Open Web Application Security Project).<\/p>\n<p>The purpose of this tutorial is to learn how we can protect against these kind of dangerous attacks.<\/p>\n<p>For this tutorial, we will use:<\/p>\n<ul>\n<li>Ubuntu (14.04) as Operating System.<\/li>\n<li>Apache HTTP server (2.4.7).<\/li>\n<li>PHP (5.5.9).<\/li>\n<li>PostgreSQL (9.3).<\/li>\n<\/ul>\n<p>[ulp id=&#8217;8njY7i2QRy6sg8pg&#8217;]<\/p>\n<div class=\"toc\">\n<h3>Table Of Contents<\/h3>\n<dl>\n<dt><a href=\"#section_1\">1. Preparing the environment<\/a><\/dt>\n<dd><a href=\"#section_1_1\">1.1. Installation<\/a><\/dd>\n<dd><a href=\"#section_1_2\">1.2. PHP configuration<\/a><\/dd>\n<dd><a href=\"#section_1_3\">1.3. PostgreSQL configuration<\/a><\/dd>\n<dt><a href=\"#section_2\">2. How SQL Injection works<\/a><\/dt>\n<dt><a href=\"#section_3\">3. Base application and table structure<\/a><\/dt>\n<dd>\n<dl>\n<dt><a href=\"#section_3_1\">3.1. Table structure<\/a><\/dt>\n<dt><a href=\"#section_3_2\">3.2. PHP application: login application<\/a><\/dt>\n<\/dl>\n<\/dd>\n<dt><a href=\"#section_4\">4. First protection rule: don&#8217;t display any error message<\/a><\/dt>\n<dd><a href=\"#section_4_1\">4.1. Disabling error reporting<\/a><\/dd>\n<dt><a href=\"#section_5\">5. Second protection rule: restrict permissions as much as possible<\/a><\/dt>\n<dt><a href=\"#section_6\">6. Third protection rule: use Prepared Statements<\/a><\/dt>\n<dd><a href=\"#section_6_1\">6.1. Taking advantage of Prepared Statements&#8217; performance<\/a><\/dd>\n<dd><a href=\"#section_6_2\">6.2. How NOT to use Prepared Statements<\/a><\/dd>\n<dt><a href=\"#section_7\">7. Additional security measures<\/a><\/dt>\n<dd><a href=\"#section_7_1\">7.1. Sanitizing manually the input<\/a><\/dd>\n<dd><a href=\"#section_7_2\">7.2. Black\/white listing<\/a><\/dd>\n<dd><a href=\"#section_7_3\">7.3. Log suspicious activity<\/a><\/dd>\n<dt><a href=\"#section_8\">8. Summary<\/a><\/dt>\n<dt><a href=\"#section_9\">9. Download the source code<\/a><\/dt>\n<\/dl>\n<\/div>\n<div class=\"tip\"><strong>Tip<\/strong><br \/>\nYou may skip environment preparation and jump directly to the <a href=\"#section_2\"><strong>beginning of the tutorial<\/strong><\/a> below.<\/div>\n<h2 id=\"section_1\">1. Preparing the environment<\/h2>\n<h3 id=\"section_1_1\">1.1. Installation<\/h3>\n<p>Below, commands to install Apache, PHP and PostgreSQL are shown:<\/p>\n<pre class=\"brush:bash\">sudo apt-get update\r\nsudo apt-get install apache2 php5 postgresql libapach2-mod-php5 php5-pgsql\r\nsudo service apache2 restart<\/pre>\n<h3 id=\"section_1_2\">1.2. PHP Configuration<\/h3>\n<p>We have to configure PHP to add the PostgreSQL driver. Open <code>\/etc\/php5\/apache2\/php.ini<\/code>, and add the following directive, if it not exists:<\/p>\n<pre class=\"brush:bash\">extension=pgsql.so<\/pre>\n<p>Don&#8217;t forget to restart Apache after doing any change.<\/p>\n<h3 id=\"section_1_3\">1.3. PostgreSQL configuration<\/h3>\n<p>We are going to set a password for the default PostgreSQL superuser, because it does not have configured any by default. Type the following in a terminal:<\/p>\n<pre class=\"brush:bash\">sudo -u postgres psql<\/pre>\n<p>To connect to PostgreSQL command line with\u00a0<code>postgres<\/code> user.<\/p>\n<p>Now we can change its password with the following command:<\/p>\n<pre class=\"brush:bash\">ALTER ROLE postgres WITH ENCRYPTED PASSWORD 'postgres';<\/pre>\n<p>And the password will be updated. Type\u00a0<code>\\q<\/code> to quit.<\/p>\n<h2 id=\"section_2\">2. How SQL Injection works<\/h2>\n<p>Let&#8217;s suppose that an application has a form with an input that, when the form is submitted, it will be part of a SQL sentence, for example, to make a search in a table.<\/p>\n<p>The script that controls this, would do something similar to the following:<\/p>\n<pre class=\"brush:php\">$search = $_GET['input'];\r\n\r\n$sql = \"SELECT * FROM Bar WHERE Foo = '$search'\"<\/pre>\n<p>So, if we submit the form with, for example, <code>baz<\/code> value in the field, the constructed SQL would be:<\/p>\n<pre class=\"brush:bash\">SELECT * FROM Bar WHERE Foo = 'baz'<\/pre>\n<p>Everything right for the moment.<\/p>\n<p>But, what would happen if the user introduces the SQL <code>'<\/code> delimiter character in the field? The resulting SQL instruction would be the following:<\/p>\n<pre class=\"brush:bash\">SELECT * FROM Bar WHERE Foo = '''<\/pre>\n<p>Which will produce an error since it&#8217;s not a correct SQL sentence.<\/p>\n<p>At this moment, the user realizes that <strong>the predefined interaction between the application and the database can be altered by the user<\/strong>. And, that if the behaviour can be subverted to generate SQL errors, it can also be done, but without generating them. So, it might try to introduce the following value in the form: <code>' or '1'='1<\/code>. What would this generate?<\/p>\n<pre class=\"brush:bash\">SELECT * FROM Bar WHERE Foo = '' or '1'='1'<\/pre>\n<p>Which is a correct SQL sentence. What has done the user (who should be considered an attacker already)? <strong>It has modified the application logic to make it behave as it wants it, not as the application itself is allowing<\/strong>.<\/p>\n<p>In the previous example we have seen how an attacker can read data, but it can do almost anything allowed by SQL language. The following sentence could also be generated:<\/p>\n<pre class=\"brush:bash\">SELECT * FROM Bar WHERE Foo = ''; DROP TABLE Admin<\/pre>\n<p>And any other evil actions that an attacker could think.<\/p>\n<p>Scaring, isn&#8217;t it?<\/p>\n<h2 id=\"section_3\">3. Base application and table structure<\/h2>\n<p>First of all, let&#8217;s create a small application. Mostly, the first barrier an attacker encounters is a login, so, that&#8217;s with we will work.<\/p>\n<h3 id=\"section_3_1\">3.1 Table structure<\/h3>\n<p>We will create a new database, with a table for users and their passwords, and we&#8217;ll create some of them. For that, we will introduce the following sentences in the Postgres command line:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>install.sql<\/em><\/span><\/p>\n<pre class=\"brush:bash\">CREATE DATABASE sql_injection;\r\n\r\n\\c sql_injection;\r\n\r\nCREATE TABLE Users (\r\n\u00a0\u00a0\u00a0 username varchar(50) PRIMARY KEY NOT NULL,\r\n\u00a0\u00a0\u00a0 password varchar(50) NOT NULL\r\n);\r\n\r\nINSERT INTO Users VALUES('admin', 'adminpassword');\r\nINSERT INTO Users VALUES('worker', 'workerpassword');\r\nINSERT INTO Users VALUES('WebCodeGeeks', 'W38C0D3G33K5');<\/pre>\n<p>Then, we will have a Users table with <em>admin<\/em>, <em>worker<\/em> and <em>WebCodeGeeks<\/em> user names, with their respective passwords.<\/p>\n<p><strong>Note<\/strong>: please, don&#8217;t store passwords in plain text.<\/p>\n<h3 id=\"section_3_2\">3.2. PHP application: login application<\/h3>\n<p>Let&#8217;s create a separate class for dealing with the database:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php;wrap-lines:false;highlight:[60,61,62,63,64]\">&lt;?php\r\n\r\n\/**\r\n\u00a0* Database operations class.\r\n\u00a0*\/\r\nclass DB {\r\n\r\n\u00a0\u00a0\u00a0 const DB_NAME\u00a0 = 'sql_injection';\r\n\u00a0\u00a0\u00a0 const PORT\u00a0\u00a0\u00a0\u00a0 = 5432;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ PostgreSQL default port.\r\n\u00a0\u00a0\u00a0 const DB_HOST\u00a0 = 'localhost';\r\n\u00a0\u00a0\u00a0 const USERNAME = 'postgres';\r\n\u00a0\u00a0\u00a0 const PASSWORD = 'postgres';\r\n\r\n\u00a0\u00a0\u00a0 \/**\r\n\u00a0\u00a0\u00a0\u00a0 * Database connection object.\r\n\u00a0\u00a0\u00a0\u00a0 *\/\r\n\u00a0\u00a0\u00a0 private $connection;\r\n\r\n\u00a0\u00a0\u00a0 \/**\r\n\u00a0\u00a0\u00a0\u00a0 * Connects to a database with the defined class constants, constructing a connection string like\r\n\u00a0\u00a0\u00a0\u00a0 * \"host='db_host' port='port' dbname='dbname' user='user' password='password'\".\r\n\u00a0\u00a0\u00a0\u00a0 *\r\n\u00a0\u00a0\u00a0\u00a0 * @return True if the connection was made, false if it was not.\r\n\u00a0\u00a0\u00a0\u00a0 *\/\r\n\u00a0\u00a0\u00a0 public function connect() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $connectionString = 'host=' . self::DB_HOST . ' port=' . self::PORT . ' dbname=' . self::DB_NAME\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 . ' user=' . self::USERNAME . ' password=' . self::PASSWORD;\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $connection = pg_connect($connectionString);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ($connection) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $connected = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;connection = $connection;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $connected = false;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return $connected;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 \/**\r\n\u00a0\u00a0\u00a0\u00a0 * Closes the opened connection to the database.\r\n\u00a0\u00a0\u00a0\u00a0 *\r\n\u00a0\u00a0\u00a0\u00a0 * @return True if the connection was closed succesfully; false if an error happened.\r\n\u00a0\u00a0\u00a0\u00a0 *\/\r\n\u00a0\u00a0\u00a0 public function disconnect() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $disconnected = pg_close($this-&gt;connection);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return $disconnected;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 \/**\r\n\u00a0\u00a0\u00a0\u00a0 * Authenticates an user with its password, looking if exists a row for the given credentials.\r\n\u00a0\u00a0\u00a0\u00a0 *\r\n\u00a0\u00a0\u00a0\u00a0 * @param $username The user to authenticate.\r\n\u00a0\u00a0\u00a0\u00a0 * @param $password The password to authenticate the user.\r\n\u00a0\u00a0\u00a0\u00a0 * @return True if the user was authenticated, false if not.\r\n\u00a0\u00a0\u00a0\u00a0 *\/\r\n\u00a0\u00a0\u00a0 public function authenticateUser($username, $password) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $sql = \"SELECT username\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM\u00a0\u00a0 Users\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 WHERE\u00a0 username = '$username'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND password = '$password'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \";\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = pg_query($this-&gt;connection, $sql);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $exists = pg_num_rows($result);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return $exists;\r\n\u00a0\u00a0\u00a0 }\r\n}<\/pre>\n<p>Now, let&#8217;s create a simple HTML form to login in our application:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>login.html<\/em><\/span><\/p>\n<pre class=\"brush:html\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n\u00a0\u00a0\u00a0 &lt;meta charset=\"UTF-8\"&gt;\r\n\u00a0\u00a0\u00a0 &lt;title&gt;Login form&lt;\/title&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n\u00a0\u00a0\u00a0 &lt;form action=\"home.php\" method=\"POST\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;div&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;label for=\"username\"&gt;Username:&lt;\/label&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input type=\"text\" name=\"username\" id=\"username\" required&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/div&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;div&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;label for=\"password\"&gt;Password:&lt;\/label&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input type=\"password\" name=\"password\" id=\"password\" required&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/div&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;div&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;input type=\"submit\" value=\"Login\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/div&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/form&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>And, finally, the main application page: a page that greets the authenticated user; or that says that the authentication could not be made:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>home.php<\/em><\/span><\/p>\n<pre class=\"brush:php;highlight:[27]\">&lt;?php\r\n\r\nrequire_once('DB.php');\r\n\r\n\/**\r\n\u00a0* Checks if the given parameters are set. If one of the specified parameters is not set,\r\n\u00a0* die() is called.\r\n\u00a0*\r\n\u00a0* @param $parameters The parameters to check.\r\n\u00a0*\/\r\nfunction checkPOSTParametersOrDie($parameters) {\r\n\u00a0\u00a0\u00a0 foreach ($parameters as $parameter) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 isset($_POST[$parameter]) || die(\"Please, provide a '$parameter' POST parameter.\");\r\n\u00a0\u00a0\u00a0 }\r\n}\r\n\r\n\/\/ Flow starts here.\r\n\r\ncheckPOSTParametersOrDie(['username', 'password']);\r\n\r\n$db = new DB();\r\n$db-&gt;connect() || die('An error occured while connecting to the database.');\r\n\r\n$username = $_POST['username'];\r\n$password = $_POST['password'];\r\n\r\n$authenticated = $db-&gt;authenticateUser($username, $password);\r\n\r\nif ($authenticated) {\r\n\u00a0\u00a0\u00a0 $response = \"Hello &lt;b&gt;$username&lt;\/b&gt;, you have been succesfully authenticated.\";\r\n} else {\r\n\u00a0\u00a0\u00a0 $response = 'Sorry, you could not be authenticated.';\r\n}\r\n\r\necho $response;\r\n\r\n$db-&gt;disconnect();<\/pre>\n<p>So simple, we just retrieve the username and password received from the form, and we call the authentication function of database class.<\/p>\n<h2 id=\"section_4\">4. First protection rule: don&#8217;t display any error message<\/h2>\n<p>This is more crucial than what it can be thought. We should disable every error displaying in production environments. <strong>The first thing an attacker will try is to generate errors in the application that can generate error messages with valuable information<\/strong>. And, actually, valuable information here, means one thing: <strong>database engine<\/strong> (and its version).<\/p>\n<p>How could we generate an error in our application? For example, submitting the form with the following values:<\/p>\n<pre class=\"brush:bash\">username: '\r\npassword: whatever<\/pre>\n<p>With those values, the home.php would show a screen like the following:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/06\/4.-error-screen.jpg\" \/><\/p>\n<p>What has the attacker achieved with this? Well, <strong>that the application is vulnerable to SQL injection<\/strong>. And, also, <strong>the knowledge about our DBMS<\/strong>, which is PostgreSQL, easily deducible from the use of <code>pg_query()<\/code> or <code>pg_fetch()_rows()<\/code> functions.<\/p>\n<p>How can an attacker exploit this? The possibilities are almost endless, but let&#8217;s suppose that he wants to know the database user that is handling the connection. He would just have to fill the form like:<\/p>\n<pre class=\"brush:bash\">username: whatever\r\npassword: whatever' union select current_user where '1'='1<\/pre>\n<p>And the output would be&#8230;<\/p>\n<blockquote><p>Hello <b>postgres<\/b>, you have been successfully authenticated.<\/p><\/blockquote>\n<p>The attacker knows that he&#8217;s facing a PostgreSQL database, so he knows that he can use the\u00a0<code>current_user<\/code> function, <strong>only looking at the documentation<\/strong>. He also knows that the application is using postgres user to connect to the database, the superuser by default, so he will probably have permissions to perform any kind of operation into the database, <strong>from login into the application as the admin user, to changing database superuser&#8217;s password<\/strong>, for instance.<\/p>\n<h3 id=\"section_4_1\">4.1. Disabling error reporting<\/h3>\n<p>To hide that exploitable information, we need to disable the errors. In PHP, is easy to do that. We just have to edit the\u00a0<code>\/etc\/php5\/apache2\/php.ini<\/code> file, and disable the\u00a0<code>display_errors <\/code>directive:<\/p>\n<pre class=\"brush:bash\">display_errors = Off<\/pre>\n<p>And that&#8217;s it, after restarting Apache, any error will be displayed.<\/p>\n<p><strong>Note:<\/strong> in this tutorial, we will keep displaying the errors, for debugging purposes.<\/p>\n<h2 id=\"section_5\">5. Second protection rule: restrict permissions as much as possible<\/h2>\n<p>In the previous example we have seen that the attacker can easily deduce with which permissions is dealing to the database. Disabling the error displaying, a must-do in production environments, disallows the possibility for the attacker to acquire knowledge of that information, but it remains exploitable in that sense.<\/p>\n<p>When we are developing an application that uses a database, we must take the following into account: <strong>the superuser is only for database administration and maintenance works, not for being used by applications<\/strong>.<\/p>\n<p>So, we are going to create a user that will own the database of our application, and that will be the one used to connect to it. For PostgreSQL,\u00a0 syntax would be:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>install.sql<\/em><\/span><\/p>\n<pre class=\"brush:bash\">CREATE ROLE sql_injection_user WITH ENCRYPTED PASSWORD 'sql_injection';\r\nALTER ROLE sql_injection WITH LOGIN; -- By default, roles cannot log in.\r\n\r\n-- ...<\/pre>\n<p>Now, we can declare the ownership of sql_injection database to the just created user. We could do in database creation:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>install.sql<\/em><\/span><\/p>\n<pre class=\"brush:bash\">-- ...\r\n\r\nCREATE DATABASE sql_injection WITH OWNER sql_injection_user;\r\n\r\n-- ...<\/pre>\n<p>Or, change it for a created database:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>install.sql<\/em><\/span><\/p>\n<pre class=\"brush:bash\">-- ...\r\n\r\nALTER DATABASE sql_injection OWNER TO sql_injection;\r\n\r\n-- ...<\/pre>\n<p>Now, we have to specify the permissions of the user. For this application, only read permissions are used, so we will only allow to make selects:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>install.sql<\/em><\/span><\/p>\n<pre class=\"brush:bash\">-- ...\r\n\r\nGRANT SELECT ON Users TO sql_injection_user;\r\n\r\n-- ...<\/pre>\n<p>The last step is to change the application to use this user:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php\">\/\/ ...\r\n\r\nconst USERNAME = 'sql_injection_user';\r\nconst PASSWORD = 'sql_injection';\r\n\r\n\/\/ ...<\/pre>\n<p>If we now try, for example, to delete all the records from that table, with the following values for the form:<\/p>\n<pre class=\"brush:bash\">username: whatever\r\npassword: whatever'; delete from users where '1'='1<\/pre>\n<p>We will get an error about permission denial (because we have the error display activated only for debugging purposes!):<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/06\/5.-error-screen.jpg\" \/><\/p>\n<p>But we can do the login, since we have\u00a0<code>SELECT<\/code> permissions.<\/p>\n<h2 id=\"section_6\">6. Third protection rule: use Prepared Statements<\/h2>\n<p>Our application is still vulnerable to SQL injection. In a more controlled way, but it&#8217;s still vulnerable. Now, we will see how really protect against it.<\/p>\n<p><strong>The SQL injection is generated from input generated by user. So, that is where we have to focus the defence<\/strong>.<\/p>\n<p>Using prepared statements with variable binding is the way every SQL sentence should be executed against a database. <strong>This is a must-use in every interaction with the database<\/strong>. The special feature of prepared statements with parameterized values is that the values are not bound directly, but they are passed with a different protocol, and it&#8217;s handled by the database engine. In other words, the database is which escapes the parameters&#8217; values, instead of doing it manually.<\/p>\n<p>Note that a <strong>prepared statement itself does not protect against SQL Injections if they are not used with parameterized values<\/strong>.<\/p>\n<p>Let&#8217;s see how they are used:<\/p>\n<pre class=\"brush:php;highlight:[4,5,8,9]\">public function authenticateUser($username, $password) {\r\n\u00a0\u00a0\u00a0 $sql = \"SELECT username\r\n\u00a0\u00a0\u00a0         FROM\u00a0\u00a0 Users\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  WHERE\u00a0 username = $1\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND password = $2\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \";\r\n\r\n\u00a0\u00a0\u00a0 $statement = pg_prepare($this-&gt;connection, 'authenticate', $sql);\r\n\u00a0\u00a0\u00a0 $result = pg_execute($this-&gt;connection, 'authenticate', [$username, $password]);\r\n\u00a0\u00a0\u00a0 $row = pg_fetch_row($result);\r\n\r\n    \/\/ ...\r\n}<\/pre>\n<p>This is how it works:<\/p>\n<ul>\n<li>We first define which will be the parameters of the query (lines 4, 5). That is, <strong>we parameterize the query<\/strong>. Note that we are not even wrapping the parameters between single quotes, even if they are string values.<\/li>\n<li>We submit the request for creating the prepared statement, in line 8. Note that there&#8217;s a second parameter, where a literal value is being passed, <code>'authenticate'<\/code>. This is the name that the statement will received, necessary for the database to later identify the statement. At last, we pass the SQL sentence we want to create the prepared statement for.<\/li>\n<li>Then, we execute the requested prepared statement, in line 9. We specify the same statement name we specified when creating, and we pass an array with the values, in the same order as we defined in the parameterized query. Here, the database engine identifies the previous requested statement, and it binds the parameters, internally. So, <strong>PHP has no knowledge of the final SQL query.<\/strong><\/li>\n<\/ul>\n<h3 id=\"section_6_1\">6.1. Taking advantage of Prepared Statements&#8217; performance<\/h3>\n<p>Using prepared statements, apart from protecting us from SQL Injection, can also improve the performance if they are used properly. So, we will see how we can use them to improve the performance.<\/p>\n<p>First, we will create a function only for creating the prepared statement:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php\">\/**\r\n * Creates the prepared statement for user authentication.\r\n *\/\r\nprotected function createAuthenticationStatement() {\r\n\u00a0\u00a0\u00a0 $sql = \"SELECT username\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM\u00a0\u00a0 Users\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 WHERE\u00a0 username = $1\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND password = $2\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \";\r\n\r\n\u00a0\u00a0\u00a0 $this-&gt;authenticationStatement = pg_prepare($this-&gt;connection, 'authenticate', $sql);\r\n}<\/pre>\n<p>Then, we have to <strong>call it only once<\/strong>, and it has to be after the connection has been established:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php;wrap-lines:false;highlight:[10]\">public function connect() {\r\n\u00a0\u00a0\u00a0 $connectionString = 'host=' . self::DB_HOST . ' port=' . self::PORT . ' dbname=' . self::DB_NAME\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 . ' user=' . self::USERNAME . ' password=' . self::PASSWORD;\r\n\r\n\u00a0\u00a0\u00a0 $connection = pg_connect($connectionString);\r\n\r\n\u00a0\u00a0\u00a0 if ($connection) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $connected = true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;connection = $connection;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;createAuthenticationStatement();\r\n\u00a0\u00a0\u00a0 } else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $connected = false;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $connected;\r\n}<\/pre>\n<p>And, finally, we have just to execute the statement for the given parameters:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php\">public function authenticateUser($username, $password) {\r\n\u00a0\u00a0\u00a0 $result = pg_execute($this-&gt;connection, 'authenticate', [$username, $password]);\r\n\u00a0\u00a0\u00a0 $row = pg_fetch_row($result);\r\n\r\n    \/\/ ...\r\n}<\/pre>\n<p>This is because the query is already constructed in the database, with\u00a0<code>pg_prepare()<\/code>, and when\u00a0<code>pg_execute()<\/code> is called, the database only binds the parameters, which is faster than creating the whole query again.<\/p>\n<p>For an operation like this, the performance optimization will probably be insignificant. But, for large and complex queries, it can be more significant. So, if we are using prepared statements to make our application safe, the performance optimization they can suppose is something to take into account.<\/p>\n<p><strong>Using prepared statements with parameterized values is the most important part the defence against SQL Injection<\/strong>. In fact, the previous measures can be considered as useless if prepared statements are not being used.<\/p>\n<h3 id=\"section_6_2\">6.2. How NOT to use Prepared Statements<\/h3>\n<p>We have mentioned that, to make the prepared statements effective, they have to be parameterized, with those <code>$<\/code> marks.<\/p>\n<p>Is important to understand that the following won&#8217;t protect us against injections:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php;highlight:[8,9]\">&lt;?php\r\n\r\n\/\/ ...\r\n\r\npublic function authenticateUser($username, $password) {\r\n\u00a0\u00a0\u00a0 $sql = \"SELECT username\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM\u00a0\u00a0 Users\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 WHERE\u00a0 username = '$username'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND password = '$password'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \";\r\n\r\n\u00a0\u00a0\u00a0 $statement = pg_prepare($this-&gt;connection, 'authenticate', $sql);\r\n\u00a0\u00a0\u00a0 $result = pg_execute($this-&gt;connection, 'authenticate', []);\r\n\u00a0\u00a0\u00a0 $row = pg_fetch_row($result);\r\n\r\n\u00a0\u00a0\u00a0 \/\/ ...\r\n}<\/pre>\n<p><strong>Setting the values in the query that will be used for the statement request will make it exploitable<\/strong>.<\/p>\n<h2 id=\"section_7\">7. Additional security measures<\/h2>\n<p>The prepared statements, used properly, will protect us <em>pretty<\/em> securely. But we have the possibility to add more security measures. In this section, we will see alternative techniques that may also result interesting.<\/p>\n<h3 id=\"section_7_1\">7.1. Sanitizing manually the input<\/h3>\n<p><strong>This option is for when we are not using prepared statements (they are not compatible)<\/strong>, for any reason. For example, for legacy code, for which adding support for prepared statements could suppose much work.<\/p>\n<p>The concept is the same as with prepared statements, to make the received input a literal string value, that is not breaking the sql sentence.<\/p>\n<p>For that, PHP provides a built-in function named pg_escape_literal(). Let&#8217;s see how to use it:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php;highlight:[2,3,7,8]\">public function authenticateUser($username, $password) {\r\n\u00a0\u00a0\u00a0 $username = pg_escape_literal($username);\r\n\u00a0\u00a0\u00a0 $password = pg_escape_literal($password);\r\n\r\n\u00a0\u00a0\u00a0 $sql = \"SELECT username\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FROM\u00a0\u00a0 Users\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 WHERE\u00a0 username = {$username}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AND password = {$password}\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \";\r\n\r\n\u00a0\u00a0\u00a0 $result = pg_query($this-&gt;connection, $sql);\r\n\u00a0\u00a0\u00a0 $row = pg_fetch_row($result);\r\n\r\n    \/\/ ...\r\n}<\/pre>\n<p>Note that in lines 7 and 8, the values are surrounded with curly braces, instead of with single quotes. Now, every input will be fully taken as a literal string value.<\/p>\n<h3 id=\"section_7_2\">7.2. Black\/white listing<\/h3>\n<p>We could also create a register where allowed or disallowed keywords are defined.<\/p>\n<p>Let&#8217;s create a simple black list to look for inputs that will probably have bad intentions:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php\">class DB {\r\n\r\n\u00a0\u00a0\u00a0 \/**\r\n\u00a0\u00a0\u00a0\u00a0 * Forbidden input tokens.\r\n\u00a0\u00a0\u00a0\u00a0 *\/\r\n\u00a0\u00a0\u00a0 private $blacklist = \"\/(\\s*union\\s*|where\\s*'1')\/\";\r\n\r\n    \/\/ ...\r\n}<\/pre>\n<p>This pattern will look for substrings defined inside the round braces, and separated by a pipe character. The <code>\\s*<\/code> is not to take into account the whitespaces.<\/p>\n<p>The function to check if given values contains any of those substrings is pretty simple:<\/p>\n<p><em><span style=\"text-decoration: underline;\">DB.php<\/span><\/em><\/p>\n<pre class=\"brush:php;highlight:[11]\">\/\/ ...\r\n\r\n\/**\r\n * Checks provided inputs to look for patterns defined in class property's black list array.\r\n *\r\n * @param $userInputs Inputs to check.\r\n * @return True if a positive is found, false if not.\r\n *\/\r\nprotected function blacklistPositive($userInputs) {\r\n\u00a0\u00a0\u00a0 foreach ($userInputs as $userInput) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $positive = preg_match($this-&gt;blacklist, $userInput);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ($positive) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return true;\r\n    \u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return false;\r\n}\r\n\r\n\/\/ ...<\/pre>\n<p>And, finally, we would have to call it every time before doing any query:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php;highlight:[4]\">\/\/ ...\r\n\r\npublic function authenticateUser($username, $password) {\r\n\u00a0\u00a0\u00a0 if (!$this-&gt;blacklistPositive([$username, $password])) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $result = pg_execute($this-&gt;connection, 'authenticate', [$username, $password]);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $row = pg_fetch_row($result);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (!$row) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $authenticated = false;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $authenticated = $row[0];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 } else {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $authenticated = false;\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return $authenticated;\r\n}\r\n\r\n\/\/ ...<\/pre>\n<p>The obvious disadvantage of this is that, in this case, it could haven&#8217;t a username or password that contains the\u00a0<code>union<\/code> substring.<\/p>\n<p>In the reverse way, we could also have a whitelist where the allowed inputs are defined. This makes more sense where the input is a literal value of an element of the database that is part of a query, e.g., table or column names.<\/p>\n<h3 id=\"section_7_3\">7.3. Log suspicious activity<\/h3>\n<p>Another possibility that is compatible with prepared statements is to register suspicious actions. This can be considered an extension of black\/white listing, since we only would have to create a record when the above defined\u00a0<code>blacklistPositive()<\/code> function returns\u00a0<code>true<\/code>.<\/p>\n<p>This would be useful if we want to be vigilant or ban users that are triggering our alarm with a frequency above a threshold defining deliberated attacks.<\/p>\n<p>We could simply save that suspicious activity with a function like the following:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php\">\/\/ ...\r\n\r\n\/**\r\n\u00a0* Creates a record when blaclistPositive() function returns true.\r\n\u00a0* Datetime, remote IP and the input that triggered the action are saved.\r\n\u00a0*\/\r\nprotected function logBlacklistPositive($trigger) {\r\n\u00a0\u00a0\u00a0 $datetime = date('d-m-Y h:i:s');\r\n\u00a0\u00a0\u00a0 $ip = $_SERVER['REMOTE_ADDR'];\r\n\r\n\u00a0\u00a0\u00a0 file_put_contents(self::SUSPICIOUS_LOGFILE, \"$datetime; $ip; $trigger\\n\", FILE_APPEND);\r\n}\r\n\r\n\/\/ ..<\/pre>\n<p>Of course, calling it when a positive is found:<\/p>\n<p><span style=\"text-decoration: underline;\"><em>DB.php<\/em><\/span><\/p>\n<pre class=\"brush:php;highlight:[8]\">\/\/ ...\r\n\r\nprotected function blacklistPositive($userInputs) {\r\n\u00a0\u00a0\u00a0 foreach ($userInputs as $userInput) {\r\n    \u00a0\u00a0\u00a0 $positive = preg_match($this-&gt;blacklist, $userInput);\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ($positive) {\r\n\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $this-&gt;logBlacklistPositive($userInput);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return true;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0\u00a0 return false;\r\n}\r\n\r\n\/\/ ...<\/pre>\n<p>Note that we are using a text file instead of a database to store that info. <strong>If we would store the attack attempts logs as same as the data we want to protect from those attacks, the log data could be easily subverted by the attacker not to leave any trace<\/strong>.<\/p>\n<h2 id=\"section_8\">8. Summary<\/h2>\n<p>This tutorial has shown how we should protect against extremely dangerous SQL injection attacks, starting from the most generic defence lines, those that actually should be implemented not only to protect against SQL injections, but from every kind of attack; and finishing with the most specific and effective one, the prepared statements. We have also seen alternatives ways of defence, which some of them can be combined with prepared statements.<\/p>\n<h2 id=\"section_9\">9. Download the source code<\/h2>\n<p>This was a tutorial of how to protect against SQL injections.<\/p>\n<div class=\"download\"><strong>Download<\/strong><br \/>\nYou can download the full source code of this example here: <a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2016\/06\/PHPSQLInjectionProtectionTutorial.zip\"><strong>PHPSQLInjectionProtectionTutorial<\/strong><\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application. In other words: an attacker could read, write and\/or delete data that is supposed to have access to. This is really scaring because, in the worst case, which is quite &hellip;<\/p>\n","protected":false},"author":160,"featured_media":930,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[329],"class_list":["post-12907","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-php","tag-sql-injection"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>PHP SQL Injection Tutorial: How To Protect - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PHP SQL Injection Tutorial: How To Protect - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2016-06-07T13:15:58+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-01-09T08:52:24+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Toni\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Toni\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\"},\"author\":{\"name\":\"Toni\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/54a7be647b0b871cff41cbf3d2a95966\"},\"headline\":\"PHP SQL Injection Tutorial: How To Protect\",\"datePublished\":\"2016-06-07T13:15:58+00:00\",\"dateModified\":\"2018-01-09T08:52:24+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\"},\"wordCount\":2444,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg\",\"keywords\":[\"SQL Injection\"],\"articleSection\":[\"PHP\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\",\"name\":\"PHP SQL Injection Tutorial: How To Protect - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg\",\"datePublished\":\"2016-06-07T13:15:58+00:00\",\"dateModified\":\"2018-01-09T08:52:24+00:00\",\"description\":\"SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PHP\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/php\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"PHP SQL Injection Tutorial: How To Protect\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/54a7be647b0b871cff41cbf3d2a95966\",\"name\":\"Toni\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/21c1661474c78b4757355b8beef9ab1d14f490ee3a3e67392f4e618d36643d4c?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/21c1661474c78b4757355b8beef9ab1d14f490ee3a3e67392f4e618d36643d4c?s=96&d=mm&r=g\",\"caption\":\"Toni\"},\"url\":\"https:\/\/www.webcodegeeks.com\/author\/julen-pardo\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"PHP SQL Injection Tutorial: How To Protect - Web Code Geeks - 2026","description":"SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/","og_locale":"en_US","og_type":"article","og_title":"PHP SQL Injection Tutorial: How To Protect - Web Code Geeks - 2026","og_description":"SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application.","og_url":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2016-06-07T13:15:58+00:00","article_modified_time":"2018-01-09T08:52:24+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg","type":"image\/jpeg"}],"author":"Toni","twitter_card":"summary_large_image","twitter_creator":"@webcodegeeks","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Toni","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/"},"author":{"name":"Toni","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/54a7be647b0b871cff41cbf3d2a95966"},"headline":"PHP SQL Injection Tutorial: How To Protect","datePublished":"2016-06-07T13:15:58+00:00","dateModified":"2018-01-09T08:52:24+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/"},"wordCount":2444,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg","keywords":["SQL Injection"],"articleSection":["PHP"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/","url":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/","name":"PHP SQL Injection Tutorial: How To Protect - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg","datePublished":"2016-06-07T13:15:58+00:00","dateModified":"2018-01-09T08:52:24+00:00","description":"SQL Injection is a vulnerability that allows an attacker to interact with the victim database to perform actions that are not allowed by the application.","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/php-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/php\/php-sql-injection-tutorial-protect\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"PHP","item":"https:\/\/www.webcodegeeks.com\/category\/php\/"},{"@type":"ListItem","position":3,"name":"PHP SQL Injection Tutorial: How To Protect"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/54a7be647b0b871cff41cbf3d2a95966","name":"Toni","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/21c1661474c78b4757355b8beef9ab1d14f490ee3a3e67392f4e618d36643d4c?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/21c1661474c78b4757355b8beef9ab1d14f490ee3a3e67392f4e618d36643d4c?s=96&d=mm&r=g","caption":"Toni"},"url":"https:\/\/www.webcodegeeks.com\/author\/julen-pardo\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/12907","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/160"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=12907"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/12907\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/930"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=12907"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=12907"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=12907"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}