PHP $_POST username sanitation - how that this is not ok - php

I've googled and searched SO and everything, but I can't come to an answer.
Why is the PHP code below letting plus sign through:
$aValid = array('-', '_', '.');
if (ctype_alnum(str_replace($aValid, '', $_POST['Rusername']))){
(If user types 1234+ it goes on like it vas valid????).
I, normally, found a bypass, but is it really necessary;
$aValid = array('-', '_', '.');
if (ctype_alnum(str_replace($aValid, '', (htmlentities ( trim ( $_POST['Rusername'] ) , ENT_NOQUOTES ))))){
echo "+ ok";
}else{
echo "+ not ok";
}
I would like to use this kind of validation and just add special chars to $aValid when needed.
Before inserting to MySQL I would do mysql_real_escape_string or prepared statements.
Any ideas why the + sign is ok for first piece of code. In my opinion it really should't be.
Latest update - I needed few hours of low-level debugging HTTP headers to figure iz out that my A side changed the Content-Type so that the + sign became space before it got to PHP if clause (alowed in my whitelist)...
What an - I hope it will never happen to you - experience :)
Reminder to myself: jQuery should be mentioned if it was such a major player in this validation process...

What you should do is inspect each stage of the process, rather than slamming them all into a single line:
$name = $_POST['Rusername'];
var_dump($name);
$name = str_replace($aValid, '', $name);
var_dump($name);
etc... Basic debugging. If you've got multiple stages in a statement, then check each individual stage. ctype may be working perfectly fine, but the str_replace is failing you somehow.

Any ideas why the + sign is ok for first piece of code. In my opinion it really should't be.
It's not ok... the first piece of code return false..
If you want to remove the + sign you have to add it into the $aValid variable

Related

PHP sql treat special characters

$sql = "INSERT INTO golf (datum, odkud, odjezd ) VALUES ('$datum', '$odkud', '$odjezd')";
if(!mysqli_query($connection,$sql)) {
echo '<p class="error">Záznam nebyl vložen</p>';
} else {
header ("location :/golf");
}
Hello, I am working on my thesis to school. I have this code and my supervisor keeps telling me to "treat special characters". How do I do that? He only saw the code I showed you.
your supervisor just ask you to treat special characters 😊. For that #Pedro’s answer is enough.
$odjezd = mysqli_real_escape_string($connection, $odjezd) // copied from #Pedro’s answer
But if you need more validation,
you can check the format of the date is correct?
$test_arr = explode('/', $POST[‘date’]);
if (count($test_arr) == 3) {
if (checkdate($test_arr[0], $test_arr[1], $test_arr[2])) {
// valid date ...
} else {
// problem with dates ...
}
} else {
// problem with input ...
}
Likewise you can validate your data according to your required way.
this might be helpful.
User input MUST be sanitized prior to insertion, you'll be opening a door into your server otherwise.
You also need to validate the input. What I normally do, is create a series of regex to validate each field individually, or use one of the available php validate filters.
Remember, you can - and should - do client side validation, which is great to reduce server load, but has 0 value as a security measure because it can be easily faked.
server side validation is the most important as it's your last line of defense.
Don't take user input lightly, tons of servers get hacked due to bad or nonexistent user input sanitization.
To directly answer your question, mysqli_real_escape_string() is your friend to escape special characters, i.e.:
$odjezd = mysqli_real_escape_string($connection, $odjezd)
Characters encoded are NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.
Update:
I have used mysqli_real_escape_string and i am still able to submit
"a{b}c'=%" I would like it to remove spec.characters and just input
abc...how?
Let's assume that $odkud can only contain letters or digits and be 5 chars long only to validate, we can use preg_match() as validator, i.e.:
$id = $_REQUEST['id'];
if (preg_match('/^[a-z\d]{5}$/i', $odkud)) {
# Successful match
} else {
# Match attempt failed
}
Live Regex Example & Explanation
If you just need to remove the special characters use one of the php filters mentioned above or preg_replace, i.e.:
$odkud_filtered = preg_replace('/[^a-z\d]/i', '', $odkud);
# abc
Live Regex Example & Explanation

