เมื่อ Database ถูก Hack 1 | Update News IT Security and News Technology - blogthongD
    Home     |     Android     |     iPhone     |     Apple     |     Google

Tuesday, June 5, 2007

เมื่อ Database ถูก Hack 1

ผมเคยเขียนโปรแกรมที่เก็บข้อมูลมาหลายครั้ง ทุกครั้งที่ต้องเก็บข้อมูลในฐานข้อมูล ผมจะดูว่า จะเลือกว่าจะเขียนด้วยภาษาอะไร และเก็บฐานข้อมูลด้วยโปรแกรมจัดการฐานข้อมูลตัวไหน เช่นล่าสุดผมเขียนโปรแกรมด้วยวิชวลเบสิก และเลือกใช้ฐานข้อมูลของ Microsoft SQL Server หรือ ถ้าสมัยก่อนก็เขียนโปรแกรมด้วยฟอกซ์โปร ซึ่งต้องใช้ฐานข้อมูลของฟอกซ์โปร
เมื่อผมลองเขียนโปรแกรมภาษา PHP ซึ่งอนุญาตให้เลือกโปรแกรมจัดการฐานข้อมูลได้หลายแบบ แรกๆ ผมก็เลือกใช้ MySql ซึ่งเป็นโปรแกรมจัดการฐานข้อมูลที่มีให้เกือบทุก Server และ โปรแกรมเมอร์ทุกคนที่เขียน PHP มักจะเลือกเป็นอันดับแรกๆ แล้ววันหนึ่งก็เกิดปัญหาขึ้นมาจนได้ เมื่อ Server ที่ผมเลือกฝากข้อมูล ไม่มีบริการ MySql หรือ มี MySql แต่ลูกค้าเลือก Server ในเงื่อนไขราคาถูก ปัญหาใหญ่จึงเกิดขึ้นแก่ผม ผมจึงตัดสินใจเขียนโปรแกรมจัดการฐานข้อมูลขึ้นมาเอง
อันดับแรกเลย ผมต้องตัดสินใจให้ได้ก่อนว่าจะเก็บข้อมูลในรูปแบบไหน จะเรียกใช้ด้วยคำสั่งอะไร หลังจากคิดดูหลายตลบ จึงตัดสินใจว่า ทำไมไม่เขียนให้เรียกใช้ด้วยคำสั่ง SQL ซะเลย ผมจะได้ไม่ต้องคิดออกแบบคำสั่งในการเรียกใช้ให้วุ่นวาย เนื่องจากมีรูปแบบคำสั่งเป็นมาตรฐานอยู่แล้ว และผมก็ไม่ต้องอธิบายวิธีการใช้ให้มากความ เพราะคนที่เขียนโปรแกรม PHP น่าจะรู้จักคำสั่ง SQL พื้นฐานอยู่แล้ว หรือไม่ก็สามารถศึกษาคำสั่ง SQL ได้จากแหล่งอื่นๆ
ถ้าอย่างนั้นก็เหลืออีกอย่างคือ จะเก็บข้อมูลในลักษณะไหนดี เก็บแบบ Text สิ เริ่มเขียนครั้งแรกน่าจะใช้แบบนี้ เพราะเวลาดูจะง่ายดี
หลังจากตัดสินใจได้ ผมก็เริ่มเขียนเลย
อันดับแรก
ผมเลือกเขียนในลักษณะของ Object เนื่องจาก Object มีคุณสมบัติในการเก็บรักษาข้อมูลที่เป็นส่วนตัว ซึ่งทำให้สามารถเปิดฐานข้อมูลได้ไม่จำกัด เพราะเราสามารถสร้างวัตถุขึ้นมาใหม่ได้ตลอดเวลา และวัตถุแต่ละชิ้นจะมีคุณสมบัติต่างๆ เป็นของตนเอง
วิธีสร้าง class
class Db {
var $sqlCmd="";
function Db($cmd="" , $para1="" , $para2=””) {
if (isSqlCommand($cmd,$sqlCmd)) {
$this->sqlCmd=$cmd;
if (method_exists($this,$sqlCmd)) $this->$sqlCmd($cmd,$para1,$para2);
}
}
}//--- End Of Class Db
ผมกำหนดวิธีการสร้าง class ให้ Class Constructor รับ Parameter มา 3 ค่า โดย $cmd เป็นคำสั่ง SQL ที่ต้องการให้จัดการกับตารางข้อมูล ซึ่งทำให้สามารถสร้าง Object ใหม่พร้อมกับทำงานคำสั่ง SQL ได้ทันที เช่น $guestbookDb = new Db(“SELECT * FROM /guestbook/guestbook.db”) จะเป็นการเปิดตารางข้อมูลชื่อ /guestbook/guestbook.db และดึงข้อมูลทุกรายการและทุกฟิลด์จากตาราง น่าจะสะดวกดี ตัวแปรอีกสองตัวมีไว้สำหรับส่งข้อมูลเพิ่มเติมให้กับคำสั่ง
อันดับที่ 2
ต้องมีคำสั่งสร้างตารางข้อมูลก่อน ซึ่งก็คือคำสั่ง CREATE TABLE
รูปแบบมาตรฐานคือ
CREATE TABLE tableName ( field type [ (size [,decimal] ) ] [index] [ , field type [ (size [,decimal] ) ] [index] [,...] ] )
เอาเป็นว่ารุ่นที่ 1 ยังไม่ต้องมี Index ก่อนก็แล้วกัน แค่ให้สามารถสร้างหัวตารางได้ก็พอ คำสั่งที่ได้จึงมีแค่
CREATE TABLE tableName ( field type [ (size [,decimal] ) ] [ , field type [ (size [,decimal] ) ] [,...] ] )

