การสร้าง Google Authenticator ด้วย Arduino, และการยืนยันตัวตนสองขั้นตอน

by lew
7 December 2014 - 15:05

กระบวนการยืนยันตัวตน (Authentication) เป็นปัญหาสำคัญในโครงสร้างความปลอดภัยคอมพิวเตอร์มาตั้งแต่เริ่มต้นยุคแรกๆ จนถึงทุกวันนี้ที่ปัญหาการขโมยตัวตนผู้ใช้ออนไลน์ยังเป็นปัญหาอยู่เรื่อยมา การยืนยันตัวตนที่ใช้งานเป็นวงกว้างทุกวันนี้คงเป็นรหัสผ่านที่มีคุณสมบัติการยืนยันตัวตนที่ดีพอสมควร เช่น เป็นความลับส่วนตัวกับเจ้าของตัวตน, และสามารถยกเลิกได้เมื่อความลับรั่วไหล แต่อย่างไรก็ตาม เราพบว่าผู้ใช้จำนวนมากมีแนวโน้มจะใช้รหัสผ่านอย่างไม่ปลอดภัย เช่น การใช้รหัสผ่านที่ง่ายต่อการคาดเดาจนเกินไป รวมถึงการใช้รหัสผ่านร่วมกันกับบริการอื่นๆ ทำให้รหัสผ่านตกอยู่ในความเสี่ยงเมื่อบริการใดบริการหนึ่งถูกเจาะจนข้อมูลรั่วไหล แฮกเกอร์มีแนวโน้มจะนำรหัสผ่านเหล่านั้นไปใช้งานกับบริการอื่นๆ ในทันที

กระบวนการยืนยันตัวตนแบบสองขั้นตอนออกแบบมาเพื่อเสริมความแข็งแกร่งของกระบวนการยืนยันตัวตนจากเดิมที่อาศัย "ความรู้" (รหัสผ่าน) ของผู้ใช้อย่างเดียว เพิ่มเติมว่าผู้ใช้ "มีอะไร" เพื่อยืนยันตัวตน โดยขั้นตอนที่สองที่เว็บต่างๆ ให้ความนิยมกันมากทุกวันนี้คือการนำข้อความยืนยันว่าผู้ใช้มีโทเค็นยืนยันตัวตน เช่น โทรศัพท์ อยู่กับตัว

โทเค็นยืนยันตัวตนในสมัยหนึ่งนั้นมักเป็นฮาร์ดแวร์เฉพาะบรรจุค่าความลับมาจากโรงงาน ตัวอย่างของโทเค็นกลุ่มนี้ เช่น ซิมการ์ดโทรศัพท์มือถือที่ใช้ยืนยันตัวตนของเจ้าของเลขหมายได้ หรือระบบยืนยันตัวตนในเซิร์ฟเวอร์ที่เป็นสินค้าสำคัญของบริษัท RSA ก็มักเป็น "พวกกุญแจ" มาจากโรงงาน การทำสำเนาทำได้ยาก แต่กระบวนการแจกจ่ายโทเค็นเหล่านี้ให้กับบริการที่มีคนใช้งานจำนวนมากๆ ก็เป็นเรื่องยากและค่าใช้จ่ายสูงเป็นอย่างยิ่ง โดยเฉพาะหากเราต้องมั่นใจว่ากระบวนการจัดส่งนั้นจะไม่ทำให้โทเค็นหลุดไปอยู่ในมือคนร้าย บริการใหม่ๆ เช่น กูเกิล, Digital Ocean, Amazon Web Service, ไปจนถึง Facebook จึงเลือกกระบวนการที่ราคาถูกและจัดการได้ง่ายกว่า โดยที่ยังเพิ่มความปลอดภัยให้กับผู้ใช้ได้อย่างมีประสิทธิภาพ นั่นคือการส่งความลับให้กับผู้ใช้เพื่อไปเก็บรักษาในอุปกรณ์ใดๆ เช่น โทรศัพท์มือถือ หากผู้ใช้สามารถยืนยันได้ว่ายังมีโทรศัพท์มือถืออยู่กับตัวก็น่าเชื่อได้ว่าผู้ใช้เป็นตัวจริง

สำหรับผู้ใช้ทั่วไปกระบวนการเหล่านี้คือการเอาโทรศัพท์มือถือถ่ายรูป QR code บนหน้าจอเท่านั้น บนแอพ Google Authenticator ก็จะแสดงตัวเลขหกหลักออกมาทุกๆ 30 วินาทีให้เรากรอกบนเว็บเพื่อยืนยันตัวตนอยู่เสมอ

