การป้องกันความลับหลุดตอนที่ 1

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

😢 ปัญหา

ในการเขียนแอพอะไรก็ตาม ในบางทีตัวแอพของเราก็อาจจะต้องมีการเชื่อมต่อกับภายนอก เช่น ฐานข้อมูล, API หรืออะไรก็ตามแต่ ซึ่งการเชื่อมไปที่ต่างๆเหล่านั้น มันจำเป็นที่เราจะต้องใส่ รหัสลับ ของเราลงไป เพื่อให้มันสามารถเชื่อมไปยังที่อื่นๆได้ เช่น ConnectionString ที่เอาไว้ต่อฐานข้อมูล หรือ Username & Password ในการเข้าใช้งานระบบอื่นๆ แล้วเราจะเก็บความลับพวกนั้นไว้ในแอพของเราได้ยังไง โดยไม่ให้มันหลุดออกไป เพราะเราคงไม่อยากให้ฐานข้อมูลของเรามีใครก็ไม่รู้เข้าใจดูหรือแก้ไขอะไรเล่นๆแน่นอนชิมิ ?

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

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

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

😘 เข้าใจให้ตรงกันก่อน

ข้ามไปอ่านหัวข้อถัดไปได้เลยถ้าใครใช้ asp.net core api เป็นอยู่แล้ว

ตัวอย่างในบทความนี้ผมจะสร้างเว็บโดยใช้ asp.net core (API) เป็นพื้นฐาน และใช้ Visual Studio Code ในการเดโม่ทั้งหมด ดังนั้นถ้าใครอยากจะทำตามก็ไปติดตั้งของต่างๆให้เรียบร้อยแซ๊ะ หรือถ้าขี้เกียจติดตั้งก็โหลดไฟล์เว็บด้านล่างนี้ไปแล้วข้ามไปดูหัวข้อถัดไปเลย

การสร้างโปรเจค asp.net core ก็สร้างโดยใช้คำสั่งพื้นฐานด้านล่างธรรมดาแหละ โดยผมจะสร้างเป็นตัว web api ละกันจะได้อธิบายง่ายๆหน่อย

dotnet new api -n saladpuk-demo

เพียงเท่านี้เราก็จะได้โปรเจคพื้นฐานมาเรียบร้อยละ

ตัวเว็บที่เราได้มาในโฟเดอร์ Controllers จะมีไฟล์ WeatherForecastController ที่เขาใส่ไว้เป็นพื้นฐาน เพื่อจำลองการพยากรณ์อากาศเอาไว้ ดังนั้นเราก็จะลองใช้คำสั่งด้านล่าง เพื่อทำการเปิดเว็บตัวนี้ขึ้นมา

dotnet run

แล้วก็เข้าไปดูเว็บของเราด้วย URL ตัวนี้ https://localhost:5001/WeatherForecast ซึ่งมันก็จะโชว์ข้อมูลจำลอง Json ออกมาประมาณนี้

ก็เป็นอันจบว่าเรามีตัวเว็บที่พร้อมเอาไว้เล่นกันต่อละ

💔 ตัวอย่างปัญหา

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

public IEnumerable<WeatherForecast> Get()
{
    var dbConnectionString = "รหัสลับในการต่อฐานข้อมูล";
    // จำลองว่าไปดึงข้อมูลจาก db มาแล้วได้ตัวนี้นะ
    var result = new[]
    {
        new WeatherForecast
        { 
            Date = DateTime.Now, 
            TemperatureC = 20, 
            Summary = "Balmy" 
        }
    };
    return result;
}

จากโค้ดตัวอย่างด้านบนเราจะเห็นว่าบรรทัดที่ 3 โค้ดของเรามีการฝัง ConnectionString ที่เป็นความลับเอาไว้ ซึ่งถ้าไม่มีเราก็จะไม่สามารถต่อฐานข้อมูลได้นั่นเอง ซึ่งถ้าเราปล่อยไว้แบบนี้มันจะเกิดปัญหาคือ

  • ถ้าเราอยากจะแก้ไขรหัสลับตัวนี้ เราก็จะต้อง build & deploy project ใหม่เท่านั้น

  • ใน source code ของเรามีความลับติดไปด้วยกับการ commit เสมอ ยิ่งถ้า repository ของเราเป็น public แล้วล่ะก็ ทุกคนบน GitHub ก็จะส่งยิ้มอ่อนให้เรา เพราะเขาก็เข้าฐานข้อมูลเราเช่นกัน

  • Developer ทุกคนเห็นความลับนี้หมด ดังนั้นใครจะเข้าฐานข้อมูลก็ได้

  • และอื่นๆนาๆ ที่เดี๋ยวจะมาเติมในบทความถัดๆไป

