How to insert htmlentities into mysql? - php

I want to protect my website from XSS attacks. For that I am using htmlentites. When I am trying to insert my variable into MySQL I am getting an error?
$var = htmlentities("<script>alert('hello')</script>");
$conn = mysqli_connect("localhost","root","","xss");
//mysqli_query($conn,"INSERT INTO entities (ent) VALUES('$var')");
if (!mysqli_query($conn,"INSERT INTO entities (ent) VALUES('$var')"))
{
echo("Error description: " . mysqli_error($conn));
}
echo $var;

The correct answer is you you are not supposed to do this. Don't store the result of htmlentities() in the database. This function is meant to be used only when you output in HTML context! You can't know for sure if the data stored in the database will always be used in HTML context.
XSS-prevention is very context-dependent. What if you would like to output to JavaScript or CSV or simply search the values in the database? You can't do it if they are encoded for HTML output.
To answer your more pressing issue I need to mention that your code is vulnerable to SQL injection. Use prepared statements with parameter binding.
The correct mysqli example of INSERT query would be as follows:
<?php
$var = "<script>alert('hello')</script>";
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$conn = new mysqli("localhost", "root", "", "xss");
$conn->set_charset('utf8mb4');
$stmt = $conn->prepare('INSERT INTO entities (ent) VALUES(?)');
$stmt->bind_param('s', $var);
$stmt->execute();
// use htmlentities when in HTML context
echo '<div>'.htmlentities($var).'</div>';

You can use the mysqli_real_escape_string() function to sanitize the data before inserting into the database
$var = htmlentities("<script>alert('hello')</script>");
$conn = mysqli_connect("localhost","root","","xss");
// add this line to sanitize the string before inserting into the database
$var = mysqli_real_escape_string($conn, $var);
if (!mysqli_query($conn,"INSERT INTO entities (ent) VALUES('$var')"))
{
echo("Error description: " . mysqli_error($conn));
}
echo $var;
Hope this help

Related

Php not updating table info but query works when executed alone, so do post method and mysqli_connect

<?php
$host_name = '***';
$database = '***';
$user_name = '***';
$password = '***';
$link = mysqli_connect($host_name, $user_name, $password, $database);
$con = $_POST['User_ID'];
echo "Se ha ascendido al usuario $con";
$meta= 'a:1:{s:13:"administrator";b:1;}';
$consulta = 'UPDATE ***usermeta
SET
meta_value = $meta
WHERE
User_ID=$con and meta_key = "***capabilities"';
mysqli_query($link, $consulta);
echo "<br><br><br><a href='***'>Volver</a>";
In this code im trying to update an specific column from a table but it just wont work, it appears like it is working but when i go into phpmyadmin the data wont update, here is some info to keep in mind:
mysqli_connect works
query works when i execute it on phpmyadmin
i can do other queries (select) that works
data is correctly received by POST method
those " from variable $meta have to stay
I honestly dont have any idea of what is causing the code to just not work, not a single syntax error displayed or anything else. At first i thought it had something to do with the quote marks but now i dismissed that posibility.
Any help?
There's a catalog of issues here.
Your update statement is wrapped in single quotes - so your variables will not be substituted.
You've used double quotes as a delimiters for strings inside the query - that's not supported by SQL - they should be single quotes.
Table names cannot cannot contain asterisk characters.
That you are not seeing "a single syntax error" is a major issue - the DBMS will be screaming for help when it sees this.
Embedding composite data (json) in a scalar value is just asking for trouble.
Your code is vulnerable to SQL injection.
Whenever your thread of execution leaves PHP (in your code, when you call mysqli_conect() and mysqli_query()) you should be explicitly checking the result of the operation.
For one, you should have some kind of error handling so you know what the problem is. Secondly, you're calling mysqli_query directly instead of using it as a method from your already instantiated class $link.
Also, you really should be using back-ticks for column names and single quotes for column values.
Lastly, you need to escape certain special characters using mysqli_real_escape_string. Alternatively, you could use prepared statements, but I'll keep it simple. Instead of prepared statements, you can use PHP's sprintf function.
<?php
$host_name = '***';
$database = '***';
$user_name = '***';
$password = '***';
$link = mysqli_connect($host_name, $user_name, $password, $database);
$con = $_POST['User_ID'];
echo "Se ha ascendido al usuario $con";
$meta= 'a:1:{s:13:"administrator";b:1;}';
$consulta = "UPDATE `usermeta`
SET
`meta_value` = '%s'
WHERE
`User_ID`='%s' and `meta_key` = 'capabilities'";
$consulta = sprintf(
$consulta,
esc($meta),
esc($con)
);
$link->query($consulta);
echo "<br><br><br><a href='***'>Volver</a>";
function esc($v)
{
global $link;
return $link->real_escape_string($v);
}
?>
Not sure what the asterisks are in the table name, but they shouldn't be there. Also, note that I created a function for handling escaping for brevity.
EDIT:
For error handling, you should check $link->error.
Example:
<?php
$dbError = $link->error ?? null;
if (!empty($dbError))
{
die("A database error occurred: {$dbError}!");
}
?>

