Core Principles (หลักการพื้นฐาน)

บ่อยครั้ง เวลาที่หัวข้อเรื่อง security ของ microservice ถูกหยิบยกขึ้นมาพูดถึง คนมักจะเริ่มพูดถึงประเด็นทางเทคนิคที่ค่อนข้างซับซ้อน เช่น การใช้ JWT token หรือความจำเป็นของ mutual TLS (หัวข้อที่เราจะสำรวจต่อไปในบทนี้) แต่ปัญหาของ security คือคุณจะปลอดภัยได้แค่เท่ากับจุดที่อ่อนแอที่สุดของคุณเท่านั้น ลองเปรียบเทียบง่ายๆ ถ้าคุณต้องการรักษาความปลอดภัยให้บ้านของคุณ การทุ่มเทความพยายามทั้งหมดไปกับประตูหน้าที่ทนทานต่อการงัดแงะ พร้อมไฟและกล้องวงจรปิดเพื่อขู่ผู้ไม่หวังดี แต่กลับปล่อยให้ประตูหลังเปิดอยู่ ก็ถือเป็นความผิดพลาด

ดังนั้นจึงมีประเด็นพื้นฐานบางอย่างของ application security ที่เราจำเป็นต้องพิจารณา แม้จะเพียงสั้นๆ ก็ตาม เพื่อชี้ให้เห็นถึงประเด็นมากมายที่คุณต้องตระหนัก เราจะดูว่าประเด็นหลักๆ เหล่านี้ซับซ้อนขึ้น (หรือน้อยลง) อย่างไรในบริบทของ microservices แต่ประเด็นเหล่านี้ก็ควรนำไปใช้ได้กับการพัฒนาซอฟต์แวร์โดยทั่วไปด้วยเช่นกัน สำหรับใครที่อยากข้ามไปดู “ของดี” ทั้งหมดเลย ก็ขอให้แน่ใจว่าคุณจะไม่โฟกัสกับการรักษาความปลอดภัยประตูหน้ามากเกินไปจนลืมว่าประตูหลังยังเปิดอยู่

Principle of Least Privilege (หลักการให้สิทธิ์น้อยที่สุดเท่าที่จำเป็น)

เมื่อ เราให้สิทธิ์การเข้าถึงแอปพลิเคชันแก่บุคคล ระบบภายนอกหรือภายใน หรือแม้แต่ microservice ของเราเอง เราควรใส่ใจอย่างระมัดระวังว่าเราให้สิทธิ์อะไรไปบ้าง หลักการ least privilege อธิบายแนวคิดที่ว่า เมื่อเราให้สิทธิ์เข้าถึง เราควรให้สิทธิ์ขั้นต่ำสุดเท่าที่ฝ่ายนั้นต้องการเพื่อทำงานตามที่จำเป็น และให้เฉพาะในช่วงเวลาที่พวกเขาต้องการเท่านั้น ประโยชน์หลักของแนวทางนี้คือการรับประกันว่าหาก credentials ถูกขโมยไปโดยผู้โจมตี credentials เหล่านั้นจะให้สิทธิ์การเข้าถึงแก่ฝ่ายไม่หวังดีในขอบเขตที่จำกัดที่สุดเท่าที่จะเป็นไปได้

ถ้า microservice มีสิทธิ์เข้าถึงฐานข้อมูลแบบอ่านอย่างเดียว (read-only) ผู้โจมตีที่ได้ credentials ของฐานข้อมูลนั้นไปก็จะได้แค่สิทธิ์อ่านอย่างเดียว และเข้าถึงได้แค่ฐานข้อมูลนั้นเท่านั้น ถ้า credential ของฐานข้อมูลหมดอายุก่อนที่จะถูกขโมยไปใช้ credential นั้นก็จะไม่มีประโยชน์อีกต่อไป แนวคิดนี้สามารถขยายไปถึงการจำกัดว่า microservice ใดสามารถสื่อสารกับฝ่ายใดได้บ้าง