จากที่ร่ายมาก็จะเห็นว่ามันมีปัญหาหลายเรื่องม๊วกๆ ดังนั้นเราจะค่อยแก้ไปทีละเรื่องกันนะ

🤠 วิธีแก้ปัญหา (การตั้งค่าความลับ)

ตัวแรกที่แก้ได้ง่ายที่สุดก็คือ ทำให้เราแก้ไข configuration ต่างได้ง่ายๆ โดยไม่ต้อง Build & Deploy ตัวเว็บใหม่ดีกว่า โดยถ้าเราดูที่ตัวโปรเจคของเราดีๆ เราจะเห็นไฟล์ที่ชื่อว่า appsettings.json นั่นเอง ซึ่งเจ้าตัวนี้มีหน้าที่เอาไว้ทำการ config ของต่างๆให้กับโปรเจคนั่นเอง ดังนั้นเราจะย้ายความลับของเราออกจากไฟล์ .cs ไปไว้ที่ appsettings.json แทนตามโค้ดด้านล่าง

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "DbConnectionString" : "ABCDEFG"
}

จากโค้ดด้านบน ผมเพิ่มบรรทัดที่ 10 เข้าไป โดยตั้งชื่อมันว่า DbConnectionString และให้มันเก็บรหัสลับในการต่อฐานข้อมูลลงไป (ในตัวอย่างผมให้รหัสลับเป็น ABCDEFG นะ)

โดยปรกติตัว asp.net core มันจะอ่านไฟล์ appsettings.json ตอนเริ่มเว็บด้วยเสมอ ดังนั้นถัดไปผมก็จะแก้ไฟล์ WeatherForecastController ให้มันรับ IConfiguration เข้าไปใน constructor นั่นเอง ตามนี้

public class WeatherForecastController : ControllerBase
{
    private readonly IConfiguration configuration;

    public WeatherForecastController(IConfiguration configuration)
    {
        this.configuration = configuration;
    }

    ...
}

หมายเหตุ ตัว asp.net core มันจะอ่าน Environment Variables ขึ้นมา ซึ่งเราสามารถดูค่าพวกนั้นได้โดยใช้ IConfiguration ซึ่งโดยปรกติตัว asp.net core มันจะมีตัว Dependency Injection (DI) คอยส่ง object ของ IConfiguration มาให้เราอยู่แล้วโดยที่เราไม่ต้องไปทำอะไรเพิ่มเติมเลยครัช

สุดท้ายเพื่อให้เห็นภาพได้ง่ายว่าตัวเว็บเราสามารถอ่านค่าความลับที่เราเก็บไว้ใน appsettings.json ได้ เราก็ลองเอามันมาโชว์ดู โดยการเพิ่ม api เข้าไปนิสนุงตามด้านล่าง

[HttpGet("secret")]
public string Secret()
{
    return configuration["DbConnectionString"];
}

คราวนี้เราลองเรียกตัว api ตัวใหม่นี้ดู โดยการเปิด URL ตัวนี้ https://localhost:5001/WeatherForecast/secret เราก็จะพบว่าตัว api ของเรานั้นมันจะเอาค่าที่อยู่ใน appsettings.json มาแสดงให้เราเรียบร้อยแล้ว ตามรูปด้านล่าง

ข้อดีในการทำแบบนี้ ตัวเว็บของเราเมื่อต้องการแก้ไขค่า configuration ต่างๆก็สามารถทำได้เลย โดยที่ไม่ต้องไป build & deploy ใหม่นั่นเอง

ข้อควรระวัง จากที่ทำมาทั้งหมดนี้ มันจะยังไม่ได้แก้ปัญหาเรื่องเราฝังความลับของเราไว้ที่ source code นะ แม้ว่ามันจะไม่อยู่ในไฟล์ .cs แล้วก็ตาม แต่ตัวความลับของเราก็ยังอยู่ในไฟล์ appsettings.json นั่นเอง ถ้าเรา commit ไฟล์นี้ขึ้นไป มันก็จะติดอยู่ใน history ต่อไปอยู่ดีนั่นเอง

🤠 วิธีแก้ปัญหา (แยกความลับออกจาก source code)

ถัดมาเราก็จะลองทำให้ความลับของเราไม่อยู่ติดไปกับ source code ของเรากันดีกว่า ซึ่งโดยปรกติตัวเครื่องคอมของเรามันจะมีสิ่งที่เรียกว่า Environment Variables อยู่ ซึ่งเราสามารถหามันได้จากการกดปุ่ม Windows แล้วหาคำว่า Environment Variables ตามรูปด้านล่างนั่นเอง

