Example 1:
[Your Greeting]
Thank you for your email. I’m out of the office and will be back at (Date of Return). During this period I will have limited access to my email.
For immediate assistance please contact me on my cell phone at (your cell phone number).
Best Regards,
[Your Name]
Example 2:
[Your Greeting]
I will be out of the office from (Starting date) until (End date).
If you need immediate assistance please contact (Contact Person).
Kind Regards,
[Your Name]
Example 3:
[Your Greeting]
I will be out of the office starting (Starting Date) through (End Date) returning(Date of Return).
If you need immediate assistance during my absence, please contact (Contacts Name) at (Contacts Email Address). Otherwise I will respond to your emails as soon as possible upon my return.
Warm Regards,
[Your Name]
Example 4:
[Your Greeting]
Thank you for your message. I am currently out of the office, with no email access. I will be returning on (Date of Return).
If you need immediate assistance before then, you may reach me at my mobile – (Mobile Number).
Kind Regards,
[Your Name]
Example 5:
[Your Greeting]
I will be out of the office this week. If you need immediate assistance while I’m away, please email (Contact Email Address).
Best,
[Your Name]
Example 6:
[Your Greeting]
I will be away from (Date) until (Return Date). For urgent matters, you can contact (Contact Person).
Best Regards,
[Your Name]
Example 7:
[Your Greeting]
Thank you for your email. Your message is important to (Us/Me) and (I/We) will respond as soon as possible.
Thank You!
[Your Name]
Credit : http://small-bizsense.com/professional-out-of-office-autoresponder-email-messages/
Tuesday, December 15, 2015
Friday, December 11, 2015
อัพไฟล์จาก git ขึ้น FTP ด้วย git-ftp
อัพไฟล์จาก git ขึ้น FTP ด้วย git-ftp สำหรับ Hosting ที่ไม่มี SSH or git support
https://github.com/git-ftp/git-ftp
หากเอาขึ้น FTP ครั้งแรกให้ใช้
ครั้งต่อๆไปก็ใช้
ตัวอย่าง
https://github.com/git-ftp/git-ftp
หากเอาขึ้น FTP ครั้งแรกให้ใช้
$ git ftp init -u-P ftp://host.example.com/public_html
ครั้งต่อๆไปก็ใช้
$ git ftp push -u-P ftp://host.example.com/public_html
ตัวอย่าง
Monday, November 9, 2015
บทความเกี่ยวกับ Git
ลิ้งค์บทความเกี่ยวกับ Git
https://www.atlassian.com/git/
https://gist.github.com/norsez/3016877
http://www.select2web.com/git
https://git-scm.com/book/th/v1
https://confluence.atlassian.com/bitbucketserver/bitbucket-server-documentation-home-776639749.html
http://devahoy.com/2015/08/introduction-to-git-and-github/
https://www.atlassian.com/git/
https://gist.github.com/norsez/3016877
http://www.select2web.com/git
https://git-scm.com/book/th/v1
https://confluence.atlassian.com/bitbucketserver/bitbucket-server-documentation-home-776639749.html
http://devahoy.com/2015/08/introduction-to-git-and-github/
Thursday, October 22, 2015
jQuery show content with load page โหลดข้อมูล page ด้วย jQuery
<script src="app/assets/js/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
$('#showcontentbutton').on('click', function() {
$("#loading_img").show();
$( "#result" ).load('content.php',
function(responseTxt,statusTxt,xhr){
if(statusTxt=="success"){
$("#loading_img").hide();
}
} );
});
});
</script>
<input type="button" id="showcontentbutton" value="show" />
<img src="themes/images/vtbusy.gif" id="loading_img" style="vertical-align:middle; margin-left:5px; display:none;" />
<div id="result"></div>
Query Report with Chart ตัวอย่างการ Query ข้อมูลในการทำ Report
<?php
$mysqli = new mysqli($dbconfig['db_server'],$dbconfig['db_username'],$dbconfig['db_password'],$dbconfig['db_name']);
if ($mysqli->connect_errno) {
die( "Failed to connect to MySQL Vtiger: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error);
}
$mysqli->set_charset("utf8");
switch (input_request("type")) {
case "ReportTbl1":
$query = "SELECT * FROM tbl1 ".$search;
break;
case "ReportTbl2":
$query = "SELECT * FROM tbl2 ".$search;
break;
}
$res = $mysqli->query($query);
$res_c = $mysqli->query($query);
if (!$res) {
die('<p>Invalid query: ' . $mysqli->error.'</p>');
}
$num_rows = $res->num_rows;
//echo $query;
//echo $res->field_count;
//Export to Excel
if(input_request("export")==1){
include ("app/PHPExcel/Classes/PHPExcel.php");
// สร้าง object ของ Class PHPExcel ขึ้นมาใหม่
$objPHPExcel = new PHPExcel();
// กำหนดค่าต่างๆ
$objPHPExcel->getProperties()->setCreator("AppliCAD Co., Ltd.");
$objPHPExcel->getProperties()->setLastModifiedBy("AppliCAD Co., Ltd.");
$objPHPExcel->getProperties()->setTitle("Office 2007 XLSX ReportQuery Document");
$objPHPExcel->getProperties()->setSubject("Office 2007 XLSX ReportQuery Document");
$objPHPExcel->getProperties()->setDescription("ReportQuery from AppliCAD Co., Ltd.");
$sheet = $objPHPExcel->getActiveSheet();
$pageMargins = $sheet->getPageMargins();
$columnCharacter = array('A','B','C','D','E','F','G','H','I','J','K','L','M');
// margin is set in inches (0.5cm)
$margin = 0.5 / 2.54;
$pageMargins->setTop($margin);
$pageMargins->setBottom($margin);
$pageMargins->setLeft($margin);
$pageMargins->setRight(0);
$styleHeader = array(
//'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID,'color' => array('rgb' => 'ffff00')),
'borders' => array('bottom' => array('style' => PHPExcel_Style_Border::BORDER_THIN)),
'font' => array(
'bold' => true,
'size' => 9,
'name' => 'Arial'
));
//Set หัว Column
$rowCell=1;
$c=0;
while ($f = $res->fetch_field()) {
$objPHPExcel->setActiveSheetIndex(0)->setCellValue($columnCharacter[$c].$rowCell, $f->name);
$c++;
}
$c = $c-1;
$objPHPExcel->getActiveSheet()->getStyle('A1:'.$columnCharacter[$c].'1')->applyFromArray($styleHeader);
//เขียนข้อมูล
$rowCell=2;
$c=0;
while($row = $res->fetch_array(MYSQLI_NUM)){ $c++;
for($i=0; $i < $res->field_count; $i++){
$objPHPExcel->setActiveSheetIndex(0)->setCellValue($columnCharacter[$i].$rowCell, $row[$i]);
}
$rowCell++;
}
//
// Rename worksheet
$objPHPExcel->getActiveSheet()->setTitle('ReportQuery');
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$objPHPExcel->setActiveSheetIndex(0);
$time_get_now = new DateTimeField(date("Y-m-d H:i:s"));
$time = str_replace("-","",$time_get_now->convertToDBFormat($time_get_now->getDisplayTime())); //แปลงมาเป็น Format Y-m-d H:i:s
$date = $time_get_now->convertToDBFormat($time_get_now->getDisplayDate()); //แปลงมาเป็น Format Y-m-d
list($h,$i,$s) = explode(":",$time);
$file_name = input_request("type")."_".$date."_".$h."_".$i."_".$s.")";
// Save Excel 2007 file
#echo date('H:i:s') . " Write to Excel2007 format\n";
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
ob_end_clean();
// We'll be outputting an excel file
header('Content-type: application/vnd.ms-excel');
// It will be called file.xls
header('Content-Disposition: attachment;filename="'.$file_name.'.xlsx"');
$objWriter->save('php://output');
exit();
}
?>
<script src="app/assets/js/jquery.min.js"></script>
<script src="app/assets/js/highcharts/highcharts.js"></script>
<script src="app/assets/js/highcharts/exporting.js"></script>
<style>
pre {
width: auto;
max-width: 1024px;
overflow: auto;
background-color: #eeeeee;
word-break: normal !important;
word-wrap: normal !important;
white-space: pre !important;
}
</style>
<script type="text/javascript">
$(function(){
$('#showquery_c').on('click', function() {
$("#showquery").toggle();
});
});
<?php
//Chart
if($num_rows<=1000){
if(input_request("charttype")=="pie"){ ?>
//Pie
$(function () {
$('#chart').highcharts({
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: '<?php echo input_request("type"); ?>'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.y:,.0f} ({point.percentage:.1f}%)</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.y:,.0f} (<strong>{point.percentage:.1f} %</strong>)',
style: {
color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black'
}
}
}
},
credits: {
enabled: false
},
series: [{
name: "Total",
colorByPoint: true,
data: [
<?php
$c_field = $res_c->field_count-1;
$c=0; while($row = $res_c->fetch_array(MYSQLI_NUM)){ $c++; ?>
<?php if($c>1){ ?>,<?php } ?>
{
name: "<?php echo htmlspecialchars(addslashes(str_replace("\t","",str_replace("\n","",str_replace("\r","",$row[0]))))); ?>",
y: <?php echo $row[$c_field]; ?>
}
<?php } ?>
]
}]
});
});
<?php }elseif(input_request("charttype")=="bar"){ ?>
$(function () {
$('#chart').highcharts({
chart: {
type: 'column'
},
title: {
text: '<?php echo input_request("type"); ?>'
},
/* subtitle: {
text: ''
},*/
xAxis: {
categories: [
<?php
$c_field = $res_c->field_count-1;
$c=0; while($row = $res_c->fetch_array(MYSQLI_NUM)){ $c++; ?>
<?php if($c>1){ ?>,<?php }
$data[] = $row[$c_field];
?>
'<?php echo htmlspecialchars(addslashes(str_replace("\t","",str_replace("\n","",str_replace("\r","",$row[0]))))); ?>'
<?php } ?>
],
crosshair: true
},
yAxis: {
min: 0,
title: {
text: 'Values'
}
},
tooltip: {
headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
'<td style="padding:0"><b>{point.y:,.0f} </b></td></tr>',
footerFormat: '</table>',
shared: true,
useHTML: true
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0,
dataLabels: {
enabled: true
}
}
},
credits: {
enabled: false
},
series: [{
name: 'Values',
data: [<?php echo join(',',$data); ?>]
}]
});
});
<?php
} }
//?>
</script>
<span style="font-size:24px" ><?php echo input_request("type"); ?></span>
, <span>Date Between <?php echo "<strong>".$datetime_start."</strong> to <strong>".$datetime_end."</strong>"; ?>
, [<a href="<?php echo $_SERVER['REQUEST_URI']; ?>&export=1">Export to excel</a>]</span>
, [ <span id="showquery_c" style="cursor:pointer;">Show Query</span> ]
<div id="showquery" style="display:none;">
<pre>
<?php echo $query; ?>
</pre>
</div>
<?php if($res->num_rows==0){ die("<p>-No Result-</p>"); } ?>
<?php if($num_rows<=1000){ ?>
<div id="chart" style="height:500px; width:100%"></div>
<?php }else{
echo ", <strong>".$num_rows." records </strong> > 1,000 records Can't generate Chart";
} ?>
<table border=0 cellspacing=1 cellpadding=3 width="100%" class="lvt small">
<tr>
<td class="lvtCol">#</td>
<?php while ($f = $res->fetch_field()) { ?>
<td class="lvtCol"><strong><?php echo $f->table.".".$f->name; ?></strong></td>
<?php } ?>
</tr>
<?php $c=0; while($row = $res->fetch_array(MYSQLI_NUM)){ $c++; ?>
<tr bgcolor=white onMouseOver="this.className='lvtColDataHover'" onMouseOut="this.className='lvtColData'">
<td><?php echo $c; ?></td>
<?php for($i=0; $i < $res->field_count; $i++){ ?>
<td <?php if(is_numeric($row[$i])){ ?> style="text-align:right;" <?php } ?> nowrap="nowrap">
<?php
echo is_numeric($row[$i]) ? number_format($row[$i]) : $row[$i];
if(is_numeric($row[$i])){
$sum[$i] = !isset($sum[$i]) ? $row[$i] : $sum[$i]+$row[$i];
}
?>
</td>
<?php } ?>
</tr>
<?php } ?>
<tr>
<td></td>
<?php for($i=0; $i < $res->field_count; $i++){ ?>
<td style=" text-align:right;"><?php if(isset($sum[$i])){ echo '<strong>'.number_format($sum[$i]).'</strong>'; } ?></td>
<?php } ?>
</tr>
</table>
<?php
$res->close();
$res_c->close();
$mysqli->close();
?>
Friday, October 16, 2015
iframe enlarge the height of the content on the page.-iframe ขยายขนาดตามความสูงของเนื้อหาในหน้า
นำโค๊ดด้านล่างไปใส่ใว้ใน หน้าที่เป็นเนื้อหาที่จะถูกดึงมาใน iframe
pagecontent.html
ในหน้าหลักที่ใช้ iframe
index.html
pagecontent.html
function sizeFrame() {
$("#syncpotentials", top.document).css({ height: 0 });
// กำหนดความสูงของ iframe ให้เท่ากับ 0
var heightDiv=$(document).height();
// หาความสูงของเพจ pagecontent.html
$("#syncpotentials", top.document).height(heightDiv);
// กำหนดความสูงของ iframe ให้เท่ากับความสูงของ pagecontent.html
}
$(function(){
sizeFrame();
// เรียกใช้ฟังก์ขันเมื่อไฟล์ pagecontent.html โหลดเสร็จแล้ว
$("#syncpotentials").load(sizeFrame);
// เรียกใช้ฟังก์ขันเมื่อ iframe โหลด ไฟล์ pagecontent.html
});
ในหน้าหลักที่ใช้ iframe
index.html
<iframe name="syncpotentials" id="syncpotentials" src="pagecontent.html" height="400px" width="100%" frameborder="0" scrolling="no" style="padding:10px 0 0 0;"></iframe>
Disable Copy/Paste - หาก Copy ห้ามเลือก ห้ามคลิกขวา ในหน้าเว็บ
แทรกโค๊ดด้านล่างใน Tag body หรือ Tag อื่นๆที่ไม่ต้องการให้ Copy หรือคลิกขวา
ondragstart="return false" onselectstart="return false" oncopy="return false" oncut="return false"ตัวอย่าง
<body ondragstart="return false" onselectstart="return false" oncopy="return false" oncut="return false" >
jQuery Detect if page has finished loading - กำหนด Action เมื่อหน้าเพจโหลดเสร็จ
$("#loading").show();
jQuery(window).load(function () {
$("#loading").hide();
});
Friday, October 9, 2015
Generate loading animated GIF and APNG,Free ajax loader
Free ajax loader (loading animated GIF and APNG) spinners, bars and 3D animations generator for AJAX and JQuery
http://preloaders.net/
http://preloaders.net/
Thursday, October 1, 2015
PHP highlight search keywords
function highlightkeyword($str, $search) {
if($search){
$highlightcolor = "#FFCC33";
$occurrences = substr_count(strtolower($str), strtolower($search));
$newstring = $str;
$match = array();
for ($i=0;$i<$occurrences;$i++) {
$match[$i] = stripos($str, $search, $i);
$match[$i] = substr($str, $match[$i], strlen($search));
$newstring = str_replace($match[$i], '[#]'.$match[$i].'[@]', strip_tags($newstring));
}
$newstring = str_replace('[#]', '<span style="background-color:'.$highlightcolor.'">', $newstring);
$newstring = str_replace('[@]', '</span>', $newstring);
return $newstring;
}else{
return $str;
}
}
Credit: http://www.webdevdoor.com/php/highlight-search-keyword-string-function/
Monday, September 28, 2015
Export Excel จาก MySQL ด้วย PHP โดยใช้ PHPExcel
<?php
require_once("connect.inc.php");
$query = " SELECT *,
CASE
WHEN is_subscription = 1
THEN 'Yes'
ELSE '-'
END as subscription
FROM vtiger_leaddetails ORDER BY lead_no ";
$res = $mysqli->query($query);
include ("PHPExcel/Classes/PHPExcel.php");
// สร้าง object ของ Class PHPExcel ขึ้นมาใหม่
$objPHPExcel = new PHPExcel();
// กำหนดค่าต่างๆ
$objPHPExcel->getProperties()->setCreator("AppliCAD Co., Ltd.");
$objPHPExcel->getProperties()->setLastModifiedBy("AppliCAD Co., Ltd.");
$objPHPExcel->getProperties()->setTitle("Office 2007 XLSX Leads Document");
$objPHPExcel->getProperties()->setSubject("Office 2007 XLSX Leads Document");
$objPHPExcel->getProperties()->setDescription("Leads from AppliCAD Co., Ltd.");
$sheet = $objPHPExcel->getActiveSheet();
$pageMargins = $sheet->getPageMargins();
// margin is set in inches (0.5cm)
$margin = 0.5 / 2.54;
$pageMargins->setTop($margin);
$pageMargins->setBottom($margin);
$pageMargins->setLeft($margin);
$pageMargins->setRight(0);
//กำหนดความกว้างของคอลัมน์
$objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(10);
$objPHPExcel->getActiveSheet()->getColumnDimension('B')->setWidth(10);
$objPHPExcel->getActiveSheet()->getColumnDimension('C')->setWidth(15);
$objPHPExcel->getActiveSheet()->getColumnDimension('D')->setWidth(15);
$objPHPExcel->getActiveSheet()->getColumnDimension('E')->setWidth(40);
$objPHPExcel->getActiveSheet()->getColumnDimension('F')->setWidth(15);
$objPHPExcel->getActiveSheet()->getColumnDimension('G')->setWidth(15);
$objPHPExcel->getActiveSheet()->getColumnDimension('H')->setWidth(35);
$objPHPExcel->getActiveSheet()->getColumnDimension('I')->setWidth(10);
$objPHPExcel->getActiveSheet()->getColumnDimension('J')->setWidth(10);
//กำหนด Style ของหัวคอลัมน์
$styleHeader = array(
'fill' => array('type' => PHPExcel_Style_Fill::FILL_SOLID,'color' => array('rgb' => 'ffff00')),
'borders' => array('bottom' => array('style' => PHPExcel_Style_Border::BORDER_THIN)),
'font' => array(
'bold' => true,
'size' => 9,
'name' => 'Arial'
));
//เขียนหัวคอลัมน์
$objPHPExcel->setActiveSheetIndex(0)
->setCellValue('A1', 'leadid')
->setCellValue('B1', 'lead_no')
->setCellValue('C1', 'firstname')
->setCellValue('D1', 'lastname')
->setCellValue('E1', 'company')
->setCellValue('F1', 'mobile')
->setCellValue('G1', 'phone')
->setCellValue('H1', 'email')
->setCellValue('I1', 'Sub')
->setCellValue('J1', 'attended');
$objPHPExcel->getActiveSheet()->getStyle('A1:J1')->applyFromArray($styleHeader);
//เริ่มเขียนข้อมูลที่แถวที่ 2
$rowCell=2;
while($row=$res->fetch_array()){
$objPHPExcel->setActiveSheetIndex(0)
->setCellValue('A'.$rowCell, $row['leadid'])
->setCellValue('B'.$rowCell, $row['lead_no'])
->setCellValue('C'.$rowCell,$row['firstname'])
->setCellValue('D'.$rowCell, $row['lastname'])
->setCellValue('E'.$rowCell, $row['company'])
->setCellValueExplicit('F'.$rowCell, $row['mobile'],PHPExcel_Cell_DataType::TYPE_STRING)
->setCellValueExplicit('G'.$rowCell, $row['phone'],PHPExcel_Cell_DataType::TYPE_STRING)
->setCellValue('H'.$rowCell, $row['email'])
->setCellValue('I'.$rowCell, $row['subscription'])
->setCellValue('J'.$rowCell, $row['attended']);
$rowCell++;
}
$mysqli->close();
// Rename worksheet
$objPHPExcel->getActiveSheet()->setTitle('Leads');
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$objPHPExcel->setActiveSheetIndex(0);
//ตั้งชื่อไฟล์
$datetime=date("Y-m-d-H:i:s");
$file_name = "Leads_".$datetime;
// Save Excel 2007 file
#echo date('H:i:s') . " Write to Excel2007 format\n";
$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
ob_end_clean();
// We'll be outputting an excel file
header('Content-type: application/vnd.ms-excel');
// It will be called file.xls
header('Content-Disposition: attachment;filename="'.$file_name.'.xlsx"');
$objWriter->save('php://output');
exit();
?>
สามารถดาวน์โหลด Class PHPExcel ได้จาก https://phpexcel.codeplex.com/
Friday, September 25, 2015
Thursday, September 24, 2015
Export Excel จาก MySQL ด้วย PHP แบบง่ายๆ
เพียงนำโค๊ดด้านล่างนี้ไปใว้บนสุดของหน้าเว็บไซต์ที่ต้องการ Export และเปลี่ยนชื่อ file_name เป็นชื่อที่ต้องการ
ถ้าแสดงข้อมูลด้วย table
<?php
header('Content-Type: text/html; charset=utf-8');
header("Content-Type: application/vnd.ms-excel");
header('Content-Disposition: attachment; filename="file_name.xls"');#file name
echo '<html xmlns:o="urn:schemas-microsoft-com:office:office"xmlns:x="urn:schemas-microsoft-com:office:excel"xmlns="http://www.w3.org/TR/REC-html40">';
?>
ถ้าแสดงข้อมูลด้วย table
<TABLE x:str BORDER="1">คือตัวเลขที่ขึ้นต้นด้วย 0 ก็จะแสดงเลข 0 ด้วย โค๊ดข้างบนคือทำให้ตัวเลขกลายเป็นชนิดของ string
Wednesday, September 23, 2015
ตัวอย่างประยุกต์การอ่านไฟล์ Excel และเขียนลง MySQL โดยใช้ PHPExcel
ตัวอย่างประยุกต์การอ่านไฟล์ Excel และเขียนลง MySQL โดยใช้ PHPExcel
โดยชื่อหัว Colum ของไฟล์ Excel จะเป็นดังนี้

Code
สามารถดาวน์โหลด Class PHPExcel ได้จาก https://phpexcel.codeplex.com/
โดยชื่อหัว Colum ของไฟล์ Excel จะเป็นดังนี้

Code
<?php
set_time_limit(0);
require_once("connect.inc.php");
//File สำหรับ Import
$inputFileName="Leads.xlsx";
/** PHPExcel */
require_once 'PHPExcel/Classes/PHPExcel.php';
/** PHPExcel_IOFactory - Reader */
include 'PHPExcel/Classes/PHPExcel/IOFactory.php';
$inputFileType = PHPExcel_IOFactory::identify($inputFileName);
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load($inputFileName);
$objWorksheet = $objPHPExcel->setActiveSheetIndex(0);
$highestRow = $objWorksheet->getHighestRow();
$highestColumn = $objWorksheet->getHighestColumn();
$headingsArray = $objWorksheet->rangeToArray('A1:'.$highestColumn.'1',null, true, true, true);
$headingsArray = $headingsArray[1];
$r = -1;
$namedDataArray = array();
for ($row = 2; $row <= $highestRow; ++$row) {
$dataRow = $objWorksheet->rangeToArray('A'.$row.':'.$highestColumn.$row,null, true, true, true);
if ((isset($dataRow[$row]['A'])) && ($dataRow[$row]['A'] > '')) {
++$r;
foreach($headingsArray as $columnKey => $columnHeading) {
$namedDataArray[$r][$columnHeading] = $dataRow[$row][$columnKey];
}
}
}
$c=0;
foreach ($namedDataArray as $resx) {
$c++;
echo "(".$c.") ".$resx['lead_no']." : ".$resx['firstname']."
";
//Insert
$query = " INSERT INTO vtiger_leaddetails (leadid,lead_no,email,firstname,lastname,company,mobile,phone,is_subscription) VALUES
(
'".$c."',
'".$resx['lead_no']."',
'".$resx['email']."',
'".addslashes($resx['firstname'])."',
'".addslashes($resx['lastname'])."',
'".addslashes($resx['company'])."',
'".addslashes($resx['mobile'])."',
'".addslashes($resx['phone'])."',
'".$resx['is_subscription']."'
)";
//echo $query."
";
$res_i = $mysqli->query($query);
//
}
$mysqli->close();
?>
สามารถดาวน์โหลด Class PHPExcel ได้จาก https://phpexcel.codeplex.com/
Thursday, September 17, 2015
HTML accesskey Attribute การกำหนดปุ่มคียน์ลัดให้กับ HTML
HTML accesskey Attribute
Example
Credit : http://www.w3schools.com/tags/att_global_accesskey.asp
Example
<a href="http://www.w3schools.com/css3" accesskey="c">CSS3</a>
<button type="button" title="Clear [Alt+C]" accesskey="c" class="btn btn-default" onClick="window.open('index.php','_self');">เคลียร์</button>
| Browser | Windows | Linux | Mac |
|---|---|---|---|
| Internet Explorer | [Alt] + accesskey | N/A | |
| Chrome | [Alt] + accesskey | [Alt] + accesskey | [Control] [Alt] + accesskey |
| Firefox | [Alt] [Shift] + accesskey | [Alt] [Shift] + accesskey | [Control] [Alt] + accesskey |
| Safari | [Alt] + accesskey | N/A | [Control] [Alt] + accesskey |
| Opera | Opera 15 or newer: [Alt] + accesskey Opera 12.1 or older: [Shift] [Esc] + accesskey | ||
Credit : http://www.w3schools.com/tags/att_global_accesskey.asp
HTML Color Picker เครื่องมีในการเลือกโค๊ดสี
HTML Color Picker -> http://www.w3schools.com/tags/ref_colorpicker.asp
Place cursor at end of text in text input element ให้ Cursor อยู่หลังข้อความใน Texbox
เวลาโหลดหน้าเว็บมาให้ Cursor อยู่หลังข้อความใน Textbox และให้โฟกัสที่ Textbox นี้
<input type="text" id="name" name="name" onfocus="this.value = this.value;" autofocus />
Thursday, August 27, 2015
โค๊ด JavaScript ห้ามกรอกฟอร์มภาษาไทย
$('[name="company"]').keyup(function(event){
var orgi_text="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890;:<>?._-, ~#&()@!'*+}{][$%^/|=\"";
var str = $('[name="company"]').val();
var str_length=str.length;
var str_length_end=str_length-1;
var isEng=true;
var Char_At="";
for(i=0;i=1){
if(isEng==false){
$('[name="company"]').val('');
}
}
})
Thursday, August 20, 2015
ตัวอย่าง jQuery loadContent Show และคลิกลิ้งค์ให้โหลดภายในหน้านั้นได้เลย
$(document).ready(function(){
$("#loading_img").show();
$("#showAppointmentData").load("index.php?module=Appointment&action=GetAppointmentDataRelateAccount&record="+3368,function(responseTxt,statusTxt,xhr){
if(statusTxt=="success")
$("#loading_img").hide();
});
$("body").on("click",".browse_page a",function(event){
event.preventDefault();
var url=$(this).attr("href");
//แสดงแบบปกติ
$("#loading_img").show();
$("#showAppointmentData").load(url,function(responseTxt,statusTxt,xhr){
if(statusTxt=="success")
$("#loading_img").hide();
});
return false;
});
});
Tuesday, August 18, 2015
ตัวอย่าง jQuery loadSelectBox
function loadSelectBox(id,url,selected){
$.get(
url,{},function(data){
$(id).html(data);
if (selected!=''){
$(id+' option[value='+selected+']').attr('selected','selected');
}
$("#loading_img").hide();
}
);
}
//ดึง Staff มาแสดง
$("#roleid_").on('change', function() {
if($("#roleid_").val()!= ""){
$("#loading_img").show();
var userid = '';
loadSelectBox(
'#userid',
'index.php?module=AppDashboard&action=getStaffPopup&roleid_='+$("#roleid_").val(),
userid
);
}
});
var userid = '';
loadSelectBox(
'#userid',
'index.php?module=AppDashboard&action=getStaffPopup&roleid_='+$("#roleid_").val(),
userid
);
เชื่อมคำด้วย CONCAT ใน MySQL
CONCAT(str1,str2,...)ตัวอย่าง
SELECT CONCAT(firstname,lastname) as name FROM tbl
Friday, August 14, 2015
jQuery ใช้งานบ่อยๆ
Check Box
ตรวจสอบว่า Check Box ทำการ checked อยู่หรือเปล่า
Disable
Value
Clear
tr class='advsearch'
ตรวจสอบว่า Check Box ทำการ checked อยู่หรือเปล่า
if($('#ID').attr('checked')) {
//code hear
}
Check Box ทำการ Checked$('#ID').attr('checked','checked');
Check Box เอา checked ออก$('#ID').removeAttr('checked');
Disable
$("#ID").prop("disabled",true);
Input form : เอา Disable ออก$("#ID").prop("disabled",false);
Value
Clear
$("#ID").val('');
$('#ID').html('')
SET$("#ID").val('TestValue');
$('#ID').html('TestValue');
ตรวจสอบ Valueif($("#ID").val() == ""){
//code hear
}
if($('#ID').html() == 'TestValue'){
//code hear
}
Attr$('[name="firstname"]').attr("placeholder","Please fill this form in english.");
$([name="company"]').attr("autocomplete","off");
$('[name="campaignname"]').attr("value","TestCampaign");
tr class='advsearch'
var rows = $('table.searchUIBasic tr');
$("#advancesearch").change(function () {
rows.filter('.advsearch').toggle();
});
ตัวอย่าง SQL แสดงจำนวน Leads ที่สร้างขึ้นในแต่ละเดือน Group ตาม Department
$current_year= date("Y");
$query = " SELECT ";
for($i=1;$i<=12;$i++){
$first_of_month = gmmktime(0,0,0,sprintf('%02d',$i),1,$current_year);
$days_in_month = gmdate('t',$first_of_month);
$query .=" SUM(
CASE WHEN DATE( createdtime )
BETWEEN DATE('".$current_year."-".sprintf('%02d',$i)."-01')
AND LAST_DAY('".$current_year."-".sprintf('%02d',$i)."-".$days_in_month."')
THEN 1
ELSE 0
END ) AS c_".$i.",";
}
$query .=" SUM(
CASE WHEN DATE( createdtime )
BETWEEN DATE('".$current_year."-01-01')
AND LAST_DAY('".$current_year."-12-31')
THEN 1
ELSE 0
END ) AS c_all,
department,
FROM tbl_leads
GROUP BY department
Having c_all>0 ";
Friday, August 7, 2015
Xampp วิธี Start Apache ใน Windows 10
Xampp วิธี Start Apache ใน Windows 10
วิธีแรก
ไปปิด Service ของ World Wide Web Publishing Service เพื่อไม่ให้ใช้งาน Port 80 แล้วลอง Start Apache ใหม่อีกรอบ
หรืออาจจะตั้งค่าให้ Service ของ World Wide Web Publishing Service ทำการ Start แบบ Manual โดยที่ดับเบิลคลิกที่ World Wide Web Publishing Service แล้วไป Set ตรง Startup Type เป็น Manual
วิธีแรก
ไปปิด Service ของ World Wide Web Publishing Service เพื่อไม่ให้ใช้งาน Port 80 แล้วลอง Start Apache ใหม่อีกรอบ
หรืออาจจะตั้งค่าให้ Service ของ World Wide Web Publishing Service ทำการ Start แบบ Manual โดยที่ดับเบิลคลิกที่ World Wide Web Publishing Service แล้วไป Set ตรง Startup Type เป็น Manual
Monday, July 20, 2015
เจาะระบบ WEB APP PHP/MYSQL และการป้องกัน
ชี้แจง
บทความนี้มีจุดประสงค์เพื่อนำเสนอให้เห็นช่องโหว่เรื่องความปลอดภัยของระบบ Web Application ที่ใช้ PHP/MySQL โดยจำเป็นต้องสาธิตขั้นตอนการเจาะระบบ เพื่อให้เห็นถึงจุดอ่อนและจะได้ทราบแนวทางป้องกัน ไม่ได้มีจุดประสงค์ชี้นำในการกระทำผิด ผู้ที่ใช้ความรู้ในทางที่ผิดต้องรับผิดชอบต่อการกระทำของตนเอง
… เข้าเรื่อง
โดยทั่วไปแล้ว ระบบ Web Application ที่เขียนขึ้นมาไม่ดี มักจะมีช่องโหว่ให้ผู้ไม่หวังดีเข้ามาโจมตีระบบ ไม่ว่าจะเป็นการทำ SQL Injection, XSS, Buffer Overflow หรือเทคนิคอื่นๆ ซึ่งถ้าสังเกตจากข่าวการโจมตีเว็บไซต์ส่วนใหญ่ก็มักจะโดนในรูปแบบนี้ มาดูกันดีกว่าว่าปัญหาแต่ละแบบคืออะไร โจมตีอย่างไร และป้องกันอย่างไร
1. SQL Injection
คือการแทรกคำสั่ง SQL เข้าไปในส่วนของการสั่ง Query ของโปรแกรม เพื่อให้โปรแกรมทำงานผิดพลาดหรือทำงานนอกเหนือไปจากสิ่งที่ผู้เขียนโปรแกรมกำหนด
สมมุติหน้าจอ Login ของเว็บไซต์หนึ่งมีช่องให้ผู้ใช้กรอก Username และ Password ดังรูป
เมื่อผู้ใช้คลิกปุ่ม Login ก็จะส่งข้อมูลจาก Form ไปที่หน้าสำหรับตรวจสอบค่าที่รับเข้ามา โดยมีส่วนของโค้ดในการตรวจสอบคร่าวๆ ดังนี้$username = $_GET[‘username’];
$password = $_GET[‘password’];$query = “SELECT * FROM users WHERE username=’$username’ AND password=’$password';”;
$result = mysql_query($query) or die(mysql_error());if ($result && mysql_num_rows($result) == 1) {
// Login Successful
echo “Welcome ” . $username;
} else {
//Login failed
echo ‘Uername and/or password incorrect.';
}
การทำงานของโค้ดข้างบนนี้ก็คือ เอาค่า $username และ $password ที่ได้รับ เข้ามาเป็นเงื่อนไขของคำสั่ง Query เช่น ถ้า $username เป็น abc และ $password เป็น 1234 คำสั่ง Query ที่ได้จะเป็นดังนี้
SELECT * FROM users WHERE username=’abc’ AND password=’1234′;
ซึ่งโปรแกรมก็จะไปตรวจสอบในฐานข้อมูล ว่ามี username และ password นี้อยู่จริงหรือเปล่า ถ้ามีก็จะ SELECT ค่าใน row นั้นมา และผู้ใช้ก็จะสามารถ Login เข้าสู่ระบบได้เพราะใส่ username และ password ถูกต้อง แต่ถ้าไม่สามารถ SELECT ค่าได้ หมายความว่าผู้ใช้ใส่ username หรือ password ไม่ถูกต้อง ก็จะไม่อนุญาตให้เข้าสู่ระบบ
ตัวอย่างการโจมตี
ในการเจาะระบบ ทำได้โดยการใส่ค่า input ที่เป็นคำสั่ง SQL เพื่อให้เข้าไปเปลี่ยนการทำงานของคำสั่ง Query ตัวอย่างเช่น ใส่ค่า $username เป็น abc’ or ‘1’=’1 จะได้คำสั่ง Query ดังนี้
SELECT * FROM users WHERE username=’abc’ or ‘1’ = ‘1’ AND password=”;
ซึ่งผลที่ได้จากการใส่คำสั่งนี้คือ สามารถผ่านเข้าสู่ระบบได้โดยใช้ username ชื่อ abc โดยไม่จำเป็นต้องใส่หรือรู้ password เลย เนื่องจากว่าการใส่คำสั่ง or ‘1’=’1′ จะเป็นการทำให้เงื่อนไข username เป็นจริง และจะ Select เอาค่า row ในตารางออกมาได้
นอกจากการใส่คำสั่งเพื่อข้ามการตรวจสอบ username, password ได้แล้ว การทำ SQL Injection ยังสามารถแทรกคำสั่ง SQL อันตรายๆ ได้อีกด้วย เช่น DROP TABLE, DELETE, … ฯลฯ
การป้องกัน
ก่อนจะเอา input ที่ผู้ใช้ป้อน เข้ามามาเป็นส่วนหนึ่งของเงื่อนไขคำสั่ง Query ต้องมีการตรวจสอบค่าที่รับเข้ามาว่ามี Escape Character หรือเปล่า Escape Character ก็คืออักขระพิเศษในภาษา SQL ที่สงวนไว้สำหรับการเรียกฐานข้อมูล เช่น เครื่องหมาย ‘ (single quote) ใน MySQL โดยเมื่อ MySQL พบตัว Escape Character ก็จะถือว่าจบส่วนของคำสั่งนี้ และจะทำคำสั่งที่อยู่ถัดไป
ใน PHP มีฟังก์ชันที่ชื่อว่า mysql_real_escape_string() เพื่อเอาไว้กรอง Escape Character ของ MySQL เช่น เมื่อผู้ใช้ป้อนค่า $username เข้ามาเป็น abc’ or ‘1’=’1 แล้วเราเรียกใช้ฟังก์ชัน $username = mysql_real_escape_string($username) จะได้ค่า $username เป็น abc\’ or \’1\’=\’1 ซึ่งก็คือการเอาเครื่องหมาย \ ไปใส่ไว้ก่อนหน้าเครื่องหมาย ‘ นั่นเอง
ข้อมูลเพิ่มเติม
http://en.wikipedia.org/wiki/SQL_injection
http://www.unixwiz.net/techtips/sql-injection.html
http://www.securiteam.com/securityreviews/5DP0N1P76E.html
http://www.unixwiz.net/techtips/sql-injection.html
http://www.securiteam.com/securityreviews/5DP0N1P76E.html
2. Cross Site Scripting (XSS)
คือการแทรกสคริปต์หรือแท็ก HTML ลงไปในหน้าเว็บ โดยปกติแล้วเทคนิคนี้มักจะพบได้ในระบบเว็บที่อนุญาตให้ผู้ใช้โพสต์ข้อความเข้ามา แล้วแสดงผลข้อความที่ผู้ใช้โพสต์ขึ้นมาบนหน้าเว็บ เช่น Guestbook, Webboard โดยตัวอย่างนี้จะสมมุติระบบ Guestbook มีช่อง Input ให้กรอก Name และ Message จากนั้นเมื่อคลิกปุ่ม Sign Guestbook จะไปทำคำสั่ง Query ดังนี้
$query = “INSERT INTO guestbook VALUES (‘$name’,’$message’);”;
จากนั้นส่วนหน้าเว็บที่แสดงผล Guestbook ก็จะเอาข้อความจากฐานข้อมูลมาแสดง
ตัวอย่างการโจมตี
ลองใส่โค้ด JavaScript ลงใน Guestbook ดังรูป
เมื่อคลิกปุ่ม Sign Guestbook ระบบก็จะเอาข้อความที่โพสต์ไว้มาใส่เป็นส่วนหนึ่งของหน้าเว็บ ทำให้ขึ้น Popup Message ขึ้นมา
การโจมตีแบบ XSS นอกจากจะสามารถฝังสคริปต์ในหน้าเว็บได้แล้ว ยังสามารถฝังองค์ประกอบ HTML อื่นๆ ได้หมด เช่น Iframeการป้องกัน
เอาข้อความที่รับเข้ามา มาเข้าฟังก์ชัน htmlspecialchars() เพื่อแปลง HTML Tag ให้เป็นตัวอักษรธรรมดา เช่น ใช้คำสั่ง $message = htmlspecialchars($message); ซึ่งผลลัพธ์ที่ได้จะเป็นดังรูป
จากรูป จะพบว่า < ถูกเปลี่ยนเป็น < และ > ถูกเปลี่ยนเป็น >
ข้อมูลเพิ่มเติม
http://en.wikipedia.org/wiki/Cross-site_scripting
http://www.cgisecurity.com/xss-faq.html
http://ha.ckers.org/xss.html
http://www.pantip.com/tech/developer/topic/DW3001981/DW3001981.html
http://www.cgisecurity.com/xss-faq.html
http://ha.ckers.org/xss.html
http://www.pantip.com/tech/developer/topic/DW3001981/DW3001981.html
3. Cross Site Request Forgery (CSRF)
คือการใช้เทคนิค Social Engineer เพื่อหลอกให้เหยื่อคลิกลิงก์ที่มี Request บางอย่าง ซึ่ง Request ที่ว่านี้ จะทำงานได้ก็ต่อเมื่อผู้ใช้คนนั้น Login เข้าสู่ระบบเรียบร้อยแล้ว เช่น เปลี่ยน emain, เปลี่ยน password, ฯลฯ โดยปกติแล้ว เมื่อผู้ใช้ Login ผ่าน และเข้าสู่ระบบเรียบร้อยแล้ว โปรแกรมส่วนใหญ่จะใช้การสร้าง SESSION หรือเก็บ Cookie เพื่อยืนยันตัวผู้ใช้ การส่ง Request อะไรซักอย่าง ในขณะที่ผู้ใช้กำลัง Login อยู่ ก็เหมือนกับว่าผู้ใช้เป็นคนส่ง Request นั้นไปเอง
ตัวอย่างการโจมตี
สมมุติเว็บไซต์ธนาคารแห่งหนึ่ง ผู้ใช้จะสามารถโอนเงินได้ก็ต่อเมื่อ Login เข้าระบบแล้ว และการโอนเงินต้องส่ง Request ไปที่ Server ดังนี้
ในตัวอย่างนี้ คือการโอนเงินไปที่บัญชีของ BOB เป็นจำนวน 100
ถ้า Maria ต้องการหลอก Alice ให้โอนเงินมาให้ตัวเอง ก็ต้องหลอกให้ Alice ส่ง Request มาที่ Server ดังนี้
ซึ่งโดยปกติแล้ว ถ้าในตอนที่ส่ง Request นั้น Alice ไม่ได้เข้าสู่ระบบอยู่ การโอนเงินก็จะไม่สามารถทำได้ ดังนั้น Maria ก็อาจจะส่ง E-Mail หลอกๆ ไปหา Alice โดยหวังว่าในตอนที่คลิกลิงก์นี้ Alice น่าจะกำลัง Login เข้าระบบธนาคารอยู่
<a href=”http://bank.com/transfer.do?acct=MARIA&amount=100000″>View my Pictures!</a>
ซึ่งถ้าในตอนนั้น Alice กำลังเข้าระบบธนาคารอยู่ และคลิกที่ลิงก์นี้เข้าจริงๆ ก็เท่ากับว่า Alice โอนเงินไปให้ Maria ไปเลย 10000
ถ้า Alice คลิกที่ลิงก์นี้ อาจจะไปเจอกับหน้าเว็บของธนาคาร ที่ขึ้นข้อความบอกว่า “โอนเงินเรียบร้อยแล้ว” Alice จะรู้ว่าถูกหลอกให้โอนเงิน ทำให้แผนแตกได้ ดังนั้น Maria ก็อาจจะส่ง E-Mail ที่มีโค้ดข้างล่างนี้
<img src=”http://bank.com/transfer.do?acct=MARIA&amount=100000″ alt=”” width=”1″ height=”1″ border=”0″ />
ซึ่งในโค้ดนี้ ก็คือการแทรกรูปภาพปลอมๆ เข้าไปในเมล ซึ่งเมื่อ Alice โหลดรูปภาพนี้ขึ้นมาดู ก็จะเป็นการส่ง Request ไปที่ Server บอกให้โอนเงิน โดยที่ Alice จะไม่เห็นข้อความบอกว่าโอนเงินเรียบร้อยแล้ว
การป้องกัน
ถ้าจะป้องกัน CSRF การเช็คแค่ HTTP Referer header นั้นป้องกันไม่ได้ เพราะ header สามารถถูกปลอมแปลงได้ เช่น ใช้ cURL หรือแม้กระทั่งการกำหนดให้ Request ที่เข้ามาเป็น POST อย่างเดียวก็ไม่สามารถป้องกันได้ เพราะถูกปลอมแปลงได้เช่นกัน ในการป้องกันที่ดีควรจะใช้การทำ Challenge/Response ในส่วน Request ที่สำคัญๆ
ข้อมูลเพิ่มเติม
http://en.wikipedia.org/wiki/Cross-site_request_forgery
https://www.owasp.org/index.php/Cross-Site_Request_Forgery
http://www.cgisecurity.com/csrf-faq.html
https://www.owasp.org/index.php/Cross-Site_Request_Forgery
http://www.cgisecurity.com/csrf-faq.html
4. Brute Force
การเจาะระบบโดยวิธี Brute Force เป็นการทดลอง Login โดยเดา Username หรือ Password แล้วส่งคำขอ Login ไปเรื่อยๆ จนกว่าจะสามารถเข้าสู่ระบบได้ ซึ่งการโจมตีสามารถทำได้ 2 แบบ คือ ใช้ฐานข้อมูล Password จาก Password Dictionary และทดลองสุ่ม Password ทุกตัวที่เป็นไปได้ (Brute Force) หรือจะใช้ทำวิธี Dictionary attack กับ Brute force attack คู่กันไปเลยก็ได้
ตัวอย่างการโจมตี
ใช้โปรแกรมสำหรับเจาะระบบ Login เช่น Hydra
การป้องกัน
ป้องกันการเดารหัสผ่าน เช่น กำหนดว่าถ้ามีการ Login ผิดเกินจำนวนครั้งที่กำหนด Account นั้นจะถูกระงั้บการใช้บริการ หรือถ้าทำไม่ได้ ก็ใช้วิธี Delay Login เช่น ถ้าใส่รหัสผิด ต้องรออีก 10 วินาทีถึงจะสามารถ Login ใหม่ได้ ซึ่งวิธีที่ 2 นี้สามารถลดความสามารถในการเดา Password ของโปรแกรมได้มาก เช่น ถ้าไม่ป้องกันเลย ใน 1 นาทีสามารถเดาได้ 1,000 รหัสผ่าน แต่ถ้าทำ Delay Login ไว้ 10 วินาที เวลา 1 นาทีก็สามารถเดาได้แค่ 6 รหัสผ่าน
ข้อมูลเพิ่มเติม
https://www.owasp.org/index.php/Testing_for_Brute_Force_%28OWASP-AT-004%29
http://www.symantec.com/connect/articles/password-crackers-ensuring-security-your-password
http://www.sillychicken.co.nz/Security/how-to-brute-force-http-forms-in-windows.html
http://www.symantec.com/connect/articles/password-crackers-ensuring-security-your-password
http://www.sillychicken.co.nz/Security/how-to-brute-force-http-forms-in-windows.html
5. File Upload
ในระบบที่อนุญาตให้ผู้ใช้ Upload File จำเป็นต้องมีการตรวจสอบไฟล์ที่รับเข้ามา ก่อนที่จะเอาไฟล์นั้นไปใช้ต่อ การตรวจสอบแค่ extension ของไฟล์อาจจะไม่เพียงพอ เพราะ hacker สามารถแทรกโค้ดมากับไฟล์หรือส่งไฟล์อันตรายๆเข้ามาในระบบได้
ตัวอย่างการโจมตี
ในระบบ Unix ผู้ใช้สามารถตั้งชื่อไฟล์แบบนี้ได้ <script>alert(‘xxx’)</script>.jpg ซึ่งถ้ามีเว็บไซต์ที่อนุญาตให้ Upload File แล้วแสดงรายชื่อไฟล์ที่ upload ออกมาทางหน้าเว็บ ก็สามารถถูกโจมตีด้วยวิธี XSS ได้
การป้องกัน
กำหนดไว้เลยว่าไฟล์ที่ผู้ใช้ Upload มานั้น ตรงกับที่อนุญาตให้ Upload หรือเปล่า ซึ่งอาจต้องมีการตรวจสอบหลายๆ ชั้น ไม่ว่าจะเป็น ตรวจสอบขนาดไฟล์,ชื่อไฟล์,ตรวจสอบ Content,กำหนดสิทธิ์ของไดเรคทอรี่ที่เก็บไฟล์ ว่าให้สามารถ Read และ Write ได้อย่างเดียว ไม่สามารถ Execute ได้ รวมถึงการสแกนไวรัสไฟล์ที่อัพโหลดเข้ามาด้วย ถ้าทำได้
ข้อมูลเพิ่มเติม
https://www.owasp.org/index.php/Unrestricted_File_Upload
http://blogs.securiteam.com/index.php/archives/1268
http://www.acunetix.com/websitesecurity/upload-forms-threat.htm
http://blogs.securiteam.com/index.php/archives/1268
http://www.acunetix.com/websitesecurity/upload-forms-threat.htm
ลองของจริง
สำหรับผู้ที่อยากลองเจาะระบบ Web Application ที่มีช่องโหว่ สามารถดาวนโหลดโปรแกรมที่ชื่อ Damn Vulnerable Web App มาลองเล่นได้ ซึ่งโปรแกรมนี้จะจำลองระบบที่มีช่องโหว่มาให้ ไม่ว่าจะเป็น SQL Injection, XSS สามารถดาวน์โหลดได้จาก http://www.dvwa.co.uk/ซึ่งนอกจากจะฝึกเจาะระบบได้แล้ว โปรแกรมนี้ยังมีตัวอย่าง Source Code ให้ศึกษาด้วย
ปล. ขอเตือนอีกรอบว่าบทความนี้มีจุดประสงค์เพื่อการศึกษาเท่านั้น ใครไปลองเจาะระบบของจริงก็ต้องรับผิดชอบเอาเองนะครับ
ที่มา : https://bigta.wordpress.com/2011/06/04/เจาะระบบ-web-app-phpmysql-และการป้องกั/
PASSWORD HASHING & INPUT FILTERING
บทความนี้เป็นภาคต่อของบทความที่แล้วครับ คราวนี้จะมาว่ากันด้วยเรื่องของ Password hashing และ Input Filtering ซึ่งก็เป็นอีกเรื่องนึงที่สำคัญในการรักษาความปลอดภัยของระบบ Web Application
Password hashing
จากข่าวการแฮ็กเว็บไซต์ต่างๆ ในช่วงที่ผ่านมา หลายคนคงได้ยินว่า บางเว็บไซต์เก็บข้อมูลรหัสผ่านไว้แบบ Plain text ก็คือไม่ได้เข้ารหัส ซึ่งทำให้ใครก็ตามที่เจาะเข้าระบบได้ก็สามารถเห็น Username, Password และข้อมูลสำคัญๆ ทุกอย่างได้หมดเลย ดังนั้นเพื่อเป็นการป้องกัน มาดูกันดีกว่าว่าเราจะเก็บข้อมูล Password ในฐานข้อมูลยังไงให้ปลอดภัย
ก่อนอื่นมาว่าด้วยเรื่องของการ Hash กันก่อน
การ Hash คือการแปลงแปลงชิ้นส่วนของข้อมูล (ไม่ว่าข้อมูลจะมีขนาดเล็กหรือขนาดใหญ่) ให้เป็นข้อมูลชิ้นเล็กๆ ที่มีความสัมพันธ์กับข้อมูลเดิม โดยผลที่ได้จะอยู่ในรูปของ String หรือ Integer
ซึ่งการ Hash จะเป็นการทำงานแบบ One-way คือ ไม่สามารถเอาผลลัพธ์ที่ได้ไปหาย้อนกลับว่าข้อมูลต้นฉบับก่อน Hash คืออะไร
ตัวอย่างฟังก์ชัน hash ที่นิยมใช้กัน คือฟังก์ชัน md5()
$data = “Hello World”;
$hash = md5($data);
echo $hash; // b10a8db164e0754105b7a99be72e3fe5
ผลลัพธ์ของ md5() จะได้ข้อมูล 128 bit หรือ 16 byte แต่เนื่องจากข้อมูลที่ได้เป็นเลขฐาน 16 (1 ตัวอักษรใช้ 4 bit) ดังนั้นค่าที่ได้จะเป็น string ความยาว 32 ตัวอักษร
ในระบบ Web Application เมื่อผู้ใช้ทำการลงทะเบียน ก็จะเอารหัสผ่านที่ผู้ใช้ตั้ง มาเข้าฟังก์ชัน hash แล้วค่อยเก็บลงฐานข้อมูล ดังนั้นรหัสผ่านที่อยู่ในฐานข้อมูลก็จะไม่ใช่รหัสผ่านจริง และเนื่องจากฟังก์ชัน hash เป็น one-way ดังนั้นจึงไม่มีทางรู้ได้เลยว่าผู้ใช้ตั้งรหัสผ่านว่ายังไง แม้แต่ admin ก็ไม่มีทางรู้ (ซึ่งจริงๆ แล้ว admin ก็ไม่มีสิทธิ์รู้รหัสผ่านของผู้ใช้) และเมื่อผู้ใช้ Login เข้าสู่ระบบ ก็จะเอารหัสผ่านที่ผู้ใช้ป้อน มาเข้าฟังก์ชัน hash เดียวกัน แล้วเอาค่าที่ได้มาตรวจดูว่าตรงกับค่าที่อยู่ในฐานข้อมูลมั้ย ถ้าตรงกันก็แปลว่าผู้ใช้ใส่รหัสผ่านถูกต้อง
ถ้าดูจากแนวคิดข้างบน ระบบนี้ก็น่าจะปลอดภัย แต่จริงๆ แล้ว มันก็มีปัญหาเหมือนกัน
ปัญหาที่ 1 : Hash Collision
เนื่องจากขนาดของ Output เป็นค่าคงที่ ดังนั้น จึงอาจเป็นไปได้ที่ Input ต่างกัน แต่ได้ Output มาเหมือนกัน ปัญหานี้จะเกิดขึ้นถ้าฟังก์ชัน hash ที่ใช้ ให้ output ออกมาเป็นค่าที่มีความยาวน้อยๆ เช่น ฟังก์ชัน crc32() จะให้ค่าออกมาเป็น integer 32 bit ซึ่งความยาวของ output จะจำกัดอยู่ที่ 9 ตัวอักษร ดังนั้น ถ้ามี hacker เจาะเข้าฐานข้อมูลได้ แล้วได้ hash ของ password ไป ก็สามารถเขียนโปรแกรมเพื่อสุ่มสร้าง password ที่ให้ output ออกมาตรงกับค่า hash ที่ได้ ซึ่ง password ที่สร้างขึ้นมา อาจไม่ใช่ password จริงที่ผู้ใช้ตั้งก็ได้
วิธีป้องกัน
ใช้ฟังก์ชัน hash ที่ให้ค่าความยาวของ output ไม่น้อยจนเกินไป เช่น ฟังก์ชัน sha1() จะให้ output ที่มีขนาด 160 bit (40 ตัวอักษร)
ปัญหาที่ 2 : Rainbow Tables
ตารางสีรุ้ง!!? … ไม่รู้จะแปลเป็นไทยว่าไงดี งั้นไม่แปลละกัน …
Rainbow Tables คือตารางขนาดใหญ่ที่สร้างขึ้นจากการ hash คำพื้นๆ ที่นิยมนำมาตั้งเป็นรหัสผ่าน ซึ่งตารางนี้อาจจะมีข้อมูลเป็นหลักล้านหรือหลักร้อยล้านแถวเลยก็ได้ สมมุติว่า hacker เจาะฐานข้อมูลได้ และได้ username กับ hash ของ password ไป ก็จะเอา hash ที่ได้มาเทียบกับค่า hash ใน rainbow table ถ้าเกิดในระบบมีผู้ใช้ที่ตั้งรหัสผ่านโดยใช้คำพื้นๆ hash นั้นก็จะไปตรงกับ hash ที่อยู่ใน rainbow table จากนั้นก็ … เสร็จโจร!
วิธีป้องกัน
… โรยเกลือ … แปลไม่เป็นอีกแล้วสิ … อธิบายง่ายๆ ก็คือ เราจะสร้าง salt ขึ้นมา ซึ่ง salt ที่ว่า ก็อาจจะเป็นตัวเลขหรือตัวอักษรอะไรก็ได้ใส่เข้าไปมั่วๆ เพื่อที่จะนำมา concatenate กับ password แล้วค่อยเอาไป hash ตัวอย่างโค้ด
$password = “easypassword”;echo sha1($password); // 6c94d3b42518febd4ad747801d50a8972022f956$salt = “f#@V)Hu^%Hgfds”;echo sha1($salt . $password); // cd56a16759623378628c0d9336af69b74d9d71a5
เท่านี้ก็ป้องกันได้แล้ว … รึเปล่า?
ปัญหาที่ 3 : Rainbow Tables (again)
… อีกแล้ว!!?
เนื่องจากว่า Rainbow Tables เนี่ย มันสามารถสร้างขึ้นมาใหม่ได้ ซึ่งถ้า hacker สามารถเข้ามาในระบบได้ แล้วรู้ว่า salt ที่ใช้คืออะไร (เช่น อ่านดูจากไฟล์ php) ก็สามารถสร้าง Rainbow Table ที่เกิดจากการเอา salt มาใส่กับรหัสผ่าน แล้วก็แกะรหัสผ่านได้อยู่ดี
วิธีป้องกัน
ใช้ “Unique Salt” คือ salt ของใครของมัน … ง่ายสุดก็เอา user_id นี่แหละมาทำเป็น salt ซะเลย
$hash = sha1($user_id . $password);
การ hash แบบนี้ทำได้ในกรณีที่ว่า user_id ไม่สามารถเปลี่ยนได้ ซึ่งก็ทำให้ hash แต่ละตัว มีค่า salt ของใครของมัน
ปัญหาที่ 4 : Hash Speed
ปัจจุบันนี้คอมพิวเตอร์ทำงานเร็วมาก ฟังก์ชัน hash ถูกออกแบบมาเพื่อให้สามารถคำนวนได้อย่างรวดเร็ว ดังนั้นจึงเป็นไปได้ที่ใน 1 วินาทีสามารถคำนวณ hash ได้เป็นพันล้านตัว โดยทั่วไปเราอาจคิดว่าการกำหนดให้รหัสผ่านมีความยาวอย่างน้อย 8 ตัวอักษร ก็น่าจะเพียงพอสำหรับการป้องกันการโจมตีแบบ Brute Force ได้ แต่จริงๆ แล้วไม่ใช่ ยกตัวอย่างเช่น
- ถ้ารหัสผ่านประกอบด้วยตัวอักษรตัวพิมพ์เล็ก, ตัวพิมพ์ใหญ่ และตัวเลข ก็จะมีจำนวนอักขระที่สามารถใช้งานได้คือ 62 ตัว (26+26+10)
- String ที่มีความยาว 8 ตัวอักษร ก็มีความเป็นไปได้คือ 62^8 ก็ประมาณ 218 ล้านๆ
- ถ้าสามารถคำนวณ hash ได้ 1 พันล้านตัวใน 1 วินาที (สมมุติว่าใช้คอมหลายๆ เครื่องช่วยกันคำนวณ) ก็จะใช้เวลาแค่ 60 ชั่วโมงในการแกะ
ไม่ต้องพูดถึงรหัสผ่านที่มีความยาวแค่ 6 ตัวอักษรที่นิยมใช้กัน ซึ่งในความยาวแค่นี้ สามารถแกะได้ด้วยเวลาแค่ 1 นาที ดังนั้น การกำหนดความยาวขั้นต่ำของรหัสผ่านไว้ที่ 9 ถึง 10 ตัวอักษร ก็เป็นเรื่องที่สมเหตุสมผล
วิธีป้องกัน
ใช้ฟังก์ชัน hash ที่คำนวณนานๆ เช่น สามารถคำนวณ hash ได้แค่ 1 ล้านตัวต่อวินาที แทนที่จะเป็น 100 ล้านตัว ก็จะเพิ่มเวลาที่ hacker ใช้ในการแกะรหัสผ่านเป็น 1000 เท่า จากเดิม 60 ชั่วโมง ก็จะกลายเป็น 7 ปี
ใน PHP จะมีฟังก์ชัน cryp() ซึ่งใช้อัลกอรึทึม BLOWFISH ในการเข้ารหัส สามารถศึกษาวิธีการใช้งานได้ที่http://www.w3schools.com/php/func_string_crypt.asp
จะเห็นได้ว่า ในเรื่องของการรักษาความปลอดภัยนั้น ความร่วมมือของผู้ใช้ก็มีส่วนด้วย ดังนั้น ถ้าเป็นไปได้ ก็ควรกำหนดให้ผู้ใช้ตั้งรหัสผ่านที่มีความยาว 8 ตัวอักษรขึ้นไป และในรหัสผ่านนั้นต้องประกอบด้วยตัวอักษรตัวพิมพ์เล็ก, ตัวพิมพ์ใหญ่, ตัวเลข หรือมีอักขระพิเศษเข้าไปด้วยก็จะยิ่งดี บางระบบสามารถตั้งรหัสผ่านเป็นภาษาไทยได้ด้วย ซึ่งก็ยิ่งเพิ่มความปลอดภัยมากขึ้นไปอีก
ในกรณีที่ผู้ใช้ลืมรหัสผ่าน มีกรณีเดียวเท่านั้นคือ ต้อง Reset password และกำหนดรหัสผ่านใหม่ ซึ่งการ Reset รหัสผ่าน ก็จะใช้วิธีสร้าง One-time password ขึ้นมา เป็นรหัสผ่านที่ใช้เข้าระบบได้แค่ครั้งเดียว คือเข้ามาเพื่อกำหนดรหัสผ่านใหม่เท่านั้น
… สุดท้าย อย่างที่บอก ว่ารหัสผ่านที่เก็บในฐานข้อมูลนั้นต้องเป็นความลับ และต้องไม่มีทางหาย้อนกลับได้ว่ารหัสผ่านจริงๆ คือคำว่าอะไร ดังนั้น ถ้าเข้าเว็บไซต์ไหน ที่เมื่อเวลากด “Forget Password” แล้วมันสามารถบอกรหัสผ่านเดิมของเราได้ ก็มั่นใจได้เลยว่าถ้าเว็บไซต์นี้โดนเจาะ … ซวยแน่
เรียบเรียงจาก
http://net.tutsplus.com/tutorials/php/understanding-hash-functions-and-keeping-passwords-safe/
http://net.tutsplus.com/tutorials/php/understanding-hash-functions-and-keeping-passwords-safe/
ข้อมูลเพิ่มเติม
http://www.blognone.com/news/24093
http://en.wikipedia.org/wiki/Rainbow_table
http://en.wikipedia.org/wiki/Salt_%28cryptography%29
http://www.blognone.com/news/24093
http://en.wikipedia.org/wiki/Rainbow_table
http://en.wikipedia.org/wiki/Salt_%28cryptography%29
Input Filtering
จากบทความก่อน ผมก็ลืมเรื่องที่สำคัญอีกเรื่องนึง คือการตรวจสอบ Input ที่รับเข้ามาจากผู้ใช้ ซึ่งเรื่องนี้ก็เป็นเรื่องใหญ่อีกเช่นกัน เนื่องจาก Input ทุกอย่างที่รับเข้ามาจากผู้ใช้ สามารถถูกสร้างขึ้นได้เองหรือปลอมแปลงมาโดยไม่ได้ผ่าน Interface ของเว็บเราก็ได้ ซึ่งถ้าไม่มีการตรวจสอบข้อมูลที่รับเข้ามา แล้วเอามาประมวลผลเลย อย่างนี้ไม่ดีแน่
สิ่งที่ต้องจำ คือ “ห้ามเชื่อข้อมูลทุกอย่างที่มาจากแหล่งข้อมูลภายนอก” ไม่ว่าจะเป็น
- ข้อมูลจาก form
- ข้อมูลจาก $_GET, $_POST, $_REQUEST
- คุกกี้ $_COOKIES
- ข้อมูลจาก Web service
- ไฟล์
- ตัวแปรบางอย่างจาก Server (เช่น $_SERVER[‘SERVER_NAME’])
- ตัวแปร Environment
- ค่าที่ได้จากการ Query ฐานข้อมูล
- ฯลฯ
ข้อมูลทุกอย่างที่รับเข้ามา ต้องผ่านการกรอง (Filter) ก่อนนำมาใช้
Filter มีอยู่ด้วยกัน 2 ลักษณะ
1. Sanitizing filters
- อนุญาตหรือไม่อนุญาตให้มีตัวอักษรที่กำหนดอยู่ใน string
- Return ค่าเป็น String
ตัวอย่าง
FILTER_SANITIZE_SPECIAL_CHARS ตัด HTML escape character (เช่น ‘ ” < > &) ออกจาก string
FILTER_SANITIZE_URL ตัดอักขระอื่นที่ไม่ใช่ตัวอักษร,ตัวเลข และไม่ใช่ $-_.+!*'(),{}|\\^~[]`<>#%”;/?:@&=
FILTER_SANITIZE_URL ตัดอักขระอื่นที่ไม่ใช่ตัวอักษร,ตัวเลข และไม่ใช่ $-_.+!*'(),{}|\\^~[]`<>#%”;/?:@&=
2. Logical filters
- Return ค่าเป็น TRUE หรือ FALSE
ตัวอย่าง
FILTER_VALIDATE_EMAIL ตรวจสอบว่าค่าที่รับเข้ามาอยู่ในรูปแบบของ e-mail หรือเปล่า
FILTER_VALIDATE_INT ตรวจสอบค่าที่รับเข้ามาว่าเป็นชนิด Int หรือเปล่า
FILTER_VALIDATE_INT ตรวจสอบค่าที่รับเข้ามาว่าเป็นชนิด Int หรือเปล่า
ตัวอย่างการใช้งาน filter เพื่อตรวจสอบค่าที่รับเข้ามาว่าอยู่ในรูปแบบของ email หรือเปล่า
if (isset($_REQUEST[’email’])) {
if (!is_string($_REQUEST[’email’])) {
break;
}
$email = filter_input(INPUT_POST, ’email’, FILTER_VALIDATE_EMAIL);
}
ที่มา : https://bigta.wordpress.com/2011/06/12/php-password-hashing-input-filtering/
Subscribe to:
Comments (Atom)