อย่างที่เราจะเห็นต่อไปในบทนี้ หลักการ least privilege สามารถขยายไปถึงการทำให้ access control ถูกให้สิทธิ์เฉพาะในช่วงเวลาที่จำกัดด้วยเช่นกัน ซึ่งจะช่วยจำกัดผลเสียที่อาจเกิดขึ้นได้มากขึ้นไปอีกในกรณีที่เกิดการถูกโจมตี

Defense in Depth (การป้องกันแบบหลายชั้น)

ที่ UK ซึ่งเป็นที่ที่ผมอาศัยอยู่ เต็มไปด้วยปราสาทมากมาย ปราสาทเหล่านี้เป็นเครื่องเตือนใจส่วนหนึ่งถึงประวัติศาสตร์ของประเทศเรา (อย่างน้อยก็เป็นเครื่องเตือนใจถึงยุคก่อนที่ United Kingdom จะรวมกันเป็นหนึ่งเดียวในระดับหนึ่ง) มันเตือนเราถึงยุคที่ผู้คนรู้สึกว่าจำเป็นต้องปกป้องทรัพย์สินของตนจากศัตรู บางครั้งศัตรูที่พวกเขาคิดว่าจะมาก็แตกต่างกันไป—ปราสาทหลายแห่งใกล้บ้านผมใน Kent ถูกออกแบบมาเพื่อป้องกันการรุกรานทางชายฝั่งจากฝรั่งเศส 1 ไม่ว่าเหตุผลจะเป็นอะไร ปราสาทก็เป็นตัวอย่างที่ดีของหลักการ defense in depth

การมีกลไกป้องกันเพียงชั้นเดียวถือเป็นปัญหา หากผู้โจมตีหาทางเจาะทะลุแนวป้องกันนั้นได้ หรือหากกลไกป้องกันนั้นป้องกันได้เฉพาะผู้โจมตีบางประเภทเท่านั้น ลองนึกถึงป้อมปราการชายฝั่งที่มีกำแพงหันหน้าเข้าหาทะเลด้านเดียว ซึ่งจะไม่มีการป้องกันเลยหากถูกโจมตีทางบก ถ้าคุณไปดู Dover Castle ที่อยู่ใกล้บ้านผมมาก จะเห็นการป้องกันหลายชั้นได้อย่างชัดเจน ประการแรก ปราสาทตั้งอยู่บนเนินเขาขนาดใหญ่ ทำให้การเข้าใกล้ปราสาททางบกเป็นเรื่องยาก ปราสาทมีกำแพงไม่ใช่แค่ชั้นเดียวแต่สองชั้น—การเจาะกำแพงชั้นแรกได้ ผู้โจมตียังต้องรับมือกับกำแพงชั้นที่สองอีก และเมื่อผ่านกำแพงชั้นสุดท้ายไปได้ ก็ยังมี keep (หอคอยหลัก) ขนาดใหญ่ตระหง่านรออยู่

หลักการเดียวกันนี้ต้องนำมาใช้เมื่อเราสร้างการป้องกันเข้าไปใน application security ของเรา การมีการป้องกันหลายชั้นเพื่อรับมือกับผู้โจมตีถือเป็นสิ่งสำคัญมาก ในสถาปัตยกรรม microservice เรามีจุดต่างๆ มากมายขึ้นที่เราสามารถป้องกันระบบของเราได้ การแยกฟังก์ชันการทำงานออกเป็น microservice ต่างๆ และจำกัดขอบเขตของสิ่งที่ microservice เหล่านั้นทำได้ ก็เท่ากับว่าเรากำลังใช้หลักการ defense in depth อยู่แล้ว เรายังสามารถรัน microservice บน network segment ที่แตกต่างกัน ใช้การป้องกันระดับเครือข่ายในหลายๆ จุดมากขึ้น และแม้แต่ผสมผสานเทคโนโลยีที่หลากหลายในการสร้างและรัน microservice เหล่านี้ เพื่อให้ zero-day exploit เพียงตัวเดียวไม่สามารถส่งผลกระทบต่อทุกอย่างที่เรามีได้