PHP code no longer works when switching to mysqli

I'm trying to convert some php code that uses mysql into mysqli code. I'm not sure why it doesn't work - I didn't write the original code and am not that comfortable with the hash part of it, and it seems to be where the issue is. As I show in the code below, the "error" part gets echo'ed so it's something to do with the hash strings, but I don't really understand why changing to mysqli has broken the code. Both versions of the code are below, and the original code works. I deleted the variables (host name, etc.) but otherwise this is the code I am working with.
Mysql Code:
// Send variables for the MySQL database class.
function db_connect($db_name)
{
$host_name = "";
$user_name = "";
$password = "";
$db_link = mysql_connect($host_name, $user_name, $password) //attempt to connect to the database
or die("Could not connect to $host_name" . mysql_connect_error());
mysql_select_db($db_name) //attempt to select the database
or die("Could not select database $db_name");
return $db_link;
}
$db_link = db_connect(""); //connect to the database using db_connect function
// Strings must be escaped to prevent SQL injection attack.
$name = mysql_real_escape_string($_GET['name'], $db_link);
$score = mysql_real_escape_string($_GET['score'], $db_link);
$hash = $_GET['hash'];
$secretKey=""; # Change this value to match the value stored in the client javascript below
$real_hash = md5($name . $score . $secretKey);
if($real_hash == $hash) {
// Send variables for the MySQL database class.
$query = "insert into scores values (NULL, '$name', '$score');";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
}
Mysqli code (doesn't work):
// Send variables for the MySQL database class.
function db_connect($db_name)
{
$host_name = "";
$user_name = "";
$password = "";
$db_link = mysqli_connect($host_name, $user_name, $password) //attempt to connect to the database
or die("Could not connect to $host_name" . mysqli_connect_error());
mysqli_select_db($db_link, $db_name) //attempt to select the database
or die("Could not select database $db_name");
return $db_link;
}
$db_link = db_connect(""); //connect to the database using db_connect function
// Strings must be escaped to prevent SQL injection attack.
$name = mysqli_real_escape_string($_GET['name'], $db_link);
$score = mysqli_real_escape_string($_GET['score'], $db_link);
$hash = $_GET['hash'];
$secretKey=""; # Change this value to match the value stored in the client javascript below
$real_hash = md5($name . $score . $secretKey);
if($real_hash == $hash) {
// Send variables for the MySQL database class.
$query = "INSERT INTO `scores` VALUES (NULL, '$name', '$score');";
$result = mysqli_query($db_link, $query) or die('Query failed: ' . mysqli_error($db_link));
echo $result;
}
else {
echo "error"; //added for testing. This part gets echoed.
}
mysqli_close($db_link); //close the database connection
One notable "gotchu" is that the argument order is not the same between mysql_real_escape_string and mysqli_real_escape_string, so you need to swap those arguments in your conversion.
$name = mysqli_real_escape_string($db_link, $_GET['name']);
$score = mysqli_real_escape_string($db_link, $_GET['score']);
It's good that you're taking the time to convert, though do convert fully to the object-oriented interface if mysqli is what you want to use:
// Send variables for the MySQL database class.
function db_connect($db_name)
{
$host_name = "";
$user_name = "";
$password = "";
// Enable exceptions
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db = new mysqli($host_name, $user_name, $password);
$db->select_db($db_name);
return $db;
}
$db = db_connect(""); //connect to the database using db_connect function
$secretKey=""; # Change this value to match the value stored in the client javascript below
$real_hash = md5($name . $score . $secretKey);
if($real_hash == $_GET['hash']) {
// Don't include ; inside queries run through PHP, that's only
// necessary when using interactive MySQL shells.
// Specify the columns you're inserting into, don't leave them ambiguous
// ALWAYS use prepared statements with placeholder values
$stmt = $db->prepare("INSERT INTO `scores` (name, score) VALUES (?, ?)");
$stmt->bind_param("ss", $_GET['name'], $_GET['score']);
$result = $stmt->execute();
echo $result;
}
else {
echo "error"; //added for testing. This part gets echoed.
}
// Should use a connection pool here
$db->close();
The key here is to use prepared statements with placeholder values and to always specify which columns you're actually inserting into. You don't want a minor schema change to completely break your code.
The first step to solving a complex problem is to eliminate all of the mess from the solution so the mistakes become more obvious.
The last if statement is controlling whether the mysql query gets run or not. Since you say this script is echoing "error" form the else portion of that statement, it looks like the hashes don't match.
The $hash variable is getting passed in on the URL string in $_GET['hash']. I suggest echo'ing $_GET['hash'] and $real_hash (after its computed by the call to MD5) and verify that they're not identical strings.
My hunch is that the $secretKey value doesn't match the key that's being used to generate the hash that's passed in in $_GET['hash']. As the comment there hints at, the $secretKey value has to match the value that's used in the Javascript, or the hashes won't match.
Also, you may find that there's a difference in Javascript's md5 implementation compared to PHP's. They may be encoding the same input but are returning slightly different hashes.
Edit: It could also be a character encoding difference between Javascript and PHP, so the input strings are seen as different (thus generating different hashes). See: identical md5 for JS and PHP and Generate the same MD5 using javascript and PHP.
You're also using the values of $name and $score after they've been escaped though mysqli_real_string_escape, so I'd suggest making sure Javascript portion is handling that escaping as well (so the input strings match) and that the msqli escape function is still behaving identically to the previous version. I'd suggest echo'ing the values of $name and $score and make sure they match what the Javascript side is using too. If you're running the newer code on a different server, you may need to set the character set to match the old server. See the "default character set" warning at http://php.net/manual/en/mysqli.real-escape-string.php.

ACRA Insert report into my server's database

I successfully configured ACRA to send my caught exceptions to my server, the problem is I can't insert the report into the database:
#AcraCore(buildConfigClass = BuildConfig.class)
#AcraHttpSender(uri = "http://www.myserver.com/crashreports/addreport.php",
httpMethod = HttpSender.Method.POST)
public class MyApplication extends Application {
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
ACRA.init(this);
}
}
I know it sends somethings because I see an empty entry in my phpMyAdmin, but I can't get the report inside the database:
<?php
$link = mysqli_connect("localhost", "root", "pass", "db");
// Check connection
if($link === false){
die("ERROR: Could not connect. " . mysqli_connect_error());
}
// Escape user inputs for security
$report = mysqli_real_escape_string($link, $_REQUEST['']);
// attempt insert query execution
$sql = "INSERT INTO VoiceRemoteCrash (report) VALUES ('$report')";
if(mysqli_query($link, $sql)){
echo "Records added successfully.";
} else{
echo "ERROR: Could not able to execute $sql. " . mysqli_error($link);
}
// close connection
mysqli_close($link);
?>
I've searched docs, but didn't find much info, and my PHP knowledge is somewhat basic.
$_REQUEST[''] will returns NULL and will throw an "undefined index" notice.
You could get your report from POST raw data using file_get_contents('php://input').
I suggest you to have a look to : How can I prevent SQL injection in PHP? and use parameterized queries.
This line references a nonsense variable:
$report = mysqli_real_escape_string($link, $_REQUEST['']);
You want something like:
$report = mysqli_real_escape_string($link, $_REQUEST['form_variable_name']);
But you shouldn't even do that, because the real_escape_string() functions can not be relied on to prevent SQL injection. Instead, you should use prepared statements with bound parameters, via either the mysqli or PDO driver. This post has some good examples.

