การป้องกันความลับหลุดตอนจบ

🤔 การเก็บรหัสลับบนคลาว์ เขาทำกันยังไงนะ ?

😘 ทวนปัญหา

จากบทความที่ 3 เราจะพบว่า ความลับ ที่ใช้ใน Production Environment เราเอามันไป ซ่อนไว้บนคลาว์ ได้แล้วก็จริง แต่การที่เราจะให้แอพของเราไปอ่านความลับพวกนั้นมา เราก็ต้องมอบ ความลับ ที่ใช้ในการเชื่อมต่อเข้าสู่คลาว์ให้กับ Developer เห็นอยู่ดี ดังนั้นทุกอย่างมันเลยกลับมาที่จุดเริ่มต้นของปัญหา ดังนั้นเราไปทวนกันก่อนว่าเราต้องแก้ปัญหาเรื่องไยบ้าง

💔 รายการที่ต้องแก้

  • การผัง Secret ไว้เป็น Hard code ทำให้เราต้อง Build & Deploy โปรเจคใหม่เท่านั้น

  • ใน Source code ของมี Secret ติดเข้าไปใน commit เสมอ

  • Developer บางคนอาจจะรู้ Secret ซึ่งเขาอาจจะเป็นคนร้ายในอนาคต หรือ เป็นจุดที่อันตรายถ้าเขาเก็บมันไว้ไม่ดี

  • ถ้ามีคนที่มี access เข้าถึง service บนคลาว์ตัวนั้นๆได้ เขาก็สามารถเข้ามาดู secret เหล่านั้นได้เหมือนเดิม

  • ถ้าเราไม่ให้ access เข้าถึง service ตัวนั้นๆ เราก็จะเอาแอพขึ้นคลาว์ไม่ได้

ดูคร่าวๆแล้วเหมือนมันจะยาวเลยเน๊อะ แต่ในรอบนี้เรามี Infrastructure ที่ซ่อนความลับทุกอย่างไว้บนคลาว์เรียบร้อยละ เดี๋ยวมาดูกันว่าเราจะแก้ปัญหาทั้งหมดนี้ด้วยการจัดการเพียงครั้งเดียวได้ยังไงกันเรย

❤️ หัวใจหลัก

😈 ปัญหา

ปัญหาทั้งหมดทั้งมวลตั้งแต่บนแรกจนถึงบทนี้ + และหลายโปรเจคที่ส่วนใหญ่ทุกคนบนโลกทำกันแล้วมันเกิดปัญหาในการจัดการความลับคือ เราแยกความลับออกมาแล้วเราต้องจัดการมันเอง เช่น แยก Connection String, Access Token, Credential หรืออะไรก็ตามออกมาไว้ที่นึง แล้วสุดท้ายเวลาจะให้แอของเรา เช่น เว็บ มือถือ แอพบน PC เชื่อมต่อเข้าไป เราก็จะต้องบริหารจัดการเก็บความลับพวกนั้นเอง เช่นไปเข้ารหัสก่อนจะเอาลงในมือถือ หรืออะไรก็ตามแต่ ดังนั้นสรุปสั้นปัญหาเกิดจาก เราต้องจัดการเก็บความลับเอง นั่นเอง

😘 วิธีแก้ปัญหา

วิธีการแก้นั้นสุดแสนจะง่ายคือทำตรงข้ามกันโดย เราจะไม่จัดการเก็บความลับเอง นั่นเอง บร๊ะ!! ฟังดูง่ายดีนะ แล้วเราจะทำแบบนั้นได้ยังไง? ดังนั้นไปดูพระเอกของเรากันเลยดีกั่ว

🤠 จำกัดการเข้าถึงด้วย Azure Managed Identities

อย่างที่เกริ่นไว้ว่าเราจะไม่จัดการเรื่องการรักษาความลับเอง ซึ่งก็เข้าทางกับเจ้า Azure Managed Identities โดยเจ้าตัวนี้มีหลักการง่ายๆคือ เขาจะดูแลความลับให้เราเอง โดยจะยอมให้เฉพาะ service ที่เรากำหนดไว้เข้าถึงความลับได้เท่านั้น ส่วนใครก็ตามที่ไม่มีสิทธิ์ก็จะเข้าถึงความลับของเราไม่ได้นั่นเอง ดังนั้นเราไปดูวิธีการตั้งค่าเจ้านี้กันเลยดีกว่า

เข้าไปที่ตัว Web App Service ที่เราต้องการเข้าใช้งาน App Configuration (ที่ทำไว้ในบทความที่ 3) ขึ้นมา แล้วที่เมนูด้านซ้ายไปที่ Identity แล้วทำการเปิดใช้งานมัน แล้วก็กด Saveตามรูปด้านล่าง

ถัดมาเขาก็จะถามว่าจะเปิดใช้งานความสามารถนี้หรือเปล่าก็กด Yes ไปแซร๊ะ