Microservices ให้ความสามารถในการทำ defense in depth ได้มากกว่าแอปพลิเคชันแบบ monolithic ที่เป็น single-process และด้วยเหตุนี้จึงช่วยให้องค์กรสร้างระบบที่ปลอดภัยยิ่งขึ้นได้

Types of Security Controls (ประเภทของ security control)

เมื่อ พิจารณา security control ที่เราอาจนำมาใช้เพื่อรักษาความปลอดภัยของระบบ เราสามารถแบ่งประเภทได้ดังนี้: 2

Preventative (การป้องกันล่วงหน้า)

หยุด ไม่ให้การโจมตีเกิดขึ้น ซึ่งรวมถึงการเก็บ secret อย่างปลอดภัย การเข้ารหัสข้อมูลทั้งขณะพักและขณะเดินทาง และการติดตั้งกลไก authentication และ authorization ที่เหมาะสม

Detective (การตรวจจับ)

แจ้งเตือน คุณให้รู้ว่ามีการโจมตีกำลังเกิดขึ้นหรือเกิดขึ้นแล้ว application firewall และบริการตรวจจับการบุกรุก (intrusion detection) เป็นตัวอย่างที่ดี

Responsive (การตอบสนอง)

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

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

Automation (การทำงานอัตโนมัติ)

หนึ่งใน ธีมที่กลับมาซ้ำๆ ในหนังสือเล่มนี้คือเรื่อง automation เนื่องจากเรามีชิ้นส่วนที่เคลื่อนไหวได้มากขึ้นในสถาปัตยกรรม microservice automation จึงกลายเป็นกุญแจสำคัญที่ช่วยให้เราจัดการความซับซ้อนที่เพิ่มขึ้นของระบบได้ ในขณะเดียวกัน เราก็มุ่งไปสู่การเพิ่มความเร็วในการส่งมอบซอฟต์แวร์ และ automation ก็มีความสำคัญมากในเรื่องนี้ คอมพิวเตอร์ทำสิ่งเดิมซ้ำๆ ได้ดีกว่ามนุษย์มาก—ทำได้เร็วกว่าและมีประสิทธิภาพมากกว่า (และมีความแปรปรวนน้อยกว่าด้วย) มันยังช่วยลดความผิดพลาดของมนุษย์ และทำให้การนำหลักการ least privilege มาใช้ง่ายขึ้น—เราสามารถกำหนดสิทธิ์เฉพาะให้กับสคริปต์เฉพาะตัวได้ ยกตัวอย่างเช่น

อย่างที่เราจะเห็นตลอดทั้งบทนี้ automation ช่วยให้เรากู้คืนระบบได้หลังเกิดเหตุการณ์ไม่พึงประสงค์ เราสามารถใช้มันเพื่อ revoke และหมุนเวียน (rotate) security key รวมถึงใช้เครื่องมือช่วยตรวจจับปัญหา security ได้ง่ายขึ้น เช่นเดียวกับด้านอื่นๆ ของสถาปัตยกรรม microservice การนำวัฒนธรรมของ automation มาใช้จะช่วยคุณได้อย่างมากเมื่อพูดถึงเรื่อง security

Build Security into the Delivery Process (สร้าง Security เข้าไปในกระบวนการส่งมอบ)

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

ตลอด 20 ปีที่ผ่านมา เราได้เห็นปัญหาลักษณะเดียวกันนี้กับเรื่อง testing, usability และ operations ด้านเหล่านี้ของการส่งมอบซอฟต์แวร์มักถูกส่งมอบแบบแยกส่วน (siloed) และมักทำหลังจากโค้ดส่วนใหญ่เสร็จเรียบร้อยแล้ว อดีตเพื่อนร่วมงานของผมคนหนึ่งชื่อ Jonny Schneider เคยเปรียบเทียบแนวทางเรื่อง usability ของซอฟต์แวร์ว่าเป็นเหมือนกับทัศนคติแบบ “อยากสั่งเฟรนช์ฟรายส์เพิ่มไหม” พูดง่ายๆ คือ usability เป็นเรื่องที่คิดถึงทีหลัง—เป็นสิ่งที่คุณโรยหน้าไปบน “อาหารจานหลัก” 3