กระบวนการสร้างตัวเลขนี้กูเกิลไม่ได้สร้างขึ้นมาเอง แต่มีการสร้างมาตรฐานออกเป็นสองมาตรฐาน เรียกว่า HOTP (An HMAC-Based One-Time Password Algorithm), TOTP (Time-Based One-Time Password Algorithm)

HOTP ใช้แล้วทิ้ง

เมื่อแรกเริ่มออกมาตรฐานการสร้างรหัสผ่านแบบใช้ครั้งเดียวแล้วทิ้ง เพื่อใช้ประกอบร่วมกับรหัสผ่านจากผู้ใช้นั้น กระบวนการออกแบบคือให้ผู้ใช้เก็บ ข้อความลับ (secret) เอาไว้ในอุปกรณ์ โดยอุปกรณ์ที่ว่านี้จะปล่อยตัวเลขออกมาใหม่ทุกครั้ง โดยตัวอุปกรณ์ยืนยันตัวตนจะนับจำนวนครั้งที่ผู้ใช้ และใช้ค่าจำนวนครั้งนั้นไปคำนวณค่ายืนยัน (Message Authentication Code - MAC) เพื่อใช้ยืนยันว่าผู้ใช้มีอุปกรณ์ที่เก็บความลับไว้จริง

ความได้เปรียบของ HOTP คืออุปกรณ์เองไม่ต้องการพลังงานในการทำงานใดๆ ทั้งสิ้นในขณะที่ไม่ได้ทำงาน ตัวอย่างสำคัญคือ YubiKey (ในภาพ) ที่จะทำงานเหมือนคีย์บอร์ดทุกครั้งที่ผู้ใช้แตะลงไปบนปุ่มสัมผัส แต่ตัวเลขที่พิมพ์ออกมานั้นจะต่างไปทุกครั้ง เพราะตัวกุญแจจำได้ว่าตัวเองปล่อยรหัสผ่านออกไปแล้วกี่ครั้ง

ชื่อ HOTP นั้นความสำคัญอยู่ที่กระบวนการ HMAC ที่เป็นการแฮชเพื่อส่งข้อมูลยืนยันโดยที่ไม่เปิดเผยว่ากุญแจลับที่ใช้ยืนยันข้อความนั้นเป็นอะไร HMAC (สำหรับคนที่อ่านเรื่องการแฮชมาแล้ว อาจจะสงสัยว่าทำไมจึงนำกุญแจลับไปต่อกับเนื้อหาเอกสารตรงๆ ไม่ได้ ใน Stack Exchange มีพูดคุยกันในประเด็นนี้)

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

TOTP มาตามนัด

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

TOTP ในโทรศัพท์มือถือมีปัญหาน้อยมาก เพราะโทรศัพท์มือถือมีแบตเตอรี่ตลอดเวลาอยู่แล้ว ขณะเดียวกันนาฬิกาในโทรศัพท์มือถือก็แม่นยำเป็นอย่างมากเพราะมีการตั้งนาฬิกาใหม่ทั้งจากเสาสัญญาณโทรศัพท์มือถือและเซิร์ฟเวอร์เทียบเวลาผ่านอินเทอร์เน็ต รวมไปถึง GPS

Google Authenticator ทั้งหมดคือ URI

เมื่อกูเกิลออกแอพพลิเคชั่น Google Authenticator มานั้น กูเกิลรองรับทั้ง HOTP และ TOTP โดยองค์องค์ประกอบสำคัญของ Google Authenticator คือการกำหนดมาตรฐาน URI สำหรับการส่งความลับจากกูเกิลเข้ามายังโทรศัพท์มือถือ เช่น

otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example

มาตรฐานนี้กำหนดส่ง ประเภทของ OTP, ชื่อบัญชี, ข้อความลับ, และชื่อหน่วยงาน ไปใน URI เดียวกัน ในการใช้งานจริงเรามักเห็น QR code จาก URI ก็จะได้ตามภาพ (สามารถเอาแอพ Google Authenticator สแกนได้จริง)