เขาแจ้งเตือนเพราะว่าถ้าเราเปิดโหมดนี้และทำการ register เข้ากับ AAD (Azure Active Directory) แล้ว เจ้า service ตัวนี้มันจะเข้าถึงความลับต่างๆของเราได้นั่นเอง ซึ่งถ้าอ่านตรงนี้แล้วไม่เข้าใจก็ยังไม่ต้องกังวล เดี๋ยวเราจะมีอธิบายเรื่อง AAD อีกใบบนถัดๆไป

เพียงเท่านี้ก็เปิดการเปิดให้ตัว Web App Service ของเราพร้อมที่จะเข้าถึงความลับต่างๆของเราละ ตามรูปด้านล่าง

ถัดไปเราก็จะไปกำหนดว่าจะให้ Service ตัวไหนบ้างที่จะเข้าถึง ความลับ ที่อยู่ใน App Configuration ของเราได้บ้าง โดยการเปิด App Configuration Service ที่เราสร้างไว้ แล้วให้เข้าไปที่เมนู Access control (IAM) ที่อยู่ด้านซ้าย แล้วเราจะเจอหน้าตามรูปด้านล่าง

คราวนี้เราก็จะมากำหนดว่าจะให้ service ไหนบ้างที่จะเข้าถึงได้ โดยการกดปุ่ม Add แล้วเลือก Add role assignment ตามรูปด้านล่าง

สุดท้ายก็ทำการตั้งค่าต่างๆ ตามตารางด้านล่าง

ชื่อ

ความหมาย

Role

สิทธิ์ที่เราจะกำหนดให้

Assign access to

จะมอบ security principle รูปแบบไหนให้กับเจ้า role ที่เลือก

และสุดท้ายก็กดเลือก Service ของเราให้เรียบร้อยแล้วกดปุ่ม Save ตามรูปด้านล่าง

เพียงเท่านี้ก็เป็นอันเสร็จสิ้นว่า เราได้มอบสิทธิ์ให้กับ Web App Service ตัวที่เราเลือกไปตะกี้ ให้สามารถเข้าใช้งานตัว App Configuration Service ได้แล้วนั่นเอง

สุดท้ายเราก็จะแก้โค้ดของเรานิดหน่อยให้สามารถเข้าใช้งาน App Configuration Service ได้โดยที่ไม่ต้องระบุ Connection String ใดๆทั้งสิ้นเลย โดยที่ตัว source code ของเราก็ต้องติดตั้ง package ด้านล่างนี้เข้าไปเพิ่มด้วย

dotnet add package Azure.Identity --version 1.1.0

แล้วที่ไฟล์ Program.cs ก็ทำการเพิ่มโค้ดตัวนี้ลงไป

using Azure.Identity;
using Microsoft.Azure.Services.AppAuthentication;

และในไฟล์เดียวกัน ตรงเมธอด CreateHostBuilder ก็ทำการแก้ตามด้านล่างนี้ด้วย (เลือกใช้โค้ดตาม .net version ที่เราใช้ให้ถูกตัวนะ)

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                var settings = config.Build();
                config.AddAzureAppConfiguration(options =>
                    options.Connect(new Uri("Endpoint"),
                    new ManagedIdentityCredential()));
            })
            .UseStartup<Startup>();

จากโค้ดด้านบนก็จะเห็นข้อความที่ชื่อว่า Endpoint อยู่ชิมิ ซึ่งเจ้าตัวนี้คือตัว URL ของ App Configuration Service ที่เราจะเชื่อมต่อเข้าไปนั่นเอง โดยสามารถเข้าไปดูได้ที่เมนู Overview แล้วมันจะอยู่บนขวา ให้ copy มาแล้ววางใส่ได้เรย ตามรูปด้านล่าง

หลังจากที่ลองแก้โค้ดเสร็จเรียบร้อย ไหนเรามาลองเปิดเว็บของเราใน Localhost ดูหน่อยจิ๊ ซึ่งผลที่ได้คือ

Unhandled exception. Azure.Identity.CredentialUnavailableException:
No managed identity endpoint found.
...

เราก็จะพบ error ยาวเป็นหางว่าวตามด้านบนนั่นเอง ซึ่งใจความสำคัญของมันก็คือ ตัวเว็บของเราเชื่อมต่อไปเอาอ่านความลับที่อยู่ใน App Configuration ไม่ได้นั่นเอง ส่วนสาเหตุที่เกิดแบบนี้ขึ้นก็ถูกแล้ว เพราะเครื่องที่ใช้ Run website ตัวนี้ไม่มีสิทธิ์ที่จะเข้าถึง App Configuration Service ของเรายังไงล่ะ (ที่เราตั้งไว้ในขั้นตอนด้านบนไง)

ดังนั้นเราจะลองเอาตัวเว็บของเราไป Deploy ลงบนคลาว์แล้วลองเปิดดูกันบ้างนะ ซึ่งผลลัพท์ก็คือ

บนคลาว์สามารถใช้งานได้ตามปรกติ!! เพราะเราเปิดสิทธิ์ให้ตัว Service นี้สามารถเข้าถึง App Configuration ได้ยังไงล่ะ

