# การเก็บรหัสผ่านที่ถูกต้อง

ในบทความนี้เราจะมาดูกันว่า การเก็บความลับไว้ในฐานข้อมูลนั้น **ขั้นต่ำ** ที่เรียกว่าปลอดภัยจริงๆมันเป็นยังไง ซึ่งในบทความนี้จะไม่ได้ลงรายละเอียดว่าต้องทำยังไงบ้าง แต่จะให้เข้าใจแนวคิดพื้นฐานในการเพิ่มความปลอดภัยให้กับระบบนะฮ๊าฟ

{% hint style="info" %}
**แนะนำให้อ่าน**\
บทความนี้เป็นส่วนหนึ่งของคอร์ส [👦 Security พื้นฐาน](https://saladpuk.gitbook.io/learn/basic/security101) ถ้าเพื่อนๆสนใจอยากรู้หลักในการรักษาความปลอดภัยตั้งแต่เริ่มต้นเลย ก็กดอ่านที่ตัวสีฟ้าๆได้เลยนะ
{% endhint %}

## 🦉 นิทานเรื่องที่สอง

เว็บไซต์ **เฟสปาล์ม** ได้เปิดตัวบริการ Social Media ที่สั่นสะท้านวงการ ทำให้มีคนนับล้านๆคนเข้าไปใช้บริการอย่างล้นหลา จนมียอดผู้ใช้ทะลุหลัก 1,000 ล้านคนภายในเวลาไม่นาน จนกระทั่งวันหนึ่งก็มีข่าวช๊อกโลก เพราะมีคนจับได้ว่าเฟสปาล์มได้ทำการขายข้อมูลผู้ใช้ให้กับบริษัทต่างๆ เลยทำให้ Hacker ทั่วโลกโกรธเกรี้ยวบริษัทเฟสปาล์มเป็นอันมาก และหนึ่งในบรรดา Hacker ก็เจาะเข้าระบบฐานข้อมูล ดึงข้อมูลผู้ใช้ออกมาได้ส่วนนึง ทำให้มีผู้ใช้หลายล้านรายที่ตกเป็นเหยื่อ เพราะถูกล่วงรู้ รหัสผ่าน และแม้กระทั่งข้อมูลบัตรเครดิตนั่นเอง

ดังนั้นบริษัทเฟสปาล์มเลยต้องมาวางแผนรับมือไม่ให้เกิดปัญหาที่ว่ามานี้อีก โดยสิ่งที่แรกเอามาดูก็คือ **ข้อมูลผู้ใช้ที่อยู่ในฐานข้อมูล** มันเป็นประมาณนี้

| Id | Username | Password |
| -- | -------- | -------- |
| 1  | saladpuk | 1234     |
| 2  | thaksin  | homesick |
| 3  | prayut   | 1234     |

เจ้าหน้าที่รักษาความปลอดภัยที่ชื่อ **ยิน** เห็นแล้วได้ถึงกับตกใจแล้วพูดออกมาว่า

### 🔥 ห้ามเก็บความลับไว้โดยไม่เข้ารหัสนะเฟร้ย !!

พูดไม่ทันขาดคำ เจ้าหน้าที่รักษาความปลอดภัยที่อยู่ข้างๆที่ชื่อ **วอดก้า** ก็พูดแทรกขึ้นมาว่า มันจำเป็นต้องเก็บเป็น plain text ไว้ เพราะเวลาลูกค้าโทรมาบอกว่าลืมรหัสผ่าน เราก็จะได้เอารหัสผ่านให้เขายังไงล่ะ

**ยิน** ได้ฟังคำตอบก็ถึงกับทรงกริ้วและบอกว่า ก็นี่ยังไงล่ะปัญหาที่เกิดขึ้น เพราะถ้ามีคนเจาะระบบเข้ามาได้ เขาก็จะได้ข้อมูลทุกอย่างไปเลยแบบชิวๆอะดิ มันเหมือนกับเอ็งวางเงินสดไว้กลางบ้านเลยยังไงล่ะ ถ้าโจรเข้ามาในบ้านได้มันก็เปรมเลยเพราะไม่ต้องเสียเวลาเจาะตู้เซฟก็ฉกเงินออกไปได้เลยนั่นเอง ดังนั้นเราจะต้องแก้เรื่องนี้ซะ

แม้ **วอดก้า** จะหงุดหงิดได้แต่คิดในใจว่า "ทำไมเอ็งไม่ไปแก้ให้มันเจาะไม่ได้แทนฟระ?" แต่เขาก็ไม่ได้พูดออกมา ได้แต่ก้มหน้าเพิ่มให้ระบบต้องทำการเข้ารหัสตัว password ก่อนที่จะเอาไปเก็บลงฐานข้อมูล โดยใช้ **DES** ในการเข้ารหัส ซึ่งทำให้ฐานข้อมูลเป็นแบบนี้ (ผลลัพท์เป็นแบบสมมุตินะ)

| Id | Username | Password       |
| -- | -------- | -------------- |
| 1  | saladpuk | A1B2C3D4       |
| 2  | thaksin  | CF67D00569ACC1 |
| 3  | prayut   | A1B2C3D4       |

**ยิน** มองข้อมูลในฐานข้อมูลตาถลน พร้อมกับตะโกนบอก **วอดก้า** ไปว่า

### 🔥 การเข้ารหัสห้ามทำย้อนกลับได้นะเฟร้ย !!

**วอดก้า** งง เป็นไก่ตาแตกเพราะไม่รู้ว่าจะต้องเข้ารหัสยังไงดี เลยทำให้ **เบลม็อท** เข้ามาช่วยอธิบายให้ฟังเป็นข้อๆว่า **Data Encryption Standard (DES)** ที่ **วอดก้า** ใช้ในการเข้ารหัส มันเป็นรุ่นสมัยก่อนที่เขาไม่ใช้กันแล้ว (56-bits key) เพราะมันตั้งแต่ 1999 มันใช้เวลาแค่ 22 ชั่วโมงนิดๆก็สามารถถอดรหัสกลับมาได้แล้วยังไงล่ะ ดังนั้น**จงเลือก algorithm ที่เหมาะสม**กับมันด้วย ไม่ใช่ตะบี้ตะบันเจอใน Stackoverflow ตัวไหนก็จับมายัดใส่เลย ดังนั้นจงไปศึกษาเพิ่มซะ

### 🔥 ห้ามให้คนอื่นเดาได้ว่ารหัสผ่านแต่ละคนสั้นยาวต่างกัน

**เบลม็อท** ชี้ไปที่รหัสผ่านของเจ้า **saladpuk** กับ **thaksin** แล้วพูดว่า ดูปุ๊ปก็พอจะเดาได้ว่ารหัสผ่านของ saladpuk น่าจะสั้นกว่าของ thaksin ดังนั้นถ้าเขารู้รหัสผ่านของ thaksin เขาก็จะรู้แนวทางในการใส่รหัสผ่านของคนอื่น ที่สั้นหรือยาวกว่าของ thaksin นั่นเอง พูดจบ เบลม็อท ก็เปิดบทความของ **สลักผัก** ขึ้นมาให้อ่านเรื่องการใช้ **HASH** เพื่อแปลงข้อความให้มันมีความยาวเท่ากันเสมอ (ถ้าลืมไปแล้วหรืออยากทบทวนก็กดไปอ่านจากตรงนี้ซะ [การใช้ HASH function](https://saladpuk.gitbook.io/learn/basic/security101#undefined-3))

### 🔥 รหัสผ่านแม้ว่ามันจะเป็นอันเดียวกัน ก็ต้องห้ามให้มันเก็บเป็นค่าเดียวกัน

**เบลม็อท** ชี้ให้เห็นรหัสผ่านของเจ้า **saladpuk** กับ **prayut** ว่าผลลัพท์มันได้เป็นค่าเดียวกันเลย เพราะถ้าเก็บเป็นแบบนี้ เมื่อมีคนถอดรหัสของใครซักคนได้ เขาก็จะรู้รหัสผ่านของคนอื่นๆที่ใช้รหัสแบบเดียวกันทันทีเลยอะดิ ดังนั้นถ้าต้องการให้ **ความลับที่เป็นค่าเดียวกัน แต่ได้ผลลัพท์ต่างกัน** เราก็สามารถใส่ของมั่วๆเข้าไปในความลับนั้นก็ได้ โดยเราเรียกมันว่า **Salt** หรือ **Nonce** (number used only once) นั่นเอง

#### **Salt**

เป็นการ**สุ่มอะไรมั่วๆซักอย่างเข้าไปต่อกับสิ่งที่เราต้องการให้เป็นความลับ** เช่น รหัสผ่านของ saladpuk คือ 1234 เราก็อาจจะสร้าง salt ออกมาเป็น su!sC\* แล้วเอามันไปต่อท้ายหรือไว้ก่อนหน้าก็ได้เลือกเอาซักอย่าง เราก็จะได้ผลลัพท์ออกมาเป็น 1234su!sC\* (ในตัวอย่างคือเอาไปต่อท้าย) ซึ่งผู้ใช้แต่ละคนก็จะต้องมี salt เป็นคนละตัวกันด้วยนะ ดังนั้น รหัสผ้านของ prayut คือ 1234 เราก็จะสร้าง salt ออกมาอีกตัวสมมุติว่าเป็น A#$563d เราก็เอามันมาต่อท้ายก็จะออกมาเป็น 1234A#$563d ดังนั้นพอเอาค่าทั้ง 2 ตัวนี้มีผ่านการ HASH เราก็จะได้ผลลัพท์ที่ไม่เหมือนกันยังไงล่ะ ตามรูปเลย

| Id | Username | Password                         | Salt    |
| -- | -------- | -------------------------------- | ------- |
| 1  | saladpuk | fd4438f5b856bb685004b84221ba794b | su!sC\* |
| 2  | thaksin  | f679b30fde31dff10304dc357300c52b | $%Acdes |
| 3  | prayut   | 3d54cf99f817a21cd9fd2b7218e58fec | A#$563d |

ในการเก็บข้อมูล เราก็จะทำการ**เก็บ salt ที่เราสร้างให้กับผู้ใช้แต่ละคนเอาไว้ด้วย**นะ

### 🔥 การเข้ารหัสควรทำให้มันเสียเวลาในการถอดรหัส

**เบลม็อท** พูดต่อว่า ถ้าใครอยากจะถอดรหัสความลับต่อให้เป็นระบบตัวเองก็ตาม ก็ต้องเพิ่มความยากในการถอดรหัส ให้มันเสียเวลาในการถอดรหัสนานที่สุดเท่าที่จะทำได้ด้วย โดยหนึ่งในหลักการที่ช่วยได้คือการทำสิ่งที่เรียกว่า **Pepper** นั่นเอง

#### **Pepper**

หลักการเหมือนกับ salt เลยคือสุ่มของมั่วๆขึ้นมาซักตัว แต่จะ**สุ่มแค่ 1 ตัวอักษร** อาจจะเป็นตัวเล็กหรือตัวใหญ่ก็ได้ เพื่อเอาไปต่อกับความลับของเราให้มันยาวขึ้น เวลาที่เอาไป HASH มันจะได้ผลลัพท์ออกมาไม่เหมือนเพื่อนนั่นแต่ แต่จะต่างกันคือ **มันจะไม่เก็บค่า papper ไว้ที่ไหนอีกเลย** ดังนั้นเวลาที่เราจะตรวจสอบว่าผู้ใช้ ใส่รหัสผ่านถูกหรือเปล่า ระบบจะต้องเสียเวลาในการไปสุ่มตั้ง a-z และ A-Z ซึ่งถ้ามีผลลัพท์ไหนตรงกับรหัสผ่านที่เก็บไว้ ก็แสดงว่ารหัสผ่านที่ส่งเข้ามาถูกต้องนั่นเอง

{% hint style="info" %}
**Pepper**\
หน้าที่หลักของมันจริงๆคือ **ทำให้คนที่จะมาล้วงความลับจะต้องเสียเวลาในการถอดรหัสเพิ่มขึ้นอีกหลายเท่าตัว**นั้นเอง เพราะแม้กระทั่งระบบก็ยังไม่รู้เลยว่าเจ้า pepper ของผู้ใช้คนนั้นๆคืออะไรนั่นเอง
{% endhint %}

### 🔥 กระบวนการทั้งหมดไม่ควรทำบท disk

เป็นเรื่องสุดท้ายที่ **เบลม็อท** กล่าวทิ้งท้ายไว้ ซึ่ง **ยิน** ได้ช่วยอธิบายก่อนที่จะไม่มีบทพูดในเรื่องนี้ว่า ถ้าเราทำการเข้ารหัสหรือถอดรหัสโดยมีการบันทึกข้อมูลไว้ใน disk นั่นหมายความว่าถ้า disk ก้อนนั้นหลุดออกไป หรือ แม้กระทั่งคนภายในแอบเข้ามากู้ข้อมูลใน disk ออกไป นั่นก็หมายความว่าระบบก็ยังมีความเสี่ยงอยู่ดี ดังนั้น**ทุกสิ่งทุกอย่างในการเข้ารหัสถอดรหัส เราควรทำไว้ใน Memory เท่านั้น เพราะมันจะไม่ทิ้งหลักฐานอะไรไว้อีกเลย**

### 🔥 อย่าเขียนการเข้า-ถอดรหัสเอง !!

**ยิน** ได้กล่าวทิ้งท้ายไว้ พร้อมกับบอกว่า เอ็งจะมั่นใจได้ยังไงว่าของที่เขียนเองมันปลอดภัย ดังนั้นจงไปหา library ที่ใช้ในการจัดการเรื่องนี้โดยเฉพาะจะดีกว่า เพราะส่วนใหญ่เขาจะมีการตรวจสอบอยู่ตลอดเวลา และเมื่อไหร่ที่มีอัพเดทว่าตัวนี้ไม่เหมาะสมแล้ว หรืออะไรก็ตาม ส่วนใหญ่เขาก็จะอัพเดท library เพื่ออุดรูรั่วเหล่านั้น ทำให้ระบบของเราได้รับผลพลอยได้ไปด้วยนั่นเอง

## 🤔 ต้องทำขนาดนี้ด้วยเหรอ ?

ไม่ต้องทำก็ได้นะ แต่เป็นคุณเองจะอยากใช้เว็บที่เก็บข้อมูลบัตรเครดิตของคุณไว้ โดยที่มันทำแค่เพียงครึ่งนึงของที่ผมพูดหรือเปล่า? ถ้าไม่คำตอบก็ชัดเจนอยู่แล้วว่าทำไมต้องทำ ซึ่งในปัจจุบันของที่เหล่า hacker ใช้เป็นตัวพื้นฐานของเขาเลยคือ

### 🔥 Rainbow Table

เป็นตารางที่ถูกคำนวนไว้เรียบร้อยหมดแล้วว่า ถ้าผลลัพท์ในการ HASH เป็นแบบนี้ แสดงว่าข้อความที่เป็น plain text คืออะไรนั่นเอง ดังนั้นถ้าเราใช้ HASH กากๆ ที่มันตกยุกต์แล้ว เหล่า hacker แทบจะยิ้มตุ่ยเลยทีเดียว เพราะมันไม่ต่างกับการเก็บ plain text ไว้เลยยังไงล่ะ

### 🔥 Dictionary Attack

เป็นข้อมูลที่รวบรวม HASH รหัสผ่านที่คนชอบใช้กัน ดังนั้นถ้าเราใช้รหัสผ่านที่คนอื่นใช้เหมือนๆกับของเรา แล้วตัวระบบดันไปเก็บค่า hash แบบเดียวกันอีก ก็หวานหมูเลย

### 🔥 Bruteforce Attack

เป็นการพยายามหาคำตอบโดยแทยที่ทุกความเป็นไปได้ลงไป เช่น แทนค่าตั้งแต่ aaaaaaaa\~ZZZZZZZZZ ไรงี้ เพื่อให้ฟลุ๊คแล้วเจอผลลัพท์ที่ตรงกับ HASH ที่เก็บไว้นั่นเอง ซึ่งปัจจุบันตัว **PIN** ก็พึ่งเป็นข่าวว่าโดนโจมตีโดยใช้เจ้าตัวนี้อยู่ เพราะมันเป็นแค่ตัวเลขไม่เกิน 8 หลักไรงี้ไง

### 🔥 Computing Power

เมื่อหลายปีก่อน เครื่องถอดรหัสสามารถถอด HASH ได้ราวๆ 1,000,000,000 ล้านตัวต่อ 1 วินาที ดังนั้นไม่น่าแปลกใจเลยใช่ไหมว่าทำไมเราต้องทำให้รหัสผ่านมันต้องใช้เวลาให้ถอดรหัสนานที่สุดเท่าที่เป็นไปได้

## 🎯 บทสรุป

การเก็บรหัสผ่านไว้ในฐานข้อมูลจริงๆมีอีกหลายเรื่องที่อยากจะเล่าให้ฟัง ดังนั้นในบทความหน้าเดี๋ยวเรามาลองดูกันว่า แค่เรื่องการ **Login** เข้าสู่ระบบธรรมดาๆที่พบได้ทั่วไป เชื่อไหมว่าเบื้องหลังจริงๆมันไม่ได้ง่ายแบบนั้นนะถ้าเราต้องเอาเรื่อง security เข้ามาใช้ด้วย

จากนิทานที่เล่าให้ฟังไป เราก็จะพบว่าการเก็บความลับไว้ในระบบนั้น มันไม่ใช่ทำแบบขอไปทีแล้วมันก็จะจบนะ ดังนั้นขอสรุปคร่าวๆก่อนละกัน

* ห้ามเก็บความลับเป็น plain text
* แม้กระทั่งคนภายในของเราเองก็ต้องห้ามรู้รหัสลับต่างๆที่เก็บไว้ในฐานข้อมูล
* การถอดรหัสควรจะต้องใช้เวลาในการถอดรหัสนานที่สุดเท่าที่จะทำได้
* รหัสผ่านแม้จะเป็นรหัสเดียวกันก็ต้องเก็บลงในฐานข้อมูลเป็นคนละตัวกัน
* ห้ามให้ใครเดาได้ว่ารหัสผ่านแต่ละคนยาวสั้นต่างกัน
* การเข้ารหัสถอดรหัสต่างๆควรทำใน Memory ห้ามมาทำไว้บน disk
* ให้ใช้ Security Library ที่สากลเขาใช้ ดีกว่าเขียนเองเสมอ


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.saladpuk.com/basic/security101/secure-password.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
