Numbers

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Numbers

Post by s.dot »

If I expect a number to be 34 for example, and I do the following check:

Code: Select all

if(!is_numeric($_POST['number'])){
    die('Invalid ID.');
}
OR

Code: Select all

if(!in_array($_POST['num'],array(1,34,57))){
    die('Invalid ID.');
}
Does that still need to be escaped or cleaned with mysql_real_escape_string(), stripslashes(), strip_tags(), htmlentities() or the likes? Or is it guaranteed to be a number?

I'm curious because I've seen some people use hex representation of ascii text to get around some blocks on other web sites.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
angus
Forum Newbie
Posts: 10
Joined: Sat Sep 17, 2005 5:43 pm

Post by angus »

A number can only have 1 decimal point and numbers (obviously), thats what I believe is_numeric is looking for. SO I wouldn't have thought so.
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Why not run an is_numeric test on the value 1,000.00 and see what is returned. I know that a decimal point is allowed in is_numeric, but I am not sure if commas are.

Another thing you can do is type cast the value to an integer or use int_val to return the numeric (integer) value of the posted data.
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Post by Oren »

angus wrote:A number can only have 1 decimal point and numbers (obviously), thats what I believe is_numeric is looking for. SO I wouldn't have thought so.
That's not true, have a read at http://us2.php.net/manual/en/function.is-numeric.php

scottayy: It may help if you tell us exactly what this number can be (e.g can it be 034? can it be 34.00?).
User avatar
$phpNut
Forum Commoner
Posts: 40
Joined: Tue May 09, 2006 5:13 pm

Post by $phpNut »

I stand corrected :lol:

Forgot about signs and standard form ... and I did A Level Math aswell 8O reasuring :D
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

It will be an integer, usually an auto incremented id from a database

so could be in the range from 1 to X, but won't have 0s in front or contain decimal points.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

Use

Code: Select all

ctype_digit
You may want to check string length as well. That should be secure.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Simplest method is to cast the variable - to integer or float depending on the number (float is needed for very large numbers or you lose data). ctype_digit() will only accept numerals - not decimal points or hex. is_numeric() accepts numerals, decimal point and hex (hence those bypasses mentioned earlier) but never ever accepts the comma separator even if you switch the locale (annoying in many ways).
Does that still need to be escaped or cleaned with mysql_real_escape_string(), stripslashes(), strip_tags(), htmlentities() or the likes? Or is it guaranteed to be a number?
If input filtering is separated into a separate layer from escaping, then it often makes sense to add the extra measures even though they appear to be wasted effort. The idea is called Defense in Depth and attempts to ensure an application is not reliant on just one protective measure (which could conceivably fail for any number of reasons - the common one being developer error) and has a backup or three...
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

If I may ask: there's one thing I don't understand about casting. It's often recommended as a method to validate numeric data. From PHP architects guide to php security: "A cast forces PHP to convert the parameter from a string to a numeric value, ensuring that the input is a valid number".
Example:

Code: Select all

$_GET['product_id'] = (int) $_GET['product_id'];
But the thing is, if someone or something is inputting a non-valid value, input you don't want, isn't it better - at least in some situations - to throw an error or exception when wrong input is entered?

As I see it, the first layer of input filtering/validation should check all input and if any wrong input is entered, an error, warning, message or whatever must be returned. Then the second layer is or could be something like casting to make sure the processing logic only works with the right input. It's just that casting (again, in some situations I can think of) is like: "ok, the data is bad but I'll accept it anyway and clean it up for you".
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

Keep in mind filtering and validation are themselves two discrete ideas. They often overlap or are integrated into the same step), but are not always one and the same. Take the example of using casting to "validate" numeric data. Casting by itself cannot validate a numeric value in the context of your application - it only ensures a value is filtered and ensures it is a numeral - assuming you take filtering to be a narrow upfront check. Actual validation would involve additional tests specific to your app.

As an example, I could be sending an item id to a shopping app. Casting the $_POST value to Integer does not validate the value - it filters out obviously invalid data. I still need to check the item id corresponds to a valid item. In other contexts there may be MIN/MAX contraints, a non-ranged series of valid values, etc.
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Some good points.

But, to take your example of the shopping cart: say I have 10 items which you can buy. They have product_id 1, product_id 2,3, ..10. Now my shopping cart recieves the following:
shop.php?buyitem=3.24523

Now, casting this will convert this to buyitem=3. But the original value was not valid. So I am now processing something which I probably should not, because the 3.245 means someone is trying to mess with my script (for example).

So wouldn't you agree that in this case casting is not the first step to take? Instead, when recieving such a value I should send the user to a page with a friendly error message.
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

A general rule that I follow (excuse me while I butt in here) is to use query string vars in limited scope. I also have a tendency to know exactly what is coming through them (ie, database id's, etc), so I know what to look for. If the passed var doesn't match the type or format I expect, I error the script and send a message to the user. This is just me, more than anything, because I hate when apps don't work the way they are supposed even when someone screws around with it.

As for type casting, it is a secondary cautionary measure, at least for me, and I use it only if I need to, after validating the passed data against what I expect it to be.

/excuses himself on the way out

OK, you all can carry on.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

In this case ctype_digit() will detect the decimal allowing you to pass an error/redirect - it's the best way of ensuring whole number values are being passed - not simply a value to be cast as integer... As you say, this is a specific example - and its one where the context demands something outside simple casting. Actually in this case, casting would "fix" the user input which in general is not recommended if it can lead to a mistake in setting up a shopping cart for a user. Fixing input can become a bad practice to get into to - let the user make the corrections, its their request! :)

I think the moral of the thread should be that filtering/validating should be very specific, precise and appropriate for its context in an application. Anything else is inviting potential trouble...

To Everah, I take the lazy approach of escaping everything entering the database. My open source projects all use the variable binding structure provided by ADOdb and ADOdb Lite which quote and escape all values without mucking around with mysql_real_escape_string() every second line...;) Lazy, but highly effective...
User avatar
Oren
DevNet Resident
Posts: 1640
Joined: Fri Apr 07, 2006 5:13 am
Location: Israel

Post by Oren »

scottayy wrote:It will be an integer, usually an auto incremented id from a database

so could be in the range from 1 to X, but won't have 0s in front or contain decimal points.
I agree with Everah:
Everah wrote:As for type casting, it is a secondary cautionary measure, at least for me, and I use it only if I need to, after validating the passed data against what I expect it to be.
But since GET data is always a string, I don't see another option other than type casting if you want to keep things simple (= not using regex).
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Maugrim_The_Reaper wrote:To Everah, I take the lazy approach of escaping everything entering the database.
Lazy, secure, good programming... That's just semantics. I am with you. Nothing gets close to my database without some sort of washing. Especially when it comes to things coming from the querystring. But like I said, I have minimized my reliance on querystring data being passed to my apps. Almost everything is passed through post, and even then, I validate and revalidate the crap out of it.
Oren wrote:But since GET data is always a string...
Even integer values? I wasn't aware of this. I do know that asking if a querystring numeric value is numeric (is_numeric) returns true when the value is numeric. But I never looked at querystring data being a string always. Interesting.
Post Reply