ความจริงก็คือ ซอฟต์แวร์ที่ใช้งานยาก ไม่ปลอดภัย ไม่สามารถทำงานได้ดีใน production และเต็มไปด้วยบั๊ก ไม่มีทางเป็น “อาหารจานหลัก” ได้เลยไม่ว่าจะมองแบบไหน—มันเป็นสินค้าที่มีข้อบกพร่องอย่างดีที่สุด เราเก่งขึ้นมากในการดึงเรื่อง testing เข้ามาอยู่ในกระบวนการส่งมอบหลัก เช่นเดียวกับที่เราทำกับเรื่อง operations (DevOps ใครๆ ก็รู้จัก) และ usability—security ก็ไม่ควรต่างออกไป เราจำเป็นต้องทำให้นักพัฒนามีความตระหนักรู้ทั่วไปเกี่ยวกับประเด็น security มากขึ้น ให้ผู้เชี่ยวชาญเฉพาะทางหาทางเข้าไปฝังตัวอยู่กับทีมส่งมอบเมื่อจำเป็น และให้เครื่องมือพัฒนาขึ้นเพื่อให้เราสามารถสร้างแนวคิดด้าน security เข้าไปในซอฟต์แวร์ของเราได้

สิ่งนี้อาจสร้างความท้าทายให้กับองค์กรที่นำแนวทาง stream-aligned team ที่มีความเป็นอิสระมากขึ้นในการเป็นเจ้าของ microservice ของตนเองมาใช้ แล้วบทบาทของผู้เชี่ยวชาญด้าน security จะเป็นอย่างไร? ใน “Enabling Teams” เราจะดูว่า ผู้เชี่ยวชาญเฉพาะทาง อย่างผู้เชี่ยวชาญด้าน security สามารถทำงานสนับสนุน stream-aligned team และช่วยให้เจ้าของ microservice สร้างแนวคิดด้าน security เข้าไปในซอฟต์แวร์ของตนได้อย่างไร แต่ก็ต้องแน่ใจด้วยว่าคุณมีความเชี่ยวชาญเชิงลึกที่เหมาะสมพร้อมใช้เมื่อคุณต้องการ

มี เครื่องมืออัตโนมัติที่สามารถตรวจสอบระบบของเราเพื่อหาช่องโหว่ เช่น การมองหาการโจมตีแบบ cross-site scripting Zed Attack Proxy (หรือ ZAP) เป็นตัวอย่างที่ดี ZAP ได้แรงบันดาลใจจากงานของ OWASP และพยายามจำลองการโจมตีที่เป็นอันตรายบนเว็บไซต์ของคุณ ยังมีเครื่องมืออื่นๆ ที่ใช้ static analysis เพื่อค้นหาข้อผิดพลาดในการเขียนโค้ดที่พบบ่อยซึ่งอาจเปิดช่องโหว่ด้าน security เช่น Brakeman สำหรับ Ruby นอกจากนี้ยังมีเครื่องมืออย่าง Snyk ซึ่งในบรรดาความสามารถอื่นๆ สามารถตรวจจับการพึ่งพา third-party library ที่มีช่องโหว่ที่รู้จักได้ด้วย เครื่องมือเหล่านี้สามารถผนวกเข้ากับ CI build ปกติได้อย่างง่ายดาย การนำมันเข้ามาเป็นส่วนหนึ่งของขั้นตอนตรวจสอบโค้ดมาตรฐานของคุณก็เป็นจุดเริ่มต้นที่ดีมาก แน่นอนว่าควรทราบไว้ว่าเครื่องมือเหล่านี้จำนวนมากสามารถจัดการได้แค่ปัญหาระดับเฉพาะจุดเท่านั้น—เช่น ช่องโหว่ในโค้ดชิ้นใดชิ้นหนึ่ง มันไม่สามารถทดแทนความจำเป็นในการทำความเข้าใจ security ของระบบคุณในระดับที่กว้างขึ้นและเป็นระบบมากขึ้นได้