ข้อมูลอื่นๆ ที่จริงแล้วเป็นเพียงข้อมูลประกอบไว้แสดงให้ผู้ใช้เห็นเวลาที่มีหลายๆ บัญชีในแอพเดียวกันเท่านั้น แต่ข้อความที่เป็นความลับจริงๆ คือ secret ที่มีค่าเป็น "JBSWY3DPEHPK3PXP" ค่านี้กูเกิลกำหนดให้เข้ารหัสแบบ base32 เอาไว้ ก่อนใช้งานจะต้องถอดรหัส base32 ก่อนจึงนำไปใช้เป็นกุญแจสำหรับ HMAC-SHA1 โดยค่าตัวอย่างนี้เมื่อถอดรหัสแล้วจะได้ค่าเป็น "Hello!\xde\xad\xbe\xef"

Google Authenticator ยังกำหนดให้รหัสเปลี่ยนไปทุกๆ 30 วินาที โดยใช้ค่าเวลา epoch (เวลาเป็นวินาทีย้อนกลับไปถึงวันที่ 1 มกราคม 1970) แล้วมาหารด้วย 30 เมื่อค่านี้เปลี่ยนไป ค่า OTP ก็จะเปลี่ยนด้วย สุดท้ายคือกำหนดให้แสดงรหัสผ่าน 6 หลัก ซึ่งน่าจะทนทานเพียงพอ เพราะแฮกเกอร์อาจะต้องเดารหัสผ่านหลายแสนครั้งเพื่อให้ได้รหัสที่ถูกต้อง เซิร์ฟเวอร์โดยทั่วไปน่าจะทำให้การยิงทดสอบรหัสผ่านในระดับนี้ทำได้ยาก

RTC นาฬิกาแห่งคอมพิวเตอร์

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

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

ในกรณีของบทความนี้จะใช้ชิป DS1307 ซึ่งเป็นที่นิยมในกลุ่มนักพัฒนาที่ใช้บอร์ด Arduino เพราะมีไลบรารีรองรับอยู่แล้วจำนวนมาก ไลบรารีที่สำคัญในการเชื่อมต่อกับ DS1307 ได้แก่ Wire, DS1307, และ Time

การเชื่อมต่อ DS1307 เข้ากับบอร์ด Arduino เพียงแต่ต่อสาย VCC, GND, SCL เข้าไปยัง D2, และ SDA ไปยัง D3 การใช้งานที่เหลือก็สามารถใช้งานผ่านไลบรารีได้โดยไม่ต้องสนการเชื่อมต่อชั้นล่างๆ เช่น I2C

การตั้งเวลาใน RTC DS1307 สามารถตั้งจากตัวอย่างของไลบรารี SetTime ได้ทันที โดยตัวอย่างจะตั้งเวลาจากเวลาคอมไพล์และอัพโหลดซอฟต์แวร์ลง Arduino ทำให้เวลาต่างจากเวลาจริงเพียงเล็กน้อยเท่านั้น

การอ่านค่าเวลา epoch จาก DS1307 สามารถอ่านผ่านฟังก์ชั่น now() ได้ทันที แต่เนื่องจากเวลาเป็นค่าของประเทศไทยที่เร็วกว่าเวลา UTC อยู่ 7 ชั่วโมง ดังนั้นจึงต้องลบเวลาออก 25200 วินาทีเพื่อจะได้เวลาที่ UTC

AVR-Crypto-Lib เข้ารหัสบน Arduino

ไลบรารีที่มีอยู่ใน Arduino มีอยู่มากมาย แต่ข่าวร้ายคือ Arduino ไม่มีไลบรารีสำหรับการเข้ารหัสโดยตรงทำให้จำเป็นต้องพอร์ตไลบรารีมาด้วยตัวเอง สำหรับไลบรารีการคำนวณ HMAC-SHA1 นั้นมีผู้พัฒนาไว้อยู่แล้วในชื่อโครงการ AVR-Crypto-Lib ที่มีกระบวนการเข้ารหัสแบบต่างๆ มาให้พร้อมในตัวจำนวนมาก

เราสามารถแยกไลบรารีเฉพาะ HMAC-SHA1 มาใช้งานใน Arduino ได้เท่านั้นโดยแยกสี่ไฟล์ออกมาจากไลบรารี ได้แก่ sha1.c, sha1.h, hmac-sha1.c, และ hmac-sha1.h โดยทั้งสี่ไฟล์ต้องมีไฟล์ config.h, และ debug.h เราสามารถวางไฟล์เหล่านี้ว่างๆ เพื่อให้คอมไพล์ได้เลย ทั้งไลบรารี sha1 และ hmac-sha1 ควรสามารถคอมไพล์ได้จาก avr-gcc หากติดตั้งไว้ต่างหากในเครื่อง

