It looks fine, but what if a user submitted this as their password:
|" OR 1=1 OR ""="|
This would cause the SQL to read:
|SELECT * FROM users WHERE username="" AND password="" OR 1=1 OR ""=""|
which would allow the attacker to get into your system without even knowing a login!
On many databases you can also run multiple queries by putting a semicolon in the SQL you pass. Consider this password:
|"; DELETE FROM users WHERE ""="|
This would run the first query, which would probably find no records, but it would then run the DELETE query which would delete all of yoru users. Note that this could also be used to delete any other data in yoru system or to change your data or insert a new user with admin priviledges.
To protect against this, you need to "escape" the variables you put into your SQL. When using Mysql you can do this:
If you're using [[http://pear.php.net/package/MDB2|PEAR::MDB2]] you can do this (this will work for *any* database system that MDB2 supports):
quote($username).' AND
password='.$db->quote($password);
?>
If you're using [[http://pear.php.net/package/DB|PEAR::DB]] you can do this (this will work for *any* database system that DB supports):
quoteSmart($username).' AND
password='.$db->quoteSmart($password);
?>
====== What does "Call to undefined method XX::XXXX" mean? ======
Often people who just start using PEAR get messages such as ''Call to undefined method DB_Error::setFetchMode()'' or ''Call to undefined method DB_Error::query()''. This is almost always a result of insufficient error checking. See [[PEAR::Error Handling|PEAR Error Handling]] for more info on how to catch and process these errors.
====== What does "supplied argument is not a valid MySQL result resource" mean? ======
This means that your query failed. Since you're seeing this error, you must not be checking for mysql errors. It's always best to check for errors whenever you can. To see the error, use mysql_error.
====== Why does my script get included multiple times with include_once / require_once? ======
This could also be: 'Why do I get "Cannot redeclare class/function foo in /path/to/myFile.php"?'
This is often the result of different strings being passed to the _once functions that go to the same file. For example, if you're on Windows, you can use upper-case in oen include and lower-case in another. Both will go to the same file, but the _once functions see them as different (most other operating systems, like Linux, have case-sensitive filesystems).
Another example for Linux folk: If you have both the /path/to/includes directory and the /path/to/includes/subdir directory in your include_path and you have the "myinc.php" file in the subdir, you can include the file with one of two paths:
Neither of these is more correct than the other, but they both include the same file. To fix this, use the same string for including in all of your files.
====== Why is magic_quotes_gpc bad? ======
magic_quotes_gpc is a setting which makes all of variables sent to the PHP script by the browser (POST, GET, and COOKIE) "magically" have backslashes before all single and double quotes. This setting was supposed to make inserting values from the client's browser into a database safe. In practice, though, this setting makes quotes show up everywhere, especially when you're not inserting the values into a database, processing the text first, or sending it back to the browser.
Assume that magic_quotes_gpc is on and that you have a form which is validated before submission. If the form does not validate you send the form back with the values filled out for the user's convenience.
';
If the user submits ''I didn't enter 30 chars'' in the text field, the form will not validate and be output once more with the same value in the text field. However, due to magic_quotes_gpc naively escaping the ' in the field, the text will read ''I didn\'t enter 30 chars''. If the user submits this again, it will read ''I didn\\\'t enter 30 chars''. Notice how 2 extra backslahes appeared this time. The number of backslashes will nearly double with every submission.
This can be fixed in one of two ways. The first is to leave magic_quotes_gpc on and run stripslashes on the data whenever it's not going into SQL. This is a bad solutions first of all because you're wasting script time by adding slashes, then removing them. This also means extra typing every time you want to use something from the user but not put it into SQL. This is also extremely dangerous as different databases escape their data differently. For MySQL you add a backslash, but for Oracle you use two single quotes. Using backslashes with Oracle may leave you open to an [[PHPFAQs#what_is_an_sql_injection_attack|SQL Inection Attack]].
The second, and correct way is to handle them only when you need to and in the correct manner. Turn off magic_quotes_gpc. When you're putting data into the database always use a quoting function.
quoteSmart($value).')';
$db->query($sql);
?>
When outputting to the browser, always use htmlentities() with ENT_QUOTES. This goes for outputting variables in JavaScript too.
';
?>
And if you're outputting to plain-text, just output it.
I would also like to note here that if you use a package such as [[http://pear.php.net/package/HTML_QuickForm|PEAR::HTML_QuickForm]], it will take care of all escaping when outputting the form and will even stripslashes for you automatically if magic_quotes_gpc is on and you can't turn it off.
====== Why is register_globals bad? ======
It makes code harder to read, it can easily introduce security vulnerabilities, and the code is guaranteed not to work if register_globals is turned off. For more, see the [[http://us4.php.net/register_globals|manual page]], [[http://marc.theaimsgroup.com/?l=php-general&m=105480233205838&w=2|this discussion]], [[http://www.iamcal.com/publish/articles/php/secure_and_portable_applications/|this article]], [[http://www.schlossnagle.org/~george/blog/archives/271_Why_register_globals_and_remote_includes_are_a_serious_problem_in_PHP.html|another article]], or just [[http://www.google.com/search?hl=en&ie=UTF-8&q=PHP+register_globals+evil&btnG=Google+Search|search Google]].
====== How do I speed up my queries in... ======
===== mysql? =====
Thus spake Curt Zirzow:
To help figure out which queries are running slow there is the
php.ini setting:
mysql.trace_mode=On
Using this will have the php library analyze your queries and if
any of them do table scans php will issue a warning about it, which
can be very helpful finding out where you need indexes.
====== Why does my webpage say "headers already sent"? ======
This is due to some code calling one of the header producing functions after some output happens on your page. These functions include: ''header()'', ''session_start()'', and ''setcookie()''. Output happens because of a print or echo call or, more commonly in these cases, text between ''?>'' and '''' at the end of a PHP file, this can happen. A common solution to this problem it to remove the ?> at the end of PHP files entirely, although this isn't recommended as it's a little sloppy.
If you can't or don't want to avoid output before calling these functions you can also use [[http://us3.php.net/manual/en/ref.outcontrol.php|output buffering]] to stop PHP from outputting anything before you want it output. However, this will cause PHP to store the output until either the end of the script or until you call one of the functions which stop output buffering. This can also cause pages which take a lot of processing to seem to load more slowly as the output won't show up until output buffering is turned off.
====== How do I make search engines spider my dynamic site? ======
A common misconception is that "dynamic" pages, i.e. those with GET parameters, won't be indexed by search engines. This is simply false. Dynamic pages are processed the same as normal pages. Google in particular introduces a small delay when it sees a ? in the URL as they don't want to bog down your site by making lots of dynamic calls at once. You site will still be indexed. The largest factor when it comes to indexing is the processing time of your PHP script.
Read here for lots of good info regarding PHP and search engines: [[http://www.stargeek.com/php-seo.php|http://www.stargeek.com/php-seo.php]]
====== Why can't I use Apache 2 with PHP? ======
The PHP Manual says [[http://us2.php.net/manual/en/install.unix.apache2.php|not to use PHP and Apache2 in production.]] This is because of multi-threading issues. Some PHP libraries are not thread-safe and therefore can crash PHP. These errors are often not seen by smaller or low-traffic sites as they are due to race conditions in the libraries. If you want to use Apache2 with PHP and not have it crash, the recommendation is to use Apache2's prefork mode.
See [[http://marc.theaimsgroup.com/?l=php-general&m=109021837828927&w=2|this thread on the php-general list]] for more discussion.
====== How do I get the last inserted Primary Key (ID) value... ======
===== from a mysql table? =====
Assuming you are using auto_increment...
OR
===== from a non-mysql db? =====
Non-mysql DBs usually use specially defined sequences. You should get the value of the ID *before* inserting the record in this case.
===== from PEAR::DB? =====
There is no built-in way if you're using the auto_increment feature of mysql. You can use the mysql_insert_id() function, as above, but that is non-portable (won't work on other DB systems). If you truly want to be DB backend independant, you should look into [[http://pear.php.net/manual/en/package.database.db.intro-sequences.php|PEAR::DB's sequences]].
====== How do I loop through database results to create a...? ======
===== Select box =====
';
while ($line = mysql_fetch_assoc($result)) {
echo '';
}
echo '';
?>
===== (HTML) Table =====
col1 col2 ';
while ($line = mysql_fetch_assoc($result)) {
echo '"'.htmlentities($line['col1'], ENT_QUOTES).
'" '.htmlentities($line['col2'], ENT_QUOTES).' ';
}
echo '';
?>
====== How do I use sessions? ======
You have to call session_start() before using anything in the session. This must be called before there is any output to the browser. This includes whitespace after/before PHP tags, so:
...
is out.
So is:
After the call to session_start() you can get/set/unset any session value you want at any time in the script (all of this is stored on the server). The best way to do all of this is through the $_SESSION superglobal. Use it like a normal assicative array:
or:
or:
Everything you put in there will be available from any function without having to use the global keyword or the $GLOBALS superglobal.
Note that you can't store resources in the session (at least, they can't be used as resources on subsequent requests.) This includes things such as open file desriptors and database connections or
statement handles.
====== How do I make my numbers output with a certain number of decimal places? ======
In PHP, strings and numbers are converted back and forth on the fly. Here are some examples:
If you use a variable as a number (+ - / *, etc) then it becomes a number and PHP will output it with as many decimals as it has. If you set a variable as a string ('1.00') and don't do any math, it stays as a string. Note: variables coming from forms through GET or POST are *strings*, not numbers.
number_format() converts a number to a string. This is how it makes it output with a certain # of decimals. Note, however this example:
Note how $k became a number again which is not formatted.
Now, since you want to be able to do math on your numbers it's best to assume that all variables that you want to output as numbers are, in fact, numbers and not strings. To that end, whenever you output a number and you want it in a specific format, you should use number_format() when you are outputting it.
====== Why am I getting ''Fatal error: Call to undefined function: MDB2_Driver_mysql::fetchRow()''? ======
This is caused by incorrect use of ''MDB2''. ''MDB2'' returns a statement handle from its ''query()'' method which you then call ''fetchRow()'' on. If you call ''fetchRow()'' on your db object you will get the above error. The correct way to fetch rows with MDB2 is:
require_once 'MDB2.php';
$db = MDB2::connect('mysql://user@host/db');
$sth = $db->query('SELECT * FROM table');
while ($rec = $sth->fetchRow(MDB2_FETCHMODE_ASSOC)) {
print_r($rec);
}