Method ในการสร้างตารางเป็นอย่างนี้
//--------------------------------------------------------------------------------------------------------------------
function Create($sqlCmd="",$propStr="") {
/*
Method for create new table
CREATE คำสั่งสำหรับสร้างฐานข้อมูลใหม่
version 1.00
สร้างเมื่อวันที่ 1 เมษายน 2545
Sql Command
CREATE TABLE tableName ( field type [ (size [,decimal] ) ] [index] [ , field type [ (size [,decimal] ) ] [index] [,...] ] )
field คือชื่อฟิลด์
type คือชนิดของข้อมูลของฟิลด์ มี ชนิดคือ
- auto [ (size) ] เป็นตัวเลขที่เพิ่มขึ้นอัตโนมัติ ครั้งละ 1 ถ้าระบุ size จะเติม 0 ข้างหน้า(ยังใช้งานไม่ได้)
- char [ (size) ] เป็นตัวอักษรที่มีขนาดจำนวน size ตัวอักษร ถ้าไม่ระบุ size จะไม่จำกัดจำนวน
- date เป็นชนิดวันที่ รูปแบบในการเก็บข้อมูลคือ ปปปปดดวว
- int [ (size) ] เป็นตัวเลขจำนวนเต็มที่มีขนาด size ถ้าไม่ระบุ size จะเป็นจำนวนเต็มไม่จำกัดหลัก
- double [ (size [ ,decimal) ] ] เป็นตัวเลขทศนิยมที่มีขนาด size และจำนวนทศนิยม decimal หลัก
ตัวอย่าง CREATE TABKE guestbook.db ( id auto(6) , postDate date , postTime char(8) , name char , message char )
รูปแบบ db header คือ field1 type=type of field;size=size of field,decimal=number of decimal;

property
debug = yes/no yes=พิมพ์ค่าแสดงผลลัพท์การทำงาน
*/

$fieldTypeList=array("auto","date","char","int","double");
$propList=db_propertyExplode($propStr,"overwrite=no");
if ($propList["debug"]==="yes") {
echo "<div style=\"border:2px white ridge;padding:5px;width:100%;text-align:center;\"/>";
echo "<div style=\"border:2px white groove;background-color:#FF8040;padding:2px;font-weight:bold;font-size:12pt;color:#FFF1EA\"/>Debug of CREATE SQL command</div/>";
}
$this-/>sqlCmd=trim($sqlCmd);
$sqlClause=explodeSqlCmd($this-/>sqlCmd);
if ($propList["debug"]==="yes") db_arrayPrint($sqlClause,"header=sql command<br/>$this-/>sqlCmd");
if (!isSet($sqlClause["create"])) {
$this-/>error(1010,"<b/>$this-/>sqlCmd</b/> is not a create sql command");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
if (!isSet($sqlClause["table"])) {
$this-/>error(1011,"Create table not specify");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
if (!ereg("^[a-zA-Z0-9./:]*[ ]",$sqlClause["table"]." ",$eregResult)) {
$this-/>error(1012,"Table name is not a valid name");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
$tableName=trim($eregResult[0]);
$sqlClause["field"]=trim(subStr($sqlClause["table"],strLen($tableName)));
$sqlClause["table"]=$tableName;
$this-/>dbName($sqlClause["table"]);
if ($this-/>haveFile($this-/>dbFullName())) {
$this-/>error(1013,"Table {$sqlClause["table"]} was exists");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
$fieldStr=$sqlClause["field"];
$field=array();
if ( subStr($fieldStr,0,1) != "(" or subStr($fieldStr,-1) != ")" ) {
$this-/>error(1013,"Field specification incorrect. Must have ( at begin and ) at the end.");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
$fieldStr=subStr($fieldStr,1);
$fieldStr=trim(subStr($fieldStr,0,-1));
if (empty($fieldStr)) {
$this-/>error(1014,"Field specification empty");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
while (!empty($fieldStr)) {
$char=subStr($fieldStr,0,1);
$fieldStr=subStr($fieldStr,1);
if ($char==="(") $openChar=true;
elseif ($char===")") $openChar=false;
if ( $char==="," and !$openChar ) {
if (!empty($fieldSet)) $field[]=$fieldSet;
$fieldSet="";
} else $fieldSet.=$char;
}
if (!empty($fieldSet)) $field[]=$fieldSet;
if ($propList["debug"]==="yes") db_arrayPrint($field,"name=field;header=Field Listing<br/>field : {$sqlClause["field"]};");
$dbStructure="";
foreach ($field as $fieldSpec) {
$fieldSpec=trim($fieldSpec);
if (!ereg("^[a-zA-Z0-9]*[ ]",$fieldSpec,$eregResult)) {
$this-/>error(1014,"Field name <b/>$fieldSpec</b/> not correct");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
$fieldName=trim($eregResult[0]);
$fieldSpec=trim(subStr($fieldSpec,strLen($fieldName)));
list($fieldType,$fieldTypeExtension)=explode("(",$fieldSpec);
$fieldType=trim($fieldType);
if (!in_array($fieldType,$fieldTypeList)) {
$this-/>error(1015,"Field type of <b/>$fieldName $fieldSpec</b/> incorrect");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
if (subStr($fieldTypeExtension,-1)===")") $fieldTypeExtension=subStr($fieldTypeExtension,0,-1);
list($fieldSize,$fieldDecimal)=explode(",",$fieldTypeExtension);
$fieldSize=trim($fieldSize);
if (!ereg("^[0-9]*$",$fieldSize,$r)) {
$this-/>error(1016,"Field size of <b/>$fieldName $fieldSpec</b/> not integer");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
if (!ereg("^[0-9]*$",$fieldDecimal,$r)) {
if ($propList["debug"]==="yes") echo "</div/>";
$this-/>error(1017,"Field decimal of <b/>$fieldName $fieldSpec</b/> not integer");
return;
}
if ( in_array($fieldType,array("auto","char","int")) and !empty($fieldDecimal) ) {
$this-/>error(1018,"No field decimal on type <b/>$fieldType</b/> field <b/>$fieldName $fieldSpec</b/>");
if ($propList["debug"]==="yes") echo "</div/>";
return;
}
$dbStructure .= $fieldName." ";
$dbStructure .= "type=$fieldType;";
$dbStructure .= (empty($fieldSize) ? "" : "size=$fieldSize;");
$dbStructure .= (empty($fieldDecimal) ? "" : "decimal=$fieldDecimal;");
if (subStr($dbStructure,-1)===";") $dbStructure=subStr($dbStructure,0,-1);
$dbStructure .= _dbSeparator;
}
$dbStructure=subStr($dbStructure,0,-1);
if ($propList["debug"]==="yes") echo "<div style=\"border:2px white groove;padding:2px;text-align:left;\"/><b/>db Structure Generate is</b/><br/><font color=red/>$dbStructure</font/></div/>";

$fp=fopen($this-/>dbFullName(),"w");
if ($fp) {
fputs($fp,$dbStructure._dbNewLine);
fclose($fp);
}
if ($propList["debug"]==="yes") echo "</div/>";
$this-/>Select("SELECT * FROM $this-/>dbName");
return true;
}

มีคำสั่งที่ผมต้องสร้างขึ้นมาใหม่ 2-3 คำสั่ง เนื่องจากยังต้องใช้อีกหลายแห่ง คือ
1. db_propertyExplode ซึ่งทำงานคล้ายคำสั่ง Explode ของ PHP
2. db_arrayPrint ไว้สำหรับพิมพ์แสดงค่าตัวแปรชนิด Array
2. explodeSqlCmd ไว้สำหรับถอดคำสั่ง SQL ให้ออกมาเป็นส่วนๆ เพื่อจะได้หาองค์ประกอบของคำสั่งต่อไป
งานส่วนใหญ่ของ Method คือการถอดคำสั่ง SQL ออกมาเป็นส่วนๆ เพื่อหาชื่อฟิลด์ และคุณสมบัติของแต่ละฟิลด์ แล้วสร้างแฟ้มที่ประกอบด้วยส่วนหัวของตาราง ด้วยคำสั่ง
$fp=fopen($this->dbFullName(),"w");
if ($fp) {
fputs($fp,$dbStructure._dbNewLine);
fclose($fp);
}

แค่นี้ก่อน โปรดติดตามอ่านตอนต่อไปนะครับ......

0 Comment: