PHP Session และ Cookies บน Shared Hosting

ตั้งค่า Session ให้ปลอดภัย แก้ปัญหา Session หาย และเข้าใจความแตกต่างของ Session กับ Cookies

PHP Session และ Cookies บน Shared Hosting PHP

PHP Session คืออะไร และทำงานอย่างไร

PHP Session คือกลไกที่ช่วยให้เว็บแอปพลิเคชันจดจำข้อมูลของผู้ใช้ระหว่างการโหลดหน้าเว็บแต่ละครั้ง เนื่องจาก HTTP เป็น Stateless Protocol หมายความว่าเซิร์ฟเวอร์ไม่สามารถจดจำผู้ใช้ได้โดยอัตโนมัติ Session จึงเข้ามาแก้ปัญหานี้ด้วยการสร้างไฟล์ชั่วคราวบนเซิร์ฟเวอร์และส่ง Session ID กลับมาในรูปแบบ Cookie ให้กับเบราว์เซอร์ของผู้ใช้

บน Shared Hosting โดยทั่วไป PHP จะเก็บไฟล์ Session ไว้ในโฟลเดอร์ /tmp หรือโฟลเดอร์ที่กำหนดใน php.ini การจัดการ Session บน Shared Hosting มีข้อควรระวังหลายอย่าง โดยเฉพาะเรื่องความปลอดภัย เพราะผู้ใช้หลายรายอาจแชร์เซิร์ฟเวอร์เดียวกัน

ความแตกต่างระหว่าง Session และ Cookies

ก่อนเริ่มตั้งค่า ควรเข้าใจความแตกต่างระหว่าง Session และ Cookies เสียก่อน:

สำหรับข้อมูลสำคัญเช่น สถานะการล็อกอิน ตะกร้าสินค้า หรือสิทธิ์การเข้าถึง ควรใช้ Session เสมอ ไม่ใช่ Cookies

การเริ่มต้นใช้งาน PHP Session

การใช้งาน Session ใน PHP ทำได้ง่ายมาก เพียงเรียก session_start() ที่บรรทัดแรกของไฟล์ PHP ก่อนที่จะมีการ output ใดๆ รวมถึงช่องว่างหรือ HTML ด้วย

<?php
// เริ่มต้น Session - ต้องอยู่บรรทัดแรกสุดก่อน output ใดๆ
session_start();

// กำหนดค่าใน Session
$_SESSION['user_id'] = 123;
$_SESSION['username'] = 'somchai';
$_SESSION['role'] = 'admin';

// อ่านค่าจาก Session
if (isset($_SESSION['username'])) {
    echo "ยินดีต้อนรับ " . htmlspecialchars($_SESSION['username']);
}

// ลบค่าเฉพาะตัวแปรใน Session
unset($_SESSION['role']);

// ลบ Session ทั้งหมด (ใช้ตอน logout)
session_destroy();
?>

การตั้งค่า Session บน Shared Hosting ผ่าน cPanel

บน Shared Hosting สามารถปรับแต่งการตั้งค่า Session ได้หลายวิธี วิธีที่ง่ายที่สุดคือการแก้ไขผ่านไฟล์ .htaccess หรือ php.ini ซึ่งสามารถอัปโหลดได้ผ่าน File Manager ใน cPanel

ตั้งค่าผ่านไฟล์ .htaccess

สร้างหรือแก้ไขไฟล์ .htaccess ในโฟลเดอร์ public_html และเพิ่มการตั้งค่าต่อไปนี้:

# ตั้งค่า Session บน Shared Hosting ผ่าน .htaccess

# กำหนดเวลา Session หมดอายุ (วินาที) - 1800 = 30 นาที
php_value session.gc_maxlifetime 1800

# เปลี่ยนโฟลเดอร์เก็บ Session ให้ปลอดภัยกว่า /tmp
php_value session.save_path "/home/username/session_data"

# ป้องกัน Session Fixation
php_value session.use_strict_mode 1

# ป้องกัน XSS ผ่าน Session Cookie
php_value session.cookie_httponly 1

# บังคับใช้ HTTPS เท่านั้น (ถ้ามี SSL)
php_value session.cookie_secure 1

# ปิดการส่ง Session ID ผ่าน URL (ปลอดภัยกว่า)
php_value session.use_only_cookies 1

# ตั้งค่า SameSite Cookie เพื่อป้องกัน CSRF
php_value session.cookie_samesite "Strict"
สำคัญ: หากตั้งค่า session.save_path ให้ชี้ไปยังโฟลเดอร์ที่กำหนดเอง ต้องสร้างโฟลเดอร์นั้นก่อนผ่าน File Manager ใน cPanel และตรวจสอบว่า PHP มีสิทธิ์เขียนไฟล์ในโฟลเดอร์นั้น

ตั้งค่า Cookies อย่างปลอดภัยใน PHP

สำหรับ Cookies ที่สร้างเองด้วย PHP ควรใช้ฟังก์ชัน setcookie() พร้อมตั้งค่า Security Options ให้ครบถ้วน ตัวอย่างการสร้าง Cookie ที่ปลอดภัย:

<?php
// วิธีสร้าง Cookie ที่ปลอดภัย (PHP 7.3+)
$cookie_options = [
    'expires'  => time() + (86400 * 30),  // 30 วัน
    'path'     => '/',
    'domain'   => 'yoursite.com',
    'secure'   => true,    // ส่งผ่าน HTTPS เท่านั้น
    'httponly' => true,    // ป้องกัน JavaScript อ่าน Cookie
    'samesite' => 'Strict' // ป้องกัน CSRF
];

setcookie('user_pref', 'theme_dark', $cookie_options);

// อ่านค่า Cookie
if (isset($_COOKIE['user_pref'])) {
    $pref = htmlspecialchars($_COOKIE['user_pref']);
    echo "ธีมที่เลือก: " . $pref;
}

// ลบ Cookie โดยตั้ง expires ให้เป็นอดีต
setcookie('user_pref', '', [
    'expires'  => time() - 3600,
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
?>

การเพิ่มความปลอดภัยให้ Session บน Shared Hosting

บน Shared Hosting ความเสี่ยงหลักคือการที่ผู้ใช้รายอื่นอาจเข้าถึงไฟล์ Session ของเราได้หาก Session ถูกเก็บใน /tmp ร่วมกัน วิธีป้องกันที่ดีที่สุดคือ:

  1. เปลี่ยน Session Save Path ให้ชี้ไปยังโฟลเดอร์ภายใต้ home directory ของตัวเอง เช่น /home/username/session_data
  2. Regenerate Session ID ทุกครั้งที่มีการ login เพื่อป้องกัน Session Fixation Attack
  3. ตรวจสอบ User Agent เพื่อตรวจจับการขโมย Session
  4. กำหนดเวลาหมดอายุ ของ Session ให้สั้นเหมาะสม

ตัวอย่างโค้ดสำหรับการเริ่ม Session อย่างปลอดภัย ที่ควรใช้ทดแทน session_start() ธรรมดา:

<?php
function secure_session_start() {
    // กำหนดค่าก่อน session_start()
    ini_set('session.use_strict_mode', 1);
    ini_set('session.cookie_httponly', 1);
    ini_set('session.cookie_secure', 1);
    ini_set('session.use_only_cookies', 1);
    ini_set('session.cookie_samesite', 'Strict');
    ini_set('session.gc_maxlifetime', 1800); // 30 นาที

    session_start();

    // Regenerate Session ID เพื่อป้องกัน Session Fixation
    if (!isset($_SESSION['_initiated'])) {
        session_regenerate_id(true);
        $_SESSION['_initiated'] = true;
    }

    // ตรวจสอบ User Agent เพื่อป้องกันการขโมย Session
    if (isset($_SESSION['_ua'])) {
        if ($_SESSION['_ua'] !== md5($_SERVER['HTTP_USER_AGENT'])) {
            // User Agent เปลี่ยน - อาจถูกขโมย Session
            session_destroy();
            session_start();
            session_regenerate_id(true);
        }
    } else {
        $_SESSION['_ua'] = md5($_SERVER['HTTP_USER_AGENT']);
    }

    // ตรวจสอบเวลา Session ไม่ได้ใช้งาน
    if (isset($_SESSION['_last_activity'])) {
        if (time() - $_SESSION['_last_activity'] > 1800) {
            session_destroy();
            session_start();
        }
    }
    $_SESSION['_last_activity'] = time();
}

// ใช้งานแทน session_start()
secure_session_start();
?>

การตรวจสอบการตั้งค่า PHP Session ปัจจุบัน

ก่อนปรับแต่งใดๆ ควรตรวจสอบว่า PHP ของ Hosting ตั้งค่า Session ไว้อย่างไร สามารถทำได้โดยสร้างไฟล์ phpinfo.php ชั่วคราว แล้วค้นหาหัวข้อ session ใน cPanel หรือดูจากไฟล์ทดสอบง่ายๆ

หมายเหตุ: อย่าลืมลบไฟล์ phpinfo.php ออกจาก Hosting หลังจากตรวจสอบเสร็จแล้ว เพราะไฟล์นี้แสดงข้อมูลสำคัญของเซิร์ฟเวอร์

ตรวจสอบว่า PHP Session ทำงานถูกต้อง

สร้างไฟล์ทดสอบชื่อ session_test.php แล้วอัปโหลดไปยัง Hosting เพื่อตรวจสอบการทำงาน:

<?php
// ไฟล์: session_test.php - ลบทิ้งหลังใช้งาน!
session_start();

// 1. ตรวจสอบว่า session_start() ทำงานได้
echo "<h3>1. Session Status</h3>";
echo "Session ID: " . session_id() . "<br>";
echo "Session Status: " . session_status() . " (2=active)<br>";

// 2. ทดสอบเขียนและอ่าน Session
$_SESSION['test_value'] = 'hello_' . time();
echo "<h3>2. Write/Read Test</h3>";
echo "Written: " . $_SESSION['test_value'] . "<br>";

// 3. ตรวจสอบ Session Save Path
echo "<h3>3. Session Configuration</h3>";
echo "Save Path: " . ini_get('session.save_path') . "<br>";
echo "GC Maxlifetime: " . ini_get('session.gc_maxlifetime') . " seconds<br>";
echo "Cookie HTTPOnly: " . ini_get('session.cookie_httponly') . "<br>";
echo "Cookie Secure: " . ini_get('session.cookie_secure') . "<br>";
echo "Use Only Cookies: " . ini_get('session.use_only_cookies') . "<br>";
echo "Use Strict Mode: " . ini_get('session.use_strict_mode') . "<br>";

// 4. ตรวจสอบ Cookie ที่ถูกส่ง
echo "<h3>4. Session Cookie</h3>";
echo "Cookie Name: " . session_name() . "<br>";
print_r(session_get_cookie_params());

// 5. ตรวจสอบว่าโฟลเดอร์ Session เขียนได้
echo "<h3>5. Save Path Writable</h3>";
$save_path = ini_get('session.save_path');
echo is_writable($save_path) ? "Writable: YES" : "Writable: NO (อาจทำให้ Session ไม่ทำงาน)";
?>
Tip: หากเปิดไฟล์ทดสอบแล้วพบว่า "Use Strict Mode" และ "Cookie HTTPOnly" แสดงค่า 0 แสดงว่ายังไม่ได้ตั้งค่าความปลอดภัย ให้เพิ่มการตั้งค่าใน .htaccess ตามที่แนะนำข้างต้น และรีเฟรชหน้าเพื่อตรวจสอบอีกครั้ง อย่าลืมลบไฟล์ทดสอบออกหลังใช้งาน

แก้ปัญหาที่พบบ่อย

Error: "Warning: session_start(): Cannot send session cookie - headers already sent"

ข้อผิดพลาดนี้เกิดจากการมี output ก่อนเรียก session_start() ซึ่งอาจเป็นช่องว่าง บรรทัดว่าง หรือแม้แต่ BOM (Byte Order Mark) ที่ตัวแก้ไขโค้ดบางตัวเพิ่มโดยอัตโนมัติ

วิธีแก้: ตรวจสอบว่าบรรทัดแรกสุดของไฟล์คือ <?php โดยไม่มีช่องว่างหรืออักขระใดๆ ก่อนหน้า และ session_start() ต้องเป็นบรรทัดแรกที่ทำงาน ก่อน echo หรือ HTML ใดๆ หากยังพบปัญหา ลองเปิดใช้ Output Buffering:

<?php
ob_start(); // เปิด Output Buffer ก่อน
session_start();
// โค้ดที่เหลือ...
?>

Error: Session หายทุกครั้งที่เปิดหน้าใหม่

ปัญหานี้มักเกิดจาก Session Save Path ไม่มีสิทธิ์เขียน หรือ Cookie ถูกบล็อกโดยเบราว์เซอร์ ให้ตรวจสอบดังนี้:

Error: "Warning: session_start(): open(…) failed: No such file or directory"

ข้อผิดพลาดนี้เกิดเมื่อตั้งค่า session.save_path ชี้ไปยังโฟลเดอร์ที่ไม่มีอยู่ ให้สร้างโฟลเดอร์ผ่าน File Manager ใน cPanel ก่อน จากนั้นตั้งค่า Permission ของโฟลเดอร์นั้นเป็น 700 (เจ้าของอ่าน/เขียน/execute ได้เท่านั้น) เพื่อความปลอดภัย

Error: Session ไม่ทำงานเมื่อ Domain มี www และ non-www

หากเว็บเข้าได้ทั้ง www.yoursite.com และ yoursite.com Session Cookie อาจไม่ถูกแชร์ระหว่างสอง Domain นี้ วิธีแก้คือตั้งค่า session.cookie_domain หรือบังคับ redirect ไปยัง Domain เดียวผ่าน .htaccess และเพิ่มการตั้งค่าใน PHP:

<?php
// กำหนด Cookie Domain ให้รองรับทั้ง www และ non-www
ini_set('session.cookie_domain', '.yoursite.com'); // นำหน้าด้วย dot
session_start();
?>

Error: Session หมดอายุเร็วเกินไปบน Shared Hosting

บน Shared Hosting บางราย PHP มีการรัน Garbage Collection บ่อยมาก ทำให้ Session หมดอายุก่อนกำหนด วิธีแก้ที่ดีที่สุดคือเปลี่ยน Session Handler ให้เก็บข้อมูลในฐานข้อมูล MySQL แทน ซึ่งไม่ขึ้นกับ GC ของ PHP และจัดการได้ยืดหยุ่นกว่า


คำถามที่พบบ่อย

Q: Session และ Cookies มีขนาดข้อมูลที่เก็บได้ต่างกันไหม?

A: ใช่ Cookies มีขนาดจำกัดที่ประมาณ 4KB ต่อ Cookie และเบราว์เซอร์แต่ละตัวรับ Cookie ต่อ Domain ได้ไม่เกิน 20-50 รายการ ในขณะที่ Session เก็บข้อมูลบนเซิร์ฟเวอร์ ขนาดจึงขึ้นอยู่กับ Disk Space ของ Hosting เท่านั้น แต่ควรหลีกเลี่ยงการเก็บข้อมูลขนาดใหญ่ใน Session เพราะจะใช้หน่วยความจำมาก แนะนำให้เก็บเฉพาะ ID หรือ Key แล้วดึงข้อมูลจริงจากฐานข้อมูลแทน

Q: ต้องตั้งค่า session.cookie_secure = 1 ไหม ถ้าเว็บยังไม่มี SSL?

A: ไม่ควรตั้งค่า session.cookie_secure = 1 หากเว็บยังใช้ HTTP เพราะจะทำให้ Session ไม่ทำงานเลย เบราว์เซอร์จะปฏิเสธการตั้งค่า Cookie ที่มี Secure flag บน HTTP connection อย่างไรก็ตาม ทุกวันนี้ SSL ฟรีจาก Let's Encrypt ผ่าน cPanel ทำได้ง่ายมาก แนะนำให้เปิดใช้ SSL ก่อนแล้วค่อยเปิด cookie_secure

Q: ควรเก็บข้อมูลอะไรใน Session และอะไรใน Cookies?

A: ใน Session ควรเก็บข้อมูลที่ละเอียดอ่อน เช่น User ID, สิทธิ์การเข้าถึง, ตะกร้าสินค้า, Token ต่างๆ ส่วน Cookies เหมาะสำหรับข้อมูลที่ไม่ละเอียดอ่อนและต้องการให้คงอยู่หลังปิดเบราว์เซอร์ เช่น ภาษาที่ต้องการ, ธีม, การตั้งค่า UI ที่ไม่ส่งผลต่อความปลอดภัย หลักการง่ายๆ คือ ถ้าข้อมูลนั้นถูกขโมยแล้วเกิดความเสียหาย ให้เก็บใน Session เสมอ