🎯 สรุปคร่าวๆก่อนไปต่อ

ในตอนนี้สิ่งที่เราทำลงไปก็จะเห็นว่า ไม่มี Developer คนไหนสามารถเข้าถึงความลีบได้โดยตรงอีกต่อไปแล้ว เพราะตัวที่สามารถเข้าถึงความลับได้จะต้องเป็น Service ที่ทำงานอยู่บนคลาว์ และ มันจะต้องถูก Grant สิทธิ์ในการเข้าถึงด้วยเท่านั้นนั่นเอง จากตรงนี้ทำให้เรา ไม่ต้องแจกความลับของ Production Environment ให้ Developer อีกต่อไปแล้วนั่นเอง

ซึ่งสิ่งที่เราทำลงไปนั้น ไม่มีใครเห็นเคย Secret เลย ยกเว้นคนที่ทำ App Configuration (ถ้าหลุดก็เจ้านี่แหละคือคนร้าย) และ ไม่มี Secret ไปอยู่กับโค้ดด้วย ดังนั้นปัญหาทั้งหมดนั่นก็จะมะลายหายไปหมดเลยนั่นเอง

  • การผัง Secret ไว้เป็น Hard code ทำให้เราต้อง Build & Deploy โปรเจคใหม่เท่านั้น

  • ใน Source code ของมี Secret ติดเข้าไปใน commit เสมอ

  • Developer บางคนอาจจะรู้ Secret ซึ่งเขาอาจจะเป็นคนร้ายในอนาคต หรือ เป็นจุดที่อันตรายถ้าเขาเก็บมันไว้ไม่ดี

  • ถ้ามีคนที่มี access เข้าถึง service บนคลาว์ตัวนั้นๆได้ เขาก็สามารถเข้ามาดู secret เหล่านั้นได้เหมือนเดิม

  • ถ้าเราไม่ให้ access เข้าถึง service ตัวนั้นๆ เราก็จะเอาแอพขึ้นคลาว์ไม่ได้

เพราะทั้งหมดนี่เราใช้ Azure Managed Identities เข้ามาช่วยจำกัดสิทธิ์คนเข้าถึงความลับของเรานั่นเอง

😢 แล้วจะพัฒนาแอพต่อไง?

จากตัวอย่างที่ทำไปทั้งหมดจะเห็นว่า เราไม่สามารถเปิด website ของเราภายใน Localhost ของเราได้เลย เพราะมันไม่มีสิทธิ์เข้าถึงความลับ ซึ่งทวนความทรงจำกันหน่อยนึง

  1. ความลับของเราจะใช้ที่ไหน???? .... Production Environment ไง

  2. เราจะให้ Develope เข้าถึง Production Environment ทำไมไม? ... มีแค่แก้ bug เพียงอย่างเดียว

  3. ถ้าไม่แก้ bug แล้วจะพัฒนาแอพต่อทำไง? ... ไปทำที่ Development Environment จิ

จากคำถามคำตอบทั้ง 3 ข้อด้านบน สรุปสั้นๆแบบไม่เมากาวก็คือ เราไม่ควรให้ Developer ทำงานที่ Production ดังนั้นถ้าเราอยากพัฒนาแอพต่อก็ไปสร้าง Development Environment แล้วใช้ Connection String แบบเดิมเชื่อมต่อเข้าไปเหมือนในบนที่ 2 ก็ได้ แล้วพอเอาขึ้น Production ก็แค่ Deploy ลงไปใน Web App Service ที่เรา Grant สิทธิ์ในการเข้าถึง App Configuration Service เท่านั้นยังไงล่ะ

จากที่พิมพ์มาอาจจะมึนๆแต่ผมขอสรุปเป็นภาพด้านล่างง่ายๆประมาณนี้ละกัน

💖 สรุป

จากที่ทำไว้ทั้งหมดเราก็จะสามารถแยกความลับของ Production ออกจาก Environment อื่นๆได้แล้ว โดยเราโยนหน้าที่รับผิดชอบในการดูแลความลับไว้ให้คลาว์มันดูแลเอง และตัว Service ที่เป็นความลับนั้นก็ควรจะแยกไว้ใน Subscription พิเศษที่ไม่ให้ Developer ทั่วไปเข้าถึงได้นั่นเอง

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

Cloud Practices เรื่องการรักษาความลับเป็นส่วนหนึ่งของการทำ Security ของระบบ ซึ่งในการสร้างระบบที่อยู่บนคลาว์เราจะต้องใช้ความรู้ที่เป็นหลักปฎิบัติของคลาว์มาใช้ ซึ่งบางทีหลักปฎิบัติเดิมที่เป็นของ On-premise เองอาจจะไม่เหมาะสมเมื่อเทียบกับของคลาว์ ดังนั้นจะทำอะไรก็ตามให้ดู Cloud Best Practices ของตัว Service เหล่านั้นไว้เสมอ

Last updated