MySQL injection penetration - mysql

I'm new here so I'll try to make my post as clear and readable as possible.
While browsing some site's log I came across some hacking attempts that I want to recreate/test in a closed server. I made a simple PHP web page that gets a variable named 'id' and without any filtering/validation use it in a query.
Relevant PHP code
$var = $_GET['id'];
echo $_GET['id'] . "<br>\n";
include ( "/var/www/dbconnect.php" );
$mysqli = new mysqli ( $db_host, $db_user, $db_password, "news" );
if ( $mysqli->connect_errno ) { echo "Failed to connect to MySQL: (" . $mysqli- >connect_errno . ") " . $mysqli->connect_error; }
$query = "SELECT id, date, subject FROM news_table WHERE id=" . $var;
//$query = "SELECT id, date, subject FROM news_table WHERE id=250; DROP TABLE test;"; // This won't work because in PHP's implementation multiple statements are not allowed
if ( ! $result = $mysqli->query ( $query ) ) { echo "CALL failed: (" . $mysqli->errno . ") " . $mysqli->error; } else { }
Then I load the page using the following
testserver/test-files/test-mysql-vulnerability.php?id=362099999.1
union select unhex(hex(version())) -- 1=1
and get this result:
CALL failed: (1222) The used SELECT statements have a different number
of columns
The hacker spent 5 minutes sending numerous combinations trying to break into our production server. My production server does not give any indication of success/failure like the error above.
My question is: Can the above hack work when the number of columns don't match? If so how?
tnx

As noted in the comments, don't do this.
To answer your question, though, union is useful in injections because it allows you to use an unrelated table in the output. The error you're seeing is because the original database query wanted a certain number and type of columns, and the injected query wanted only one. In this case we know that we need three columns (from the code), so we want the resultant SQL statement to be
SELECT id, date, subject FROM news_table WHERE id=3 union select 0, 0, unhex(hex(version())) --
(This may not work exactly depending on your data types and my ability to do this off the top of my head).

SELECT id,
date,
subject
FROM news_table
WHERE id = 3
UNION
SELECT NULL AS id,
NULL AS date,
Unhex(Hex(Version())) AS subject
also use mysql instead of mysqli as it is more prone to injections

Related

MySQL to Drupal 7 db_select