Mysqli_real_escape_string() or addslashes() in a forum context

I'm making a forum in php and MySql, so I need to insert and select data from my data base. I'm using mysqli to connect to my db. Something like this:
$link=mysqli_connect("fake_server", "fake_user", "fake_pass", "fake_db");
$user=mysqli_real_escape_string($link, $_POST['user']);
$pass=hash("sha256", mysqli_real_escape_string($link, $_POST['pass']));
$combo=mysqli_fetch_array(mysqli_query($link, "SELECT 1 FROM users WHERE user='$user' AND pwd='$pass'"));
if($combo==0){
// ERROR
} else {
// CORRECT
}
mysqli_close($link);
The problem is the next one:
Everybody say that mysqli_real_escape_string() is MUCH better than addslashes() for insert, but I want users can use single and double quotes in their topics. Myqsli_real_escape_string() removes them but addslashes() doesn't. What can I do in this context?
You should use prepared statements, http://php.net/manual/en/mysqli.quickstart.prepared-statements.php. In the future please provide your code in your question. Here's how you can use your current code with prepared statements:
$link=mysqli_connect("fake_server", "fake_user", "fake_pass", "fake_db");
$user=$_POST['user'];
$pass=hash("sha256", $_POST['pass']);
$stmt = $link->prepare("SELECT 1 FROM users WHERE user = ? AND pwd = ?");
$stmt->bind_param("ss", $user, $pass);
$combo=mysqli_fetch_array($stmt->execute());
if($combo==0){
// ERROR
} else {
// CORRECT
}
mysqli_close($link);
Further reading on the topic:
How can I prevent SQL injection in PHP?mysqli or PDO - what are the pros and cons?
https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet#Defense_Option_1:_Prepared_Statements_.28Parameterized_Queries.29http://php.net/manual/en/mysqlinfo.api.choosing.php
Use a parameterized query with PDO and forget worrying about escaping your queries.
Edit
Your test code:
$link=mysqli_connect("fake_server", "fake_user", "fake_pass", "fake_db");
$user=mysqli_real_escape_string($link, $_POST['user']);
$pass=hash("sha256", mysqli_real_escape_string($link, $_POST['pass']));
$combo=mysqli_fetch_array(mysqli_query($link, "SELECT 1 FROM users WHERE user='$user' AND pwd='$pass'"));
if($combo==0){
// ERROR
} else {
// CORRECT
}
mysqli_close($link);
The PDO version:
$pdo = new PDO('mysql:host=fake_server;dbname=fake_db', 'fake_user', 'fake_pass');
$query = $pdo->prepare("SELECT 1 FROM users WHERE user='?' AND pwd='?'");
$query->execute(array($_POST('user'), hash('sha256', $_POST('pass')));
if ($combo = $query->fetch ()) {
// CORRECT
// $combo would contain an array containing your select fields
} else {
// ERROR
}

mysql_real_escape_string deletes variable contents

OK guys, I'm having trouble with mysql_real_escape_string. It is a simple POST table with title and contents, which should in theory work fine (by me).
$db = new mysqli('...','...','...','...') or die ('error with connection');
$db->set_charset('utf8');
$title = trim($_POST['title']);
$contents = trim($_POST['contents']);
$title = mysql_real_escape_string($title);
$contents = mysql_real_escape_string($contents);
$sql = "INSERT INTO posts SET title = '$title', contents = '$contents'";
$query = $db->query($sql);
I found when I place 'echo' before and after 'mysql_escape_string' like:
echo 'before' . $title;
$title = mysql_real_escape_string($title);
echo 'after' . $title;
that it echoes the title on the "before" line, but on the "after" line it echoes blank $title.
Note: whan I use 'mysql_escape_string' instead (of real), it works fine (but I guess this is weaker protection).
Any ideas??
Thank you.
The reason title is empty is because mysql_real_escape_string is returning FALSE.
This happened because it requires a MySQL connection to the database, you have MySQLi. From the docs,
A MySQL connection is required before using mysql_real_escape_string() otherwise an error of level E_WARNING is generated, and FALSE is returned
The way to fix the issue is to use mysqli_real_escape_string to match your database connection, as suggested in the other answers. Obviously for security, you're better off using prepared statements.
Also, the database link needs to be supplied. Since you're using the OO style, this is done as
$db = new mysqli()
$title = $db->real_escape_string($title)
mysql_real_escape_string() works in context of connection made by "mysql" extension, but you use "mysqli".
You need to either connect using mysql_connect() not recommended
Or you need to use new approaches from mysqli such as prepared statements or mysqli_real_escape_string()
You should not interpolate user-generated strings into sql. Use prepared statements: http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
In your case:
$db = new mysqli('...','...','...','...') or die ('error with connection');
$db->set_charset('utf8');
$title = trim($_POST['title']);
$contents = trim($_POST['contents']);
$sql = $db->prepare("INSERT INTO posts (title, contents) VALUES (?,?)");
$sql->bind_param('ss', $title, $contents);
$sql->execute();

Categories