PHP variables look the same but are not equal (I'm confused)

OK, so I shave my head, but if I had hair I wouldn't need a razor because I'd have torn it all out tonight. It's gone 3am and what looked like a simple solution at 00:30 has become far from it.
Please see the code extract below..
$psusername = substr($list[$count],16);
if ($psusername == $psu_value){
$answer = "YES";
}
else {
$answer = "NO";
}
$psusername holds the value "normann" which is taken from a URL in a text based file (url.db)
$psu_value also holds the value "normann" which is retrieved from a cookie set on the user's computer (or a parameter in the browser address bar - URL).
However, and I'm sure you can guess my problem, the variable $answer contains "NO" from the test above.
All the PHP I know I've picked up from Google searches and you guys here, so I'm no expert, which is perhaps evident.
Maybe this is a schoolboy error, but I cannot figure out what I'm doing wrong. My assumption is that the data types differ. Ultimately, I want to compare the two variables and have a TRUE result when they contain the same information (i.e normann = normann).
So if you very clever fellows can point out why two variables echo what appears to be the same information but are in fact different, it'd be a very useful lesson for me and make my users very happy.
Do they echo the same thing when you do:
echo gettype($psusername) . '\n' . gettype($psu_value);
Since i can't see what data is stored in the array $list (and the index $count), I cannot suggest a full solution to yuor problem.
But i can suggest you to insert this code right before the if statement:
var_dump($psusername);
var_dump($psu_value);
and see why the two variables are not identical.
The var_dump function will output the content stored in the variable and the type (string, integer, array ec..), so you will figure out why the if statement is returning false
Since it looks like you have non-printable characters in your string, you can strip them out before the comparison. This will remove whatever is not printable in your character set:
$psusername = preg_replace("/[[:^print:]]/", "", $psusername);
0D 0A is a new line. The first is the carriage return (CR) character and the second is the new line (NL) character. They are also known as \r and \n.
You can just trim it off using trim().
$psusername = trim($psusername);
Or if it only occurs at the end of the string then rtrim() would do the job:
$psusername = rtrim($psusername);
If you are getting the values from the file using file() then you can pass FILE_IGNORE_NEW_LINES as the second argument, and that will remove the new line:
$contents = file('url.db', FILE_IGNORE_NEW_LINES);
I just want to thank all who responded. I realised after viewing my logfile the outputs in HEX format that it was the carriage return values causing the variables to mismatch and a I mentioned was able to resolve (trim) with the following code..
$psusername = preg_replace("/[^[:alnum:]]/u", '', $psusername);
I also know that the system within which the profiles and usernames are created allow both upper and lower case values to match, so I took the precaution of building that functionality into my code as an added measure of completeness.
And I'm happy to say, the code functions perfectly now.
Once again, thanks for your responses and suggestions.

Issue with encoding on receiver end when using xmlrpc_encode_request

I have an issue with charsets and how they are encoded in a request I send. I have a test case where I want the code to end up with the exact same md5-hash on both sides. While still being the character 'å', obviously. (So not converted into some broken char or just '?')
The source input is utf8 and contains a norwegian character, for example "båt".
This input will then be sent to an API that wants data to be latin1 / ISO-8859-1.
One goal is also to avoid having to add utf8_decode to the receiving end.
So this is the very simplified code of what I've sent until now:
$password_send = 'båt';
echo "Test 1: " . md5( $password ) . "\n";
$params = array('password' => $password);
$request = xmlrpc_encode_request($module, $params);
And this is how the receiving end treats it. It basically just converts it into an md5 hash and sends it to another method. No other conversion of the incoming data has been done.
$_hash = md5( $password_receive );
echo "Test 2: {$_hash}\n";
Member::updatePassword($member_id, $_hash);
I need the $_hash to be (when 'båt' is sent) to end up as the hash 7e2cdd98fccee62723784a815a2ecdcb. Since this is the md5-hash that 'båt' resolves into when the password 'båt' is saved on the site itself (and not trough the API)
So when I send 'båt' in the API-request, then on the receiver end, it ends up with: fd9cac747daca144726dc579c32f48a, which is wrong. When I check the md5() of 'båt' before I send it, then it is also displayed as fd9cac747daca144726dc579c32f48ae.
I guess this is expected, since I don't use utf8_decode yet, but if I change what I send, like so: $password_send = utf8_decode('båt');
Then it still doesn't end up with the correct hash on the receiver end, then it ends up with: b865deb1e3b0891a41c5444c00893a0f
However, if I also add utf8_decode on the receiver end, like so: $_hash = utf8_decode($password_receive), then it ends up with the hash I need it to be: 7e2cdd98fccee62723784a815a2ecdcb
But this seems very wrong... Having to do utf8_decode on both sides. And while this hash is now correct on the receiving end too, the issue is that I don't want to change any code on the receiving end. And it doesn't work to just do utf8_decode twice before I send the value, because then I just end up with the hash c2d1fbc45e123f65edd74401ef58dd6a on the receiving end (which is the equivalent of doing md5('b?t'). It only worked when I do utf8_decode once before I send it, and once on the receiver end.
So I started to realize that xmlrpc_encode_request probably is the culprit, in that it maybe did some conversion on it's own. First I checked what a var_dump of $request said, in the cases where the $password_send value has NOT been utf8_decoded. And that is:
<string>båt</string>
When I do utf8_decode on the value $password_send before it's made into an xmlrpc request, then it is:
<string>båt</string>
Then I read the documentation on xmlrpc_encode_request. And I've tried various combinations of output_options, but none of them seems to work. In every scenario I still have to do utf8_decode in the code on the input data on the receiver end to end up with the exact same md5 that I need.
I realize this might be somewhat confusing. I would really really appreciate it if someone is able to help me out here. By giving me some pointer on what I should do or try. Because I've gotten completely lost on this issue now :(
The problem seems to be the escaping of xmlrpc_encode_request function. I have same problem albeit with czech "ě" character. I believe it might be bug in PHP, I however found a simple workaround.
Just turn of escaping of non-print and non-ascii characters.
echo xmlrpc_encode_request('test', 'å'); //Ã¥ - incorrect
echo xmlrpc_encode_request('test', 'å', ['escaping' => 'markup']); //å - correct

php RegEx working on my local server - not working on remote host running php4

I am pretty sure I know what the issue is, but I cannot resolve it for the life of me and I just don't know what to do at this point.
All I am trying to do is qualify the input of a first and last name field. This code IS working on my local server running php5:
<?php
$regex = "John's Jerod";
if (!preg_match("/^[A-Za-z\'\,\.\s]+$/", $regex)) {
echo "ERROR!";
} else {
echo "NO ERROR!";
}
?>
As expected, this returns NO ERROR!, but when I run this on my live server, I only get ERROR with the same data.
I've determined it's the comma throwing it off! And I am pretty sure, because the php version I am running does an auto escape like \' in the name John\'s, so I ran striptags on all output and still the same error.
DOes anyone know what I am doing wrong or how I can resolve this?
I've got about 8 hours in the "bug" as of now. Been through 40+ variations of the RegEex with no luck.
I check and triple checked all of my form field names, vars etc to ensure everything matcheds and all else is OK.
SOS
Try checking specifically for 1. Per the documentation, it returns FALSE (strict check) on error, 0 (strict check) for no matches, or 1 if there is a match (since it stops at one).
Also, per my own preference, I use the ~ symbol for my regex's. And like David Powers said (only he didn't correct it at all), you don't need most of those backslashes (only for the period and the space).
<?php
$regex = "John's Jerod";
if (preg_match("~^[A-Za-z',\.\s]+$~", $regex) !== 1) {
echo "ERROR!";
} else {
echo "NO ERROR!";
}
?>
Hope this helps!
EDIT: Also, you say you're using strip_tags? That strips any HTML tags in a string -- not slashes. You need strip_slashes to strip slashes, not strip_tags ;)
Your regex doesn't need all those backslashes. It should be this:
"/^[A-Za-z',\.\s]+$/"
Also, you refer to using striptags(). You should be using stripslashes() if your server has magic quotes enabled.
[Edited regex]

PHP's preg-match_all causing Apache Segfault

I'm using two regular expressions to pull assignments out of MySQL queries and using them to create an audit trail. One of them is the 'picky' one that requires quoted column names/etc., the other one does not.
Both of them are tested and parse the values out correctly. The issue I'm having is that with certain queries the 'picky' regexp is actually just causing Apache to segfault.
I tried a variety of things to determine this was the cause up to leaving the regexp in the code, and just modifying the conditional to ensure it wasn't run (to rule out some sort of compile-time issue or something). No issues. It's only when it runs the regexp against specific queries that it segfaults, and I can't find any obvious pattern to tell me why.
The code in question:
if ($picky)
preg_match_all("/[`'\"]((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"] *= *'((?:[^'\\\\]|\\\\.)*)'/", $sql, $matches);
else
preg_match_all("/[`'\"]?((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"]? *= *[`'\"]?([^`'\" ,]+)[`'\"]?/", $sql, $matches);
The only difference between the two is that the first one removes the question marks on the quotes to make them non-optional and removes the option of using different kinds of quotes on the value - only allows single quotes. Replacing the first regexp with the second (for testing purposes) and using the same data removes the issue - it is definitely something to do with the regexp.
The specific SQL that is causing me grief is available at:
http://stackoverflow.pastebin.com/m75c2a2a0
Interestingly enough, when I remove the highlighted section, it all works fine. Trying to submit the highlighted section by itself causes no error.
I'm pretty perplexed as to what's going on here. Can anyone offer any suggestions as to further debugging or a fix?
EDIT: Nothing terribly exciting, but for the sake of completeness here's the relevant log entry from Apache (/var/log/apache2/error.log - There's nothing in the site's error.log. Not even a mention of the request in the access log.)
[Thu Dec 10 10:08:03 2009] [notice] child pid 20835 exit signal Segmentation fault (11)
One of these for each request containing that query.
EDIT2: On the suggestion of Kuroki Kaze, I tried gibberish of the same length and got the same segfault. Sat and tried a bunch of different lengths and found the limit. 6035 characters works fine. 6036 segfaults.
EDIT3: Changing the values of pcre.backtrack_limit and pcre.recursion_limit in php.ini mitigated the problem somewhat. Apache no longer segfaults, but my regexp no longer matches all of the matches in the string. Apparently this is a long-known (from 2007) bug in PHP/PCRE:
http://bugs.php.net/bug.php?id=40909
EDIT4: I posted the code in the answers below that I used to replace this specific regular expression as the workarounds weren't acceptable for my purpose (product for sale, can't guarantee php.ini changes and the regexp only partially working removed functionality we require). Code I posted is released into the public domain with no warranty or support of any kind. I hope it can help someone else. :)
Thank you everyone for the help!
Adam
I have been hit with a similar preg_match-related issue, same Apache segfault. Only the preg_match that causes it is built-into the CMS I'm using (WordPress).
The "workaround" that was offered was to change these settings in php.ini:
[Pcre]
;PCRE library backtracking limit.
;pcre.backtrack_limit=100000
pcre.recursion_limit=200000000
pcre.backtrack_limit=100000000
The trade-off is for rendering larger pages, (in my case, > 200 rows; when one of the columns is limited to a 1500-character text description), you'll get pretty high CPU utilization, and I'm still seeing the segfaults. Just not as frequently.
My site's close to end-of-life, so I don't really have much need (or budget) to look for a real solution. But maybe this can mitigate the issue you're seeing.
Interestingly enough, when I remove the highlighted section, it all works fine. Trying to submit the highlighted section by itself causes no error.
What about size of the submission? If you pass gibberish of equal length, what will happen?
EDIT: splitting and merging will look something like this:
$strings = explode("\n", $sql);
$matches = array(array(), array(), array());
foreach ($strings AS $string) {
preg_match_all("/[`'\"]?((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"]? *= *[`'\"]?([^`'\" ,]+)[`'\"]?/", $string, $matches_temp);
$matches[0] = array_merge($matches[0], $matches_temp[0]);
$matches[1] = array_merge($matches[1], $matches_temp[1]);
$matches[2] = array_merge($matches[2], $matches_temp[2]);
}
Given that this only needs to match against the queries when saving pages or performing other not very often-executed operations, I felt the performance hit of the following code was acceptable. It parses the SQL query ($sql) and places name=>value pairs into $data. Seems to be working well and handles large queries fine.
$quoted = '';
$escaped = false;
$key = '';
$value = '';
$target = 'key';
for ($i=0; $i<strlen($sql); $i++)
{
if ($escaped)
{
$$target .= $sql[$i];
$escaped = false;
}
else if ($quoted!='')
{
if ($sql[$i]=='\\')
$escaped = true;
else if ($sql[$i]==$quoted)
$quoted = '';
else
$$target .= $sql[$i];
}
else
{
if ($sql[$i]=='\'' || $sql[$i]=='`')
{
$quoted = $sql[$i];
$$target = '';
}
else if ($sql[$i]=='=')
$target = 'value';
else if ($sql[$i]==',')
{
$target = 'key';
$data[$key] = $value;
$key = '';
$value = '';
}
}
}
if ($value!='')
$data[$key] = $value;
Thank you everyone for the help and direction!

Categories