I am trying to execute the following mysql query below with drupal 7 db_select. But I cant understand how this can be done. Is anyone can help me to translate the following mysql query into drupal 7 dynamic db query?
My main goal is actually sorting the mysql result by given string position in the name. Please keep in mind that i dont want to fetch all the results and sort them with php, instead I want to use mysql to do that. As i know the "ORDER BY LOCATE" command is doing that exactly.
SELECT name FROM `taxonomy_term_data` WHERE LOCATE('credit', name) > 0 ORDER BY LOCATE('credit', name)
1. Proper example of db_select
It is possible, using drupal 7 db_select, here is my example working code (done with help of this post)
My example in with table cities containing column city. Find cities with double "o" and sort by it's position:
$r = db_select('cities', 't')
->fields('t')
->condition('t.city', '%' . db_like('oo') . '%', 'LIKE');
$r->addExpression("LOCATE('oo', city) ", 'loc');
$r = $r->orderBy('loc', 'DESC')
->execute()
->fetchAllAssoc("id");
So similar in your example would be:
$r = db_select('taxonomy_term_data', 't')
->fields('t')
->condition('t.name', '%' . db_like('credit') . '%', 'LIKE');
$r->addExpression("LOCATE('credit', name) ", 'loc');
$r = $r->orderBy('loc', 'DESC'); //Or ASC
//Execute your query and gather result anyway you want.
2. Do you need to use db_select?
As someone stated in comment in link I posted "There are times and places to just use db_query."
I think this is that time :) Dont overcomplicate your code just to use drupal-way logic, which is often outdated or just too simple for complex tasks.
I think you should try something like this. db_like function seems to do what you are looking for.
$result = db_select('taxonomy_term_data', 'ttd')
->fields('ttd', 'name')
->condition('ttd.name, '%' . db_like('credit') . '%', 'LIKE')
->orderBy('ttd.name', 'DESC')
->execute();

SQL COLUMN NAME [duplicate]

I'd like to get all of a mysql table's col names into an array in php?
Is there a query for this?
The best way is to use the INFORMATION_SCHEMA metadata virtual database. Specifically the INFORMATION_SCHEMA.COLUMNS table...
SELECT `COLUMN_NAME`
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA`='yourdatabasename'
AND `TABLE_NAME`='yourtablename';
It's VERY powerful, and can give you TONS of information without need to parse text (Such as column type, whether the column is nullable, max column size, character set, etc)...
Oh, and it's standard SQL (Whereas SHOW ... is a MySQL specific extension)...
For more information about the difference between SHOW... and using the INFORMATION_SCHEMA tables, check out the MySQL Documentation on INFORMATION_SCHEMA in general...
You can use the following query for MYSQL:
SHOW `columns` FROM `your-table`;
Below is the example code which shows How to implement above syntax in php to list the names of columns:
$sql = "SHOW COLUMNS FROM your-table";
$result = mysqli_query($conn,$sql);
while($row = mysqli_fetch_array($result)){
echo $row['Field']."<br>";
}
For Details about output of SHOW COLUMNS FROM TABLE visit: MySQL Refrence.
Seems there are 2 ways:
DESCRIBE `tablename`
or
SHOW COLUMNS FROM `tablename`
More on DESCRIBE here: http://dev.mysql.com/doc/refman/5.0/en/describe.html
I have done this in the past.
SELECT column_name
FROM information_schema.columns
WHERE table_name='insert table name here';
Edit: Today I learned the better way of doing this. Please see ircmaxell's answer.
Parse the output of SHOW COLUMNS FROM table;
Here's more about it here: http://dev.mysql.com/doc/refman/5.0/en/show-columns.html
Use mysql_fetch_field() to view all column data. See manual.
$query = 'select * from myfield';
$result = mysql_query($query);
$i = 0;
while ($i < mysql_num_fields($result))
{
$fld = mysql_fetch_field($result, $i);
$myarray[]=$fld->name;
$i = $i + 1;
}
"Warning
This extension is deprecated as of PHP 5.5.0, and will be removed in the future."
The simplest solution out of all Answers:
DESC `table name`
or
DESCRIBE `table name`
or
SHOW COLUMNS FROM `table name`
An old PHP function "mysql_list_fields()" is deprecated. So, today the best way to get names of fields is a query "SHOW COLUMNS FROM table_name [LIKE 'name']". So, here is a little example:
$fields = array();
$res=mysql_query("SHOW COLUMNS FROM mytable");
while ($x = mysql_fetch_assoc($res)){
$fields[] = $x['Field'];
}
foreach ($fields as $f) { echo "<br>Field name: ".$f; }
when you want to check your all table structure with some filed then use this code. In this query i select column_name,column_type and table_name for more details . I use order by column_type so i can see it easily.
SELECT `COLUMN_NAME`,COLUMN_TYPE,TABLE_NAME
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA`='yourdatabasename' order by DATA_TYPE;
If you want to check only double type filed then you can do it easily
SELECT `COLUMN_NAME`,COLUMN_TYPE,TABLE_NAME,DATA_TYPE
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA`='yourdatabasename' AND DATA_TYPE like '%bigint%' order by DATA_TYPE;
if you want to check which field allow null type etc then you can use this
SELECT `COLUMN_NAME`,COLUMN_TYPE,TABLE_NAME,IS_NULLABLE,DATA_TYPE
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA`='yourdatabasename' and DATA_TYPE like '%bigint%' and IS_NULLABLE ='NO' order by COLUMN_TYPE;
you want to check more then thik link also help you.
https://dev.mysql.com/doc/refman/5.7/en/columns-table.html
this generates a string of column names with a comma delimiter:
SELECT CONCAT('(',GROUP_CONCAT(`COLUMN_NAME`),')')
FROM `INFORMATION_SCHEMA`.`COLUMNS`
WHERE `TABLE_SCHEMA`='database_name'
AND `TABLE_NAME`='table_name';
function get_col_names(){
$sql = "SHOW COLUMNS FROM tableName";
$result = mysql_query($sql);
while($record = mysql_fetch_array($result)){
$fields[] = $record['0'];
}
foreach ($fields as $value){
echo 'column name is : '.$value.'-';
}
}
return get_col_names();
Not sure if this is what you were looking for, but this worked for me:
$query = query("DESC YourTable");
$col_names = array_column($query, 'Field');
That returns a simple array of the column names / variable names in your table or array as strings, which is what I needed to dynamically build MySQL queries. My frustration was that I simply don't know how to index arrays in PHP very well, so I wasn't sure what to do with the results from DESC or SHOW. Hope my answer is helpful to beginners like myself!
To check result: print_r($col_names);
SHOW COLUMNS in mysql 5.1 (not 5.5) uses a temporary disk table.
http://dev.mysql.com/doc/refman/5.1/en/internal-temporary-tables.html
http://dev.mysql.com/doc/refman/5.1/en/show-columns.html
So it can be considered slow for some cases. At least, it can bump up your created_tmp_disk_tables value. Imagine one temporary disk table per connection or per each page request.
SHOW COLUMNS is not really so slow, possibly because it uses file system cache. Phpmyadmin says ~0.5ms consistently. This is nothing compared to 500ms-1000ms of serving a wordpress page. But still, there are times it matters. There is a disk system involvement, you never know what happens when server is busy, cache is full, hdd is stalled etc.
Retrieving column names through SELECT * FROM ... LIMIT 1 was around ~0.1ms, and it can use query cache as well.
So here is my little optimized code to get column names from a table, without using show columns if possible:
function db_columns_ar($table)
{
//returns Array('col1name'=>'col1name','col2name'=>'col2name',...)
if(!$table) return Array();
if(!is_string($table)) return Array();
global $db_columns_ar_cache;
if(!empty($db_columns_ar_cache[$table]))
return $db_columns_ar_cache[$table];
//IMPORTANT show columns creates a temp disk table
$cols=Array();
$row=db_row_ar($q1="SELECT * FROM `$table` LIMIT 1");
if($row)
{
foreach($row as $name=>$val)
$cols[$name]=$name;
}
else
{
$coldata=db_rows($q2="SHOW COLUMNS FROM `$table`");
if($coldata)
foreach($coldata as $row)
$cols[$row->Field]=$row->Field;
}
$db_columns_ar_cache[$table]=$cols;
//debugexit($q1,$q2,$row,$coldata,$cols);
return $cols;
}
Notes:
As long as your tables first row does not contain megabyte range of data, it should work fine.
The function names db_rows and db_row_ar should be replaced with your specific database setup.
IN WORDPRESS:
global $wpdb; $table_name=$wpdb->prefix.'posts';
foreach ( $wpdb->get_col( "DESC " . $table_name, 0 ) as $column_name ) {
var_dump( $column_name );
}
Try this one out I personally use it:
SHOW COLUMNS FROM $table where field REGEXP 'stock_id|drug_name'
This question is old, but I got here looking for a way to find a given query its field names in a dynamic way (not necessarily only the fields of a table). And since people keep pointing this as the answer for that given task in other related questions, I'm sharing the way I found it can be done, using Gavin Simpson's tips:
//Function to generate a HTML table from a SQL query
function myTable($obConn,$sql)
{
$rsResult = mysqli_query($obConn, $sql) or die(mysqli_error($obConn));
if(mysqli_num_rows($rsResult)>0)
{
//We start with header. >>>Here we retrieve the field names<<<
echo "<table width=\"100%\" border=\"0\" cellspacing=\"2\" cellpadding=\"0\"><tr align=\"center\" bgcolor=\"#CCCCCC\">";
$i = 0;
while ($i < mysqli_num_fields($rsResult)){
$field = mysqli_fetch_field_direct($rsResult, $i);
$fieldName=$field->name;
echo "<td><strong>$fieldName</strong></td>";
$i = $i + 1;
}
echo "</tr>";
//>>>Field names retrieved<<<
//We dump info
$bolWhite=true;
while ($row = mysqli_fetch_assoc($rsResult)) {
echo $bolWhite ? "<tr bgcolor=\"#CCCCCC\">" : "<tr bgcolor=\"#FFF\">";
$bolWhite=!$bolWhite;
foreach($row as $data) {
echo "<td>$data</td>";
}
echo "</tr>";
}
echo "</table>";
}
}
This can be easily modded to insert the field names in an array.
Using a simple: $sql="SELECT * FROM myTable LIMIT 1" can give you the fields of any table, without needing to use SHOW COLUMNS or any extra php module, if needed (removing the data dump part).
Hopefully this helps someone else.
if you use php, use this gist.
it can get select fields full info with no result,and all custom fields such as:
SELECT a.name aname, b.name bname, b.*
FROM table1 a LEFT JOIN table2 b
ON a.id = b.pid;
if above sql return no data,will also get the field names aname, bname, b's other field name
just two line:
$query_info = mysqli_query($link, $data_source);
$fetch_fields_result = $query_info->fetch_fields();
This query fetches a list of all columns in a database without having to specify a table name. It returns a list of only column names:
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'db_name'
However, when I ran this query in phpmyadmin, it displayed a series of errors. Nonetheless, it worked. So use it with caution.
if you only need the field names and types (perhaps for easy copy-pasting into Excel):
SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='databasenamegoeshere'
AND DATA_TYPE='decimal' and TABLE_NAME = 'tablenamegoeshere'
remove
DATA_TYPE='decimal'
if you want all data types
i no expert, but this works for me..
$sql = "desc MyTable";
$result = #mysql_query($sql);
while($row = #mysql_fetch_array($result)){
echo $row[0]."<br>"; // returns the first column of array. in this case Field
// the below code will return a full array-> Field,Type,Null,Key,Default,Extra
// for ($c=0;$c<sizeof($row);$c++){echo #$row[$c]."<br>";}
}
I have tried this query in SQL Server and this worked for me :
SELECT name FROM sys.columns WHERE OBJECT_ID = OBJECT_ID('table_name')
The call of DESCRIBE is working fine to get all columns of a table but if you need to filter on it, you need to use the SHOW COLUMNS FROM instead.
Example of PHP function to get all info of a table :
// get table columns (or return false if table not found)
function get_table_columns($db, $table) {
global $pdo;
if($cols = $pdo->query("DESCRIBE `$db`.`$table`")) {
if($cols = $cols->fetchAll(PDO::FETCH_ASSOC)) {
return $cols;
}
}
return false;
}
In my case, I had to find the primary key of a table. So, I used :
SHOW COLUMNS FROM `table` WHERE `Key`='PRI';
Here is my PHP function :
// get table Primary Key
function get_table_pk($db, $table) {
global $pdo;
$q = "SHOW COLUMNS FROM `$db`.`$table` WHERE `Key` = 'PRI'";
if($cols = $pdo->query($q)) {
if($cols = $cols->fetchAll(PDO::FETCH_ASSOC)) {
return $cols[0];
}
}
return false;
}

Resource Link Indetifier using PDO?

(new to PDO and limited experience with DB's in general) :)
I'm switching over to use PDO in a project..
and even though the data was getting dumped to the DB..there was a strange 'Warning'.. that displayed merging of table I was trying to use..(and another I wasnt)... SELECT()..etc even though I was trying to INSERT()..etc..
using the NON-PDO approach worked fine however (which seems a bit odd)..
example:
include("../_include/db_forms.php");
mysql_query("INSERT INTO $tablename (timestamp, fullname, email, formcode, ipaddress, alldata) VALUES ('" . date("YmdHis") . "','" . mysql_real_escape_string($_POST['name']) . "','" . mysql_real_escape_string($_POST['email']) . "','" . $formcode . "','" . $_SERVER['REMOTE_ADDR'] . "','" . mysql_real_escape_string($allData) . "')");
^ no warning/data dumps to DB just fine
after searching/asking around a bit... it looked like another open database connection for the CMS (for other aspects of the page/header/footer include files..etc) is being used for the other queries, (or vice versa).. and this is causing the warning/problem.
I was told adding a resource link identifier would solve this...
now I understand what it is.., and why it is used (multiple DB's.. this is a reference to what one should be used/opened..etc.)
However.. I'm not exactly sure HOW to implement one.. especially using PDO approach.
here is my PDO approach/code:
require_once("../_include/db_pdo_fulllog.php"); //just a .php file with DEFINE()'s for DB connection details
// new DB routine PDO approach //
$sql = "INSERT INTO $tablename (timestamp, fullname, email, formcode, ipaddress, alldata) VALUES (?,?,?,?,?,?)";
try {
$conn = new PDO("mysql:host=" . DBSERVER . ";dbname=" . DBNAME, DBLOGIN, DBPASSWORD);
$stmt = $conn->prepare($sql);
//$stmt = $conn->prepare("INSERT INTO fulllog(timestamp, fullname, email, formcode, ipaddress, alldata) VALUES (?,?,?,?,?,?)");
//timestamp
$ts = date('Y-m-d H:i:s');
$stmt->bindParam(1, $ts);
//name
$stmt->bindParam(2, $_POST['name']);
//email
$stmt->bindParam(3, $_POST['email']);
//formcode
$stmt->bindParam(4, $formcode);
//ip address
$stmt->bindParam(5, $_SERVER['REMOTE_ADDR']);
//all data
$allData = $_POST['session_title'] .' '. $_POST['session_type'] .' '. $_POST['description'] .' '. $_POST['goals'] .' ' . $_POST['faculty'] .' '. $_POST['chapter'];
$stmt->bindParam(6, $allData);
//execute built statement
$stmt->execute();
}
catch(PDOException $e) {
echo $e->getMessage();
die;
}
How can I add a resource link identifier to this...to stop the warning/error of having the multiple databases in use?
Thanks.
edit:
(since its been requested)
the error/warning message:
Warning: mysql_fetch_row() expects parameter 1 to be resource, boolean given in /usr/www/users/xxxxorg/xxxx.org/admincms/_includes/class.now_cms.php on line 126
Err (Table 'xxxx_forms.fulllogContent' doesn't exist) in query: SELECT n.NodeName, n.ContentId, n.ParentId, n.NodeOrder, n.ParentNode, n.ContentTitle, n.ContentType, n.Status, n.ShowOnNav, n.ShowNav, n.ShowOnSitemap, n.ForceSSL, n.TargetWindow, n.ResourceUrl, n.ShortTitle, n.Keywords, n.Description, n.CCount, n.COrderField, n.COrder, COALESCE(t.TemplateBody, dt.TemplateBody) as Template, COALESCE(ct.TemplateBody, dt.TemplateBody) as ChildTemplate, n.UserGroups, n.Comments, n.Sticky, IF(md.MetaId IS NULL, 0, 1) as HasMetaData FROM fulllogContent n LEFT JOIN fulllogTemplate t ON n.Template=t.TemplateId LEFT JOIN fulllogTemplate ct ON n.ChildTemplate=ct.TemplateId LEFT JOIN fulllogTemplate dt ON dt.TemplateId=dt.TemplateId AND dt.Status=2 LEFT JOIN fulllogSection s ON n.SectionId=s.SectionId LEFT JOIN fulllogMetaDefine md ON s.SectionId=md.SectionId WHERE n.Status=1 AND n.PubDate <= NOW() AND (n.ExpDate >= NOW() OR n.ExpDate = '0000-00-00 00:00:00') ORDER BY CONCAT(IF(n.ParentId=0, '', '/'), n.ParentNode), n.NodeOrder
Again.. my table is -not- 'xxxx_forms.fulllogContent' but in fact: 'xxxx_forms.fulllog'
and most of the other stuff doesnt even apply to my table/DB either.. (nor am I doing a SELECT.. I'm doing an INSERT)..
SO it (somehow) still has the CMS database open/in use.. and is getting merged/mixed up with the one I want.
To re-iterate.. the data IS being dumped to the DB... but the page doesnt refresh/resolve and shows this warning/error..
PDO object IS such a "link identifier" itself - so, you don't need anything else.
Therefore, your problem is not connected to PDO.
To get help on a particular error message you have to post this particular message whole and exact, instead of describing it in vague terms.
Solution to fix this seemed to be changing the define() constant names to be unique/different from the constant names the other connection was using. Thanks for the feedback

Iterate through MYSQL database and replace data in every record in one column

I have this VERY inefficient way of updating the phone numbers in my database after cleaning them of all non-digits.
$san_phone = mysql_query('SELECT * FROM table');
while ($row = mysql_fetch_array($san_phone)) {
$row['phone_clean'] = preg_replace('#[^\d]#', '', $row['phone']);
echo $row['id'] . ' - ' . $row['phone_clean'] . '<br>';
mysql_query("UPDATE table SET phone = " . $row['phone_clean'] . " WHERE id = " . $row['id']);
}
That update part of the loop is causing me to timeout after only about 400 of my 2,400 records. It's obvious I'm doing something wrong so be gentle when schooling me. ;)
First off, stop using mysql_ functions as they are being deprecated. Use mysqli_ or PDO functions instead.
The method you are using to UPDATE your records is inefficient. You should instead create a temporary table, INSERT the new records in a single query, and finally run an UPDATE query to replace the data.
You can start out with this:
$san_phone = mysql_query('SELECT id, phone FROM table');
$insertArray = array();
while ($row = mysql_fetch_array($san_phone)) {
$phone_clean = preg_replace('#[^\d]#', '', $row['phone']);
echo $row['id'] . ' - ' . $row['phone_clean'] . '<br>';
$insertArray[] = "(" . $row[id] . ", '" . $phone_clean . "')";
}
$insertQuery = "INSERT INTO tempTable (id, phone) VALUES ";
$insertQuery = implode(", ", $insertArray);
mysql_query($insertQuery);
I've made a quick demo to illustrate this process. t1 is your original table, and t2 is the temporary table that contains the data to replace.
See it in action
You could use something like this user-defined function:
http://www.mysqludf.org/lib_mysqludf_preg/index.php#PREG_REPLACE_SECTION
or https://launchpad.net/mysql-udf-regexp
And rewrite your query to:
UPDATE table
SET phone = PREG_REPLACE('#[^\d]#', '', phone);
Well, multiple calls to your database incurs a speed hit, and (in my experience), it's a painful one. Even two trips to the database can lead to a noticeable delay over one.
To get around this, you want to minimize the calls to your database, which means doing as much in one call as possible. To this end, try rewriting this as a single SQL update where your replacement logic is in SQL itself. That would mean only one trip to the database and a massive speed improvement.
That's easier said than done, since last I checked, MySQL didn't have a regular expression string replacement function. You could try a work-around, or see about some of the UDF's. Another answer provided a link for one. I recommend looking into that.

MySQL how does SELECT * actually work in MySQL/Query Efficiency

I am building some AJAX queries that need to gather META data (column names) to query one input across several columns on the database...such as if you search for John Anderson and an email address of jand#email.com, "and" would match ANDerson and jAND#email.com
The query looks something like this:
// The joins might not make sense I took the database name out, since it is
// the same as the company I work for...
$querySyntax = "SELECT
idAccount,
FirstName,
LastName,
Email,
Phone,
CCCity,
CCState
FROM
account Right Join
states On account.CCState =
states.ID WHERE ";
$cols = $dbo->query("SELECT * FROM account");
$colcount = (count($cols) > 0 ? $cols->columnCount() : count($cols));
for ($ii=0; $ii < count($search); $ii++) {
if ($ii>0)
$querySyntax = $querySyntax . " AND ";
$querySyntax = $querySyntax . "(";
for ($i=0; $i<$colcount; $i++) {
if ($i>0)
$querySyntax = $querySyntax . " OR ";
$meta = $cols->getColumnMeta($i);
$colNames[$i] = $meta['name'];
$querySyntax = $querySyntax . $colNames[$i] . " LIKE '%" . $search[$ii] . "%'";
}
$querySyntax = $querySyntax . ")";
}
$querySyntax .= " LIMIT 50";
$found = $dbo->query($querySyntax);
for($i=0; $row=$found->fetch();$i++) {
$result[$i] = $row;
}
Ok, with that said, is there a better way to get the column names than the "SELECT * ..." query?
When you run SELECT * does it in fact read and store each record to memory? This could be extremely slow once the database is populated with the thousands of records we have.
SHOW COLUMNS (and most other SHOW commands that query metadata) function internally as queries against the INFORMATION_SCHEMA. When you do that against InnoDB tables, MySQL attempts to refresh table statistics by querying random pages from the respective table. The result is that SHOW commands and queries against INFORMATION_SCHEMA are terribly slow.
There are a few workarounds:
SET GLOBAL innodb_stats_on_metadata=0 which disables the automatic recalc of table stats every time you view the metadata for InnoDB tables.
See Peter Zaitsev's recent blog: Solving INFORMATION_SCHEMA slowness, and the manual Dynamically Changing innodb_stats_on_metadata.
Use DESC account which is much faster than querying INFORMATION_SCHEMA.
Use PDOStatement::getColumnMeta() as you are doing, but use prepare() instead of query(). So it will parse your query but not execute it or attempt to fetch rows of results. Test this carefully because getColumnMeta() is "experimental" and probably buggy.
However, you will find that LIKE '%search%' predicates will perform poorly anyway. You should use a real fulltext search solution like Sphinx Search or Apache Solr.
PS: When coding a loop in PHP, it's better to calculate the count once before the loop, instead of on every iteration of the loop.
for ($ii=0; $ii < count($search); $ii++) {
Should be
$c = count($search);
for ($ii=0; $ii < $c; $ii++) {
This is pretty wasteful, and yes, this retrieves all records from that table. If you are interested in only listing the columns, you could probably use the SHOW COLUMNS FROM <your table> statement (docs) to retrieve just the list of columns. Moreover, you definitely should not do it before every query. Better to do it just once and then store the column list for future use, as they are really unlikely to change during the user session, aren't they?