ไลบรารีของ Arduino จะถูกจัดอยู่ในโฟลเดอร์โดยชื่อโฟลเดอร์จะต้องตรงกับชื่อไฟล์ภายใน เราสามารถสร้างโฟลเดอร์ crypto และสร้างไฟล์ crypto.cpp ให้ include ไฟล์ hmac-sha1.c และ sha1.c และไฟล์ crypto.h ให้ include ไฟล์ hmac-sha1.h และ sha1.h ตามลำดับ จากนั้นจึงใส่ import library จาก Arduino IDE

เมื่อ import ได้สำเร็จแล้วสามารถทดสอบคอมไพล์โดยโค้ดดังต่อไปนี้

เราสามารถคำนวณ HMAC ของข้อมูลชุดหนึ่งๆ ด้วยการกำหนดกุญแจลงในฟังก์ชั่น hmac_sha1_init และตามด้วยข้อมูลที่ต้องการยืนยัน (ในกรณีของ Google Authenticator คือค่าเวลา epoch/30) ลงในฟังก์ชั่น hmac_sha1_lastBlock โดยเรียกข้อมูลยืนยันว่า บล็อคสุดท้าย (lastBlock) เพราะ HMAC นั้นรองรับการยืนยันข้อมูลขนาดใหญ่มากๆ ได้ โดยข้อมูลชุดสุดท้ายจะเป็นบล็อคสุดท้าย แต่บล็อคของ HMAC-SHA1 นั้นใหญ่ถึง 512 บิต และเวลา epoch นั้น มีขนาด 64 บิตทำให้ใช้เพียงบล็อคเดียว

การเชื่อมต่อกับคอมพิวเตอร์

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

แต่หากเรามี Arduino Leonardo (ตัวที่ Blognone แจกให้ Writer ทุกท่าน) ตัว Arduino จะสามารถจำลองตัวเองเป็นคีย์บอร์ดได้ ทำให้เราสามารถวางเคอร์เซอร์ไว้ที่ช่องพิมพ์รหัสยืนยัน, เสียบบอร์ด Arduino เข้า USB, แล้วตัวเลขจะพิมพ์ลงไปโดยตรงทันที

แนวทางนี้ทำให้เราสามารถสร้างโทเค็นฮาร์ดแวร์เพื่อยืนยันตัวตนขั้นตอนที่สองโดยต้องการเพียงบอร์ด Arduino Leonardo และโมดูล RTC เท่านั้น

แนวทางการพัฒนาต่อ

ในโลกความเป็นจริงแล้วการใช้บอร์ด Arduino เพื่อให้พนักงานพกพาเอาไว้สำหรับการยืนยันตัวตนขั้นตอนที่สองคงเป็นเรื่องไม่สมเหตุสมผลนัก เนื่องจากบอร์ดมีขนาดใหญ่และใช้งานได้ยาก การทำเป็น dongle ขนาดเล็กอาจจะต้องการฮาร์ดแวร์ที่มีขนาดเล็กลงเช่น ps3ukey ที่ผมเคยเขียนถึงในโครงงานการเปลี่ยนภาษาด้วย Cap Lock แต่การดัดแปลงเพื่อติดตั้งชิป RTC ลงไปอาจจะเป็นเรื่องยุ่งยากพอสมควร

นอกจากปัญหาขนาดแล้ว Arduino เองยังมีปัญหา bootloader ของตัวเองที่กินเวลาในการบูตช่วงหนึ่ง กระบวนการนี้ทำให้โมดูลทำงานได้ช้าลง หากสามารถพัฒนารอมที่ตัดการทำงานของ bootloader ไปได้โครงการก็จะพร้อมใช้งานจริงได้ดีขึ้น

แนวทางพัฒนาสุดท้ายคือการรักษาความลับของค่ากุญแจที่โปรแกรมลงไปในตัวชิป AVR ก่อนการใช้งานจริง (โดยเฉพาะการใช้งานในวงกว้าง กับคนจำนวนมาก) ควรมีการศึกษาแนวทางป้องกันการอ่านค่ากุญแจกลับขึ้นมาเสียก่อน เพื่อให้มั่นใจได้ว่าโทเค็นนี้จะไม่สามารถทำสำเนาได้จริง

Blognone Jobs Premium