I am very new to the whole PHP/MSSQL coding and need assistance with SQL Injection prevention.
I am adding a simple search feature to a website that uses a MSSQL database.
The code works as I want it to but it is vulnerable to SQL Injection.
Any other way to secure it except for prepare statements?
I am also not that familiar with stored procedures.
I have tried prepare statements with no luck(unless I'm doing something wrong, that's most likely)
Stored procedures I'm not familiar with.
<?PHP
$tech = (isset($_POST['Technician'])? $_POST['Technician'] : null);
$sql = "SELECT * FROM Errors WHERE Error LIKE '%$tech%' or Description LIKE '%$tech%'";
$name = trim($_POST['Technician']);
if(empty($name)){
print '<script type="text/javascript">alert("Please enter an Error Code or Error Description")</script>';
exit;
}
$stmt = sqlsrv_query($conn, $sql);
if ($stmt) {
$rows = sqlsrv_has_rows( $stmt );
if ($rows === true) {
echo "";
} else{
echo '<script type="text/javascript">alert("Please enter a valid Term")</script>';
}
}
while($db_field = sqlsrv_fetch_array($stmt)){
print '<table align="center" style="position: relative; width:250px; text-align: center;">';
print '<tr>';
print '<td>'.$db_field['Error']."</td></tr>";
print "<tr>";
print '<td>'.$db_field['Description'].'</td></tr>';
//print "<tr><th>"."Cause"."</th>";
//print "<td>".$db_field['Cause']."</td></tr>";
//print "<tr><th>"."Resolution"."</th>";
//print "<td>".$db_field['Resolution']."</td></tr>";
print "</table><br>";
}
sqlsrv_close($conn);
?>
I expect SQL Injection to fail when attempted.
Pretty simple to use a parameterized query, notice only ?:
$sql = "SELECT * FROM Errors WHERE Error LIKE ? OR Description LIKE ?";
Then build an array of parameters adding the LIKE wildcards %:
$params = array("%$tech%", "%$tech%");
Execute with parameters:
$stmt = sqlsrv_query( $conn, $sql, $params);
Alternatively, for flexibility with other queries, for the parameters you could do:
$tech = "%$tech%";
$params = array($tech, $tech);
Prepared statements are the simplest way to inject raw input into SQL. You can skip them altogether if you want but it comes at the price of greater complexity and lower security (and you need to write the alternative code yourself). The only reason why you can find escape() functions in other extensions (or no mechanism at all!) is because they're legacy libraries, sometimes very old ones. SQLSRV is reasonably modern.
Additionally, you may want to:
User filter functions to simply input checks.
Escape wildcard characters.
Resulting code would look like this:
$tech = filter_input(INPUT_POST, 'Technician');
if ($tech !== null) {
$sql = "SELECT *
FROM Errors
WHERE Error LIKE ? or Description LIKE ?";
$find = '%' . escapeLike($tech) . '%';
$params = [$find, $find];
$res = sqlsrv_query($conn, $sql, $params);
}
function escapeLike($value)
{
return strtr($value, [
'%' => '[%]',
'_' => '[_]',
'[' => '[[]',
]);
}
Related
I am some confused because some people write PHP code like this for upload data.
But my instructor used $query and $link always to upload and retrieve data from SQL.
<?php
include 'connection.php';
function reg_in() {
if (isset($_POST['submit'])) {
$name = $_POST['name'];
$email = $_POST['email'];
$mob = $_POST['mobile'];
$check_in = $_POST['in'];
$check_out = $_POST['out'];
$rm = $_POST['room'];
$room_type = $_POST['type'];
// Problem start from here
if (mysql_query("INSERT INTO `book` (`name`,`email`,`mobile_no`,`check_in`,`check_out`,`room`,`room_type`) VALUES ('$name','$email','$mob','$check_in','$check_out','$rm','$room_type')")) {
header('Location: C:/wamp/www/project/payment.html');
} else {
echo mysql_error();
}
}
}
if (isset($_POST['submit'])) {
reg_in();
//echo ' succesfully inserted';
} else {
echo 'Not book';
}
MySQL (by my instructor):-
<?php
$link = mysqli_connect("myserver.com", "test", "sunil7117", "test");
if (mysqli_connect_error()) {
die("please give correct permision");
}
//Is both are same!
//$query="INSERT INTO user(email,password) VALUES ('shivanandcpr25#gmail.com','sunil7117')";
$query = "UPDATE user SET email='test#gmail.com' WHERE email='abc#gmail.com' LIMIT 1";
echo mysqli_query($link, $query);
echo "<br>";
$query = "SELECT * FROM user";
if ($result = mysqli_query($link, $query)) {
echo "welcome to database<br>";
$row = mysqli_fetch_array($result);
echo "my E-mail id is <strong> ".$row[1]. "</strong> and passoword is <strong>".$row[2]."</strong>";
}
Neither!
Your first example uses function which has been removed from PHP years ago. mysql_query() does not exist and should not be used anymore. The reason why it was removed is that you should use prepared statements instead. They are provided by either mysqli or PDO extensions.
Your second example is better, but it is way too messy.
You should not echo mysqli_query. There's nothing useful to be printed out from this function.
Get into a habit of using prepared statements all the time and use placeholders for variable data. As of now your queries are constant, but using prepared statements is still a good practice in case you need to add a parameter later on.
Avoid using functions like mysqli_fetch_array(). Iterating the result option one by one is messy and rarely useful.
Never check the return value of mysqli calls. It's pointless. Enable error reporting instead. See How to get the error message in MySQLi?
Always set the correct charset. It should be utf8mb4 99% of the time.
The SQL query can be saved in a separate variable, but what's the point? You are only going to pass it as an argument to the query function. There's no need to use an intermediate variable.
Don't use mysqli. You should use PDO instead. If you have to use mysqli, then create a simple wrapper function or class for this purpose and execute your generic function instead of messing around with mysqli functions.
Here is an example of how I would do it. First I enable error reporting for mysqli, I open the connection and set the charset. Then I declare a function which takes 3 parameters and returns an array. First parameter is your database connection you have just opened. Second is your SQL query with placeholders if there are any. Third is optional and it is an array of values to be bound to the placeholders as parameters. This function works for all kind of SQL queries. The rest of the code becomes really simple.
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$link = new mysqli("myserver.com", "test", "sunil7117", "test");
$link->set_charset('utf8mb4'); // always set the charset
/**
* Executes an SQL query on the database.
*
* #param \mysqli $mysqli
* #param string $query e.g. SELECT * FROM users WHERE username=?
* #param array $params An array of parameters to be bound in the given order to the placeholders in SQL
* #return array
*/
function prepared_query(\mysqli $mysqli, string $query, array $params = []): array {
$stmt = $mysqli->prepare($query);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
prepared_query($link, "UPDATE user SET email='test#gmail.com' WHERE email='abc#gmail.com' LIMIT 1");
echo "<br>";
$result = prepared_query($link, "SELECT * FROM user");
echo "welcome to database<br>";
if ($result) {
$row = $result[0];
echo "my E-mail id is <strong> ".$row[1]. "</strong> and passoword is <strong>".$row[2]."</strong>";
}
I was wondering if it's possible to prepare statements using PHP's pgsql library that do not require parameters.
I'm really used to preparing all my SQL statements in the beginning of a program instead of when I need them so my code always looks similar to this
<?php
$pgsqlCon = // code to connect
// prepare SQL statements
$sql = "SELECT * FROM table01 " .
"WHERE t01_idno >= $1";
pg_prepare($pgsqlCon, "table01_cur", $sql);
$sql = "SELECT * FROM table02 " .
"WHERE t02_idno = $1";
pg_prepare($pgsqlCon, "table02_sel", $sql);
// start main
$table01 = pg_execute($pgsqlCon, "table01_cur", array('1'));
while ($row = pg_fetch_row($table01)) {
echo "<span>found something<br /></span>";
}
$table02 = pg_execute($pgsqlCon, "table02_sel", array('1'));
while ($row = pg_fetch_row($table02)) {
echo "<span>found something else<br /></span>";
}
?>
So I would like to prepare statements that don't require parameters in this way as well if that is possible.
I had the same problem and sadly this usage is not documented in the official documentation.
Looking at it you can see that the third argument is not optional (i.e. not in brackets [..]) and indeed, as the above comment says, passing an empty array() as the third argument works.
So after preparing a statement with no arguments one could execute it like so:
$rs = pg_execute($connection, $stmt, array());
I have a piece of code that reads a crash log and other metadata from a MySQL database before sending it back to the requester as JSON (this code is called by an AJAX function on another page). Optionally, filters can be specified with GET, which alter the prepared statement to filter for only the specified results - for example, to only show results from one app version. Here is the code in question:
$conn = new mysqli($sql_servername, $sql_username, $sql_password, "Crashes");
if ($conn->connect_error) {
//failed
die();
}
$sql="
SELECT ID,phone,appver,osver,user_agent,username,message,app_name,date_received FROM Crashes WHERE ";
$params="";
$params2="";
if (isset($_GET["filter_phone"])) {
$sql .= "phone = ? AND "; //i.e "WHERE phone = ? AND (...osver = ? AND appver = ? etc)"
$params.="s";
$params2.=', $_GET["filter_phone"]';
}
if (isset($_GET["filter_appver"])) {
$sql .= "appver = ? AND ";
$params.="s";
$params2.=', $_GET["filter_appver"]';
}
if (isset($_GET["filter_osver"])) {
$sql .= "osver = ? AND ";
$params.="s";
$params2.=', $_GET["filter_osver"]';
}
if (isset($_GET["filter_user_agent"])) {
$sql .= "user_agent = ? AND ";
$params.="s";
$params2.=', $_GET["filter_user_agent"]';
}
if (isset($_GET["filter_username"])) {
$sql .= "username = ? AND ";
$params.="s";
$params2.=', $_GET["filter_username"]';
}
if (isset($_GET["filter_message"])) {
$sql .= "message = ? AND ";
$params.="s";
$params2.=', $_GET["filter_message"]';
}
if (isset($_GET["filter_app_name"])) {
$sql .= "app_name = ? AND ";
$params.="s";
$params2.=', $_GET["filter_app_name"]';
}
$sql.="1";
//echo "\$params: ".$params."<br>";
//echo "\$params2: ".$params2."<br>";
//echo "<br>\$stmt->bind_param(\$params$params2);<br>";
//echo var_dump($_GET);
$stmt = $conn->prepare($sql);
exec("\$stmt->bind_param(\$params$params2);");
if ($stmt->execute()) {
//echo "<br>Success!";
} else {
//echo "<br>Failure: ".$stmt->error;
}
$result = $stmt->get_result();
$out = array();
while ($row = $result->fetch_assoc()) {
array_push($out, $row);
}
echo json_encode($out);
This code works perfectly when no filters are specified - however, when any filter is specified, the statement fails with the error No data supplied for parameters in prepared statement. I have verified that the code in the exec() should be (for example, if the user_agent filter is set) $stmt->bind_param($params, $_GET["filter_user_agent"]);
Additionally, the SQL query works perfectly when I use the query but just manually replace the ?s with the filters, so it does not appear to be a problem with the initial query, but rather the prepared statement. Help would be much appreciated.
NB: The commented out echos are for debug purposes, and are usually disabled, but I have left them in to show how I got the information that I currently have.
EDIT: Answered my own question below - the error was that I was using exec() - a function which executes external shell commands, whereas what I wanted was eval(), which evaluates a string input and the executes it as a PHP script.
Turns out I was simply mistaken as to the function of exec(). (Too much Python?) exec() runs an external shell command, whereas what I was looking for was eval() which evaluates and runs any string inputted as PHP code.
Looks like you never actually use the values $params and $params2. You concatenate them, but where is your $stmt->bind_param()? It's commented out?
I don't see a $conn->prepare("") either. Did you omit some code?
$conn is defined as a mysqli object and then again never used. Something's not right here.
Edit:
Try exec("\$stmt->bind_param(\$params\$params2);"); I assume you somehow execute the code and escape the variables - following that logic you should escape both params and params2, perhaps?
I have a successful connection made in PDO to my MySQL database and I am currently trying to get it to query the database for itmes LIKE the search query.
<?php
include ('connection.php');
function doSearch() {
$output = '';
if(isset($_POST['search'])) {
$searchq = $_POST['search'];
$searchq = preg_replace ("#[^0-9a-z]#i","",$searchq);
$sql = "SELECT * FROM entries WHERE name LIKE :searchq or description LIKE :searchq or content LIKE :searchq";
global $conn;
$stmt = $conn->prepare($sql);
$stmt->bindParam(":searchq",$searchq,PDO::PARAM_STR);
$stmt->execute();
$count = $stmt->rowCount();
if($count == 0) {
$output = 'No results found, buddy.';
} else {
while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$eName = $row['name'];
$eDesc = $row['description'];
$eCont = $row['content'];
$id = $row['id'];
$elvl = $row['level'];
$ehp = $row['hp'];
$output .= '<tr><td>'.$eName.'</td><td>'.$eDesc.'</td><td>'.$elvl.'</td><td>'.$ehp.'</td></tr>';
}
}
return $output;
}
}
?>
I am struggling to get it to search. Unless the query exactly matches only the name, it doesn't show any results.
The LIKE operator does not do partial matches unless specifically instructed to. Perhaps you meant to prepend/append a % wildcard symbol to the search string:
$searchq = '%' . $searchq . '%';
I don't see anything wrong in the SQL text of the query.
The simplest explanation is that the SQL you are expecting to be sent to the database isn't what is being sent.
I suggest you give this a try:
use unique bind variable names in the statement, use each bind variable only once.
There used to be a bug in PDO with named bind variables (not sure if that's fixed of not. Under the covers, the named bind parameters were getting converted to positional notation, and when the same bind variable two or more times, the query being sent to MySQL wasn't what we expected.
For example:
$sql = "SELECT e.*
FROM entries e
WHERE e.name LIKE :searchq1
OR e.description LIKE :searchq2
OR e.content LIKE :searchq3";
$stmt = $conn->prepare($sql);
$stmt->bindParam(":searchq1",$searchq,PDO::PARAM_STR);
$stmt->bindParam(":searchq2",$searchq,PDO::PARAM_STR);
$stmt->bindParam(":searchq3",$searchq,PDO::PARAM_STR);
When we encountered the problem, we weren't using server side prepared statements, just regular client side prepares; the SQL sent to the server included literals, not placeholders. Turning on the general_log in MySQL allowed us to see the actual SQL statements that were being sent to the database.
You might be encountering the same problem. But I'd recommend this as a step in debugging the problem, at least to verify it isn't the problem.
I have this code
http://www.nomorepasting.com/getpaste.php?pasteid=22580
which is part of a small ajax application. I would like to know a better, more efficient way to assign $query, instead of copying the sql each time with a different query or a bunch of if clauses. Basically the query will be dependant on the link clicked, but I am not sure how to show that in the logic. I am also unsure why my SQL query in $result fails.
UPDATE: I integrated Eran's function into the refactored code. NOTE: I corrected it by passing the $table variable into it and renamed it since it doesn't search the query text only but mainly returns the needed rows!
MAIN MISTAKES:
mistake 1: you overwrite query with query2 in all cases which breaks the code.
mistake 2: LIKE'%$query%' there is a space missing between LIKE and ' => LIKE '%... this most probably breaks your code too
OTHER ISSUES
security problem: sql injection danger, use mysql_real_escape_string
\n not platform independent: use PHP_EOL
alternative way of writing short if blocks
use curly brackets for normal if structures and all such structures for the matter
here is your code with some changes, look at the comments:
<?php
session_start(); //ommit, no session var used
//use braces, always!
//you may write such statements with the short form like
if (isset($_GET['cmd'])) : $cmd = $_GET['cmd']; else : die (_MSG_NO_PARAM); endif;
$query = '';
//escpae your input - very important for security! sql injection!
if ( isset ($_GET["query"]))
{
$query = mysql_real_escape_string($_GET["query"]);
}
//no need for the other part you had here
$con = mysql_connect("localhost", "root", "geheim");
if (!$con) : die ('Connection failed. Error: '.mysql_error()); endif;
mysql_select_db("ebay", $con);
if ($cmd == "GetRecordSet")
{
$table = 'Auctions';
$rows = getRowsByArticleSearch($searchString, $table);
//use PHP_EOL instead of \n in order to make your script more portable
echo "<h1>Table: {$table}</h1>".PHP_EOL;
echo "<table border='1' width='100%'><tr>".PHP_EOL;
echo "<td width='33%'>Seller ID</td>".PHP_EOL;
echo "<td width='33%'>Start Date</td>".PHP_EOL;
echo "<td width='33%'>Description</td>".PHP_EOL;
echo "</tr>\n";
// printing table rows
foreach ($rows as $row)
{
$pk = $row['ARTICLE_NO'];
echo '<tr>'.PHP_EOL;
echo '<td>'.$row['USERNAME'].'</td>'.PHP_EOL;
echo '<td>'.$row['ACCESSSTARTS'].'</td>'.PHP_EOL;
echo '<td>'.$row['ARTICLE_NAME'].'</td>'.PHP_EOL;
echo '</tr>'.PHP_EOL;
}
}
mysql_free_result($result);
//mysql_close($con); no need to close connection, you better don't
function getRowsByArticleSearch($searchString, $table)
{
$searchString = mysql_real_escape_string($searchString);
$result = mysql_query("SELECT ARTICLE_NO, USERNAME, ACCESSSTARTS, ARTICLE_NAME FROM {$table} WHERE upper ARTICLE_NAME LIKE '%" . $searchString . "%'");
if($result === false) {
return mysql_error();
}
$rows = array();
while($row = mysql_fetch_assoc($result)) {
$rows[] = $row;
}
return $rows;
}
// ?> ommit closing php tag
"SELECT ARTICLE_NO, USERNAME, ACCESSSTARTS, ARTICLE_NAME
FROM {$table} WHERE upper ARTICLE_NAME LIKE'%$query%'"
You need to put brackets around the parameters of your upper function. change your query to this, and it should work:
"SELECT ARTICLE_NO, USERNAME, ACCESSSTARTS, ARTICLE_NAME
FROM {$table} WHERE upper(ARTICLE_NAME) LIKE'%$query%'"
for a feature use:
$result = mysql_query($sql_query) or die(mysql_error());
To see what kind of mysql error you get.
you should do like nickf said.
and you are definitely prone to SQL-Injection:
wikibooks: http://en.wikibooks.org/wiki/Programming:PHP:SQL_Injection
long article: http://www.securiteam.com/securityreviews/5DP0N1P76E.html
You can abstract your query in a function that accepts the search text as a parameter. Something like:
function searchQuery($text) {
$text = mysql_real_escape_string($text);
$result = mysql_query("SELECT ARTICLE_NO, USERNAME, ACCESSSTARTS, ARTICLE_NAME FROM {$table} WHERE upper ARTICLE_NAME LIKE '%" . $text . "%'");
if($result === false) {
return mysql_error();
}
$rows = array();
while($row = mysql_fetch_assoc($result)) {
$rows[] = $row;
}
return $rows;
}
Note that you should escape user input to prevent SQL injection attacks (here I used mysql_real_escape_string() to do that). This function also returns the error code if the query fails, so you should check the result to see if it's an array or not:
$result = searchQuery($_GET['query']);
if(!is_array($result) ) {
echo 'An error has occurred:' . $result;
} else {
//iterate over rows
}
Wrap your logical structures (IF/ELSE) with curly brackets {. It's better for readability and helps avoid unnecessary mistakes.
You haven't enclosed the statements in your IF/THEN/ELSE constructions in accolades so only the first statement in every block is conditionally executed, the rest allways is.
In most cases you'd be assigning $query2 to $query while $query2 probably hasn't been defined.
As another tip: sanitize your input, don't go pasting user input into your SQL like that, it's dangerous.
You may need a space between LIKE and '%$query%'. Also, you should look into the mysql_error() function - let MySQL tell you exactly what the error is.