ซึ่งเมื่อเปิดขึ้นมาแล้ว เราก็จะเข้าไปกำหนดค่า Environment Variables (อันเดียวกับที่เรากำหนด path ให้ java ยังไงล่ะ)

ถัดมาเราก็จะทำการสร้าง Environment Variable ของเราขึ้นมาใหม่ โดยการกดที่ปุ่ม New... นั่นเอง (ในตัวอย่างผมสร้างไว้สำหรับ user ผมคนเดียวก็เลยเอาไว้ตรงนี้)

ถัดไปก็ทำการตั้งชื่อ variable ของเราได้เลย ซึ่งในตัวอย่างผมจะตั้งชื่อให้ตรงกับใน appsettings.json ละกัน จะได้ใช้เป็นตัวอย่างถัดๆไปต่อ ส่วนค่าที่จะใส่ตรงนี้ก็คือตัว database connection ตัวจริงที่เราจะทำการต่อไปนั่นเอง ตามรูปด้านล่าง แล้วก็กด OK เบย

เพียงเท่านี้เราก็สามารถเก็บความลับของเราเอาไว้ที่เครื่องได้เรียบร้อยแล้ว ก็กดปุ่ม OK อีกครั้งก็เป็นอันเสร็จสิ้นพิธี

สุดท้ายเราก็จะลองเรียกตัว api ตัวเดิมดูว่ามันจะอ่านค่าอะไรกลับมาให้เรา ซึ่งเมื่อเราเรียก URL https://localhost:5001/WeatherForecast/secret ไปเรียบร้อยเราก็จะได้ตามรูปด้านล่าง

หมายเหตุ สำหรับใครที่ลองทำตามมาถึงตรงนี้ แล้วมันยังอ่านได้ค่า ABCDEFG แบบเดิมอยู่ ให้ลองปิด Visual Studio หรือ Command Prompt หรือ Terminal ลงไปก่อน แล้วลองเปิดมา Run ตัวเว็บใหม่อีกทีดูนะ

ข้อดีในการทำแบบนี้ ตัว source code ของเราก็จะไม่มีความลับถูกเขียนไว้ในนั้นอีกต่อไปแล้ว ดังนั้นใน commit history ของเราก็จะลดปัญหาเรื่อง security leak ได้ในระดับหนึ่งเรียบร้อยแล้วนั่นเองขอรับ

ข้อควรระวัง แม้ว่าใน source code จะไม่มีความลับติดไปแล้ว แต่ในตอนทำงานเราก็จะต้องไปบอกทุกคนในทีมให้ทำการกำหนดค่า Environment Variables อยู่ดี และ ทุกคนก็จะรู้รหัสลับต่างๆที่เราพยายามซ่อนอยู่นั่นเอง ดังนั้นคนร้ายก็จะอยู่ในกลุ่มของพวกเรานั่นเอง

จากวิธีการแก้ไขปัญหาทั้ง 2 วิธีก็จะช่วยให้โค้ดของเรามีความปลอดภัยเรื่อง security leak เพิ่มขึ้น แต่ก็ยังไม่ใช่ 100% ดังนั้นเดี๋ยวไปดูบทความถัดไปกันดีกว่าว่าจะค่อยๆก่อร่างสร้างตัวยังไง มันถึงจะป้องกันเรื่องพวกนี้บนคลาว์ได้ 99.99%

หมายเหตุ สาเหตุที่เรียกว่า 99.99% เพราะสุดท้ายระบบก็จะต้องมีใครซักคนหนึ่ง ที่สามารถเข้าไป reset ความลับพวกนี้อยู่ดี เพราะไม่อย่างนั้นดันเผลอไปลบความลับออก หรือดันจำไม่ได้ว่าเก็บไว้ที่ไหน ก็จะทำให้เราไม่สามารถกู้คืนหรือแก้สถานะการณ์อะไรได้เลยนั่นเอง คงไม่อยากเป็นเหมือนบริษัทที่เป็น Bitcoin xchanger ที่เป็นข่าวว่าลืม password จนทำให้ต้องปิดบริษัทเพราะ access ไปดำเนินการต่างๆไม่ได้หรอกชิมิ?

ชอบ เกลียด โกรธ พบเนื้อหาผิด อ่านแล้ว งง อยากถาม ก็เข้ามาคุยกับ ดช.แมวน้ำ ได้ที่ Saladpuk Facebook นะฮ๊าฟ ฝากกดไลค์กดแชร์ด้วยก็ดีนะครับ 😍

Last updated