ไมเคิล ฮาวเวิร์ด และคีธ บราวน์
บทความนี้ถือว่าคุณคุ้นเคยกับ C++, C# และ SQL
สรุป: เมื่อพูดถึงปัญหาด้านความปลอดภัย มีหลายสถานการณ์ที่อาจนำไปสู่ปัญหาได้ คุณอาจเชื่อถือโค้ดทั้งหมดที่ทำงานบนเครือข่ายของคุณ ให้สิทธิ์ผู้ใช้ทุกคนเข้าถึงไฟล์สำคัญ และไม่ต้องคอยตรวจสอบเพื่อดูว่าโค้ดบนเครื่องของคุณมีการเปลี่ยนแปลงหรือไม่ คุณอาจไม่ได้ติดตั้งซอฟต์แวร์ป้องกันไวรัส ไม่สามารถรักษาความปลอดภัยรหัสของคุณเอง และให้สิทธิ์แก่บัญชีมากเกินไป คุณอาจไม่ระมัดระวังในการใช้ฟังก์ชันในตัวจำนวนหนึ่งที่เอื้อให้เกิดการบุกรุกที่เป็นอันตราย และคุณอาจเปิดพอร์ตเซิร์ฟเวอร์ทิ้งไว้โดยไม่มีการตรวจสอบใดๆ แน่นอนว่าเราสามารถยกตัวอย่างได้อีกมากมาย อะไรคือปัญหาที่สำคัญจริงๆ (เช่น ข้อผิดพลาดที่อันตรายที่สุดที่ควรได้รับการแก้ไขทันทีเพื่อหลีกเลี่ยงไม่ให้ข้อมูลและระบบของคุณเสียหาย) ผู้เชี่ยวชาญด้านความปลอดภัย Michael Howard และ Keith Brown เสนอเคล็ดลับสิบประการเพื่อช่วยเหลือคุณ
-------------------------------------------------- -------------------------------------------------- ----------------------------------
ปัญหาด้านความปลอดภัยเกี่ยวข้องกับหลายแง่มุม ความเสี่ยงด้านความปลอดภัยอาจมาจากทุกที่ คุณอาจเขียนโค้ดการจัดการข้อผิดพลาดที่ไม่มีประสิทธิภาพหรือใจกว้างเกินไปเมื่อให้สิทธิ์ คุณอาจลืมว่ามีบริการใดบ้างที่ทำงานบนเซิร์ฟเวอร์ของคุณ คุณสามารถยอมรับการป้อนข้อมูลของผู้ใช้ทั้งหมดได้ และอื่นๆ เพื่อให้คุณเริ่มต้นในการปกป้องคอมพิวเตอร์ เครือข่าย และโค้ดของคุณได้ ต่อไปนี้เป็นเคล็ดลับ 10 ประการที่คุณสามารถปฏิบัติตามได้เพื่อสร้างกลยุทธ์เครือข่ายที่ปลอดภัยยิ่งขึ้น
1. การเชื่อถือข้อมูลที่ผู้ใช้กรอกทำให้คุณตกอยู่ในความเสี่ยง
แม้ว่าคุณจะไม่ได้อ่านส่วนที่เหลือ โปรดจำไว้ว่า: “อย่าเชื่อถือข้อมูลที่ผู้ใช้กรอก” ปัญหาเกิดขึ้นหากคุณคิดอยู่เสมอว่าข้อมูลนั้นถูกต้องและไม่เป็นอันตราย ช่องโหว่ด้านความปลอดภัยส่วนใหญ่เกี่ยวข้องกับผู้โจมตีที่ส่งข้อมูลที่เขียนโดยมีเจตนาร้ายไปยังเซิร์ฟเวอร์
การเชื่อถือความถูกต้องของอินพุตสามารถนำไปสู่บัฟเฟอร์ล้น การโจมตีด้วยสคริปต์ข้ามไซต์ การโจมตีโค้ดแทรก SQL และอื่นๆ
มาหารือเกี่ยวกับเวกเตอร์การโจมตีที่อาจเกิดขึ้นเหล่านี้โดยละเอียด
2. ป้องกันบัฟเฟอร์ล้น
เมื่อผู้โจมตีระบุความยาวของข้อมูลที่มากกว่าที่แอปพลิเคชันคาดไว้ บัฟเฟอร์ล้นจะเกิดขึ้น และข้อมูลล้นเข้าไปในพื้นที่หน่วยความจำภายใน บัฟเฟอร์ล้นเป็นปัญหาหลักของ C/C++ สิ่งเหล่านี้เป็นภัยคุกคาม แต่มักจะแก้ไขได้ง่าย เราพบว่ามีบัฟเฟอร์ล้นเพียงสองตัวที่ไม่ชัดเจนและแก้ไขได้ยาก นักพัฒนาไม่ได้คาดหวังว่าข้อมูลที่ให้ไว้จากภายนอกจะมีขนาดใหญ่กว่าบัฟเฟอร์ภายใน การโอเวอร์โฟลว์ทำให้เกิดความเสียหายต่อโครงสร้างข้อมูลอื่นๆ ในหน่วยความจำ ซึ่งมักถูกใช้โดยผู้โจมตีเพื่อเรียกใช้โค้ดที่เป็นอันตราย ข้อผิดพลาดดัชนีอาร์เรย์ยังสามารถทำให้เกิดบัฟเฟอร์น้อยเกินไปและโอเวอร์รันได้ แต่สิ่งนี้พบได้น้อยกว่า
ดูข้อมูลโค้ด C++ ต่อไปนี้:
เป็นโมฆะทำอะไรสักอย่าง (ถ่าน * cBuffSrc, DWORD cbBuffSrc) {
ถ่าน cBuffDest [32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}
มีปัญหาอะไร? ในความเป็นจริง โค้ดนี้ไม่มีอะไรผิดปกติหาก cBuffSrc และ cbBuffSrc มาจากแหล่งที่น่าเชื่อถือ (เช่น โค้ดที่ไม่ไว้วางใจข้อมูล ดังนั้น จึงตรวจสอบความถูกต้องและขนาดของข้อมูล) อย่างไรก็ตาม หากข้อมูลมาจากแหล่งที่ไม่น่าเชื่อถือและไม่ได้รับการยืนยัน ผู้โจมตี (แหล่งที่ไม่น่าเชื่อถือ) สามารถทำให้ cBuffSrc มีขนาดใหญ่กว่า cBuffDest ได้อย่างง่ายดาย และยังตั้งค่า cbBuffSrc ให้ใหญ่กว่า cBuffDest ได้อีกด้วย เมื่อ memcpy คัดลอกข้อมูลลงใน cBuffDest ที่อยู่ผู้ส่งจาก DoSomething จะเปลี่ยนไป และเนื่องจาก cBuffDest อยู่ติดกับที่อยู่ผู้ส่งบนเฟรมสแต็กของฟังก์ชัน ผู้โจมตีจึงสามารถดำเนินการที่เป็นอันตรายผ่านโค้ดได้
วิธีชดเชยคือไม่ต้องเชื่อถืออินพุตของผู้ใช้ และไม่เชื่อถือข้อมูลใดๆ ที่เก็บไว้ใน cBuffSrc และ cbBuffSrc:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
const DWORD cbBuffDest = 32;
ถ่าน cBuffDest [cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#เอ็นดิฟ
memcpy(cBuffDest, cBuffSrc, นาที (cbBuffDest, cbBuffSrc));
}
ฟังก์ชันนี้สาธิตคุณสมบัติสามประการของฟังก์ชันที่เขียนอย่างถูกต้องซึ่งสามารถลดบัฟเฟอร์ล้นได้ ขั้นแรก ผู้เรียกต้องระบุความยาวของบัฟเฟอร์ แน่นอนว่าคุณไม่สามารถเชื่อถือคุณค่านี้อย่างสุ่มสี่สุ่มห้าได้! ถัดไป ในโครงสร้างการดีบัก โค้ดจะตรวจพบว่าบัฟเฟอร์มีขนาดใหญ่พอที่จะเก็บบัฟเฟอร์ต้นทางหรือไม่ ถ้าไม่เช่นนั้น การละเมิดการเข้าถึงอาจถูกกระตุ้นและโค้ดอาจถูกโหลดลงในดีบักเกอร์ เมื่อแก้ไขจุดบกพร่อง คุณจะแปลกใจว่าคุณพบจุดบกพร่องจำนวนเท่าใด สุดท้ายและที่สำคัญที่สุด การเรียกไปยัง memcpy นั้นเป็นการป้องกันโดยที่จะไม่คัดลอกข้อมูลเกินกว่าที่บัฟเฟอร์เป้าหมายจะสามารถรองรับได้
ในฐานะส่วนหนึ่งของ Windows® Security Push ที่ Microsoft เราได้จัดทำรายการฟังก์ชันการจัดการสตริงที่ปลอดภัยสำหรับโปรแกรมเมอร์ C คุณสามารถค้นหาได้ใน Strsafe.h: Safer String Handling ในภาษา C (ภาษาอังกฤษ)
3. ป้องกันการโจมตีด้วยสคริปต์ข้ามไซต์
การโจมตีด้วยสคริปต์ข้ามไซต์เป็นปัญหาเฉพาะของเว็บ พวกมันสามารถทำร้ายข้อมูลไคลเอนต์ผ่านช่องโหว่ที่ซ่อนอยู่ในเว็บเพจเดียว ลองนึกภาพผลที่ตามมาของโค้ด ASP.NET ต่อไปนี้:
<script language=c#>
Response.Write("สวัสดี" + Request.QueryString("ชื่อ"));
</script>
มีกี่คนที่เห็นโค้ดที่คล้ายกัน? แต่กลับมีปัญหาอย่างน่าประหลาดใจ! โดยทั่วไป ผู้ใช้จะเข้าถึงรหัสนี้โดยใช้ URL ที่คล้ายกับต่อไปนี้:
http://explorationair.com/welcome.aspx?name=Michael
รหัส C# จะถือว่าข้อมูลนั้นถูกต้องเสมอและมีเพียงชื่อเท่านั้น อย่างไรก็ตาม ผู้โจมตีอาจใช้โค้ดนี้ในทางที่ผิดโดยการตั้งชื่อสคริปต์และโค้ด HTML หากคุณป้อน URL ต่อไปนี้
http://northwindtraders.com/welcome.aspx?name=<script>alert(' สวัสดี!');
</script>
คุณจะได้หน้าเว็บพร้อมกล่องโต้ตอบว่า "สวัสดี!" คุณอาจจะพูดว่า "แล้วไงล่ะ" ลองนึกภาพว่าผู้โจมตีอาจหลอกให้ผู้ใช้คลิกลิงก์เช่นนี้ แต่สตริงการสืบค้นมีสคริปต์และ HTML ที่เป็นอันตรายจริงๆ ดังนั้นจึงได้รับคุกกี้ของผู้ใช้และส่งไปยังเว็บไซต์ที่เป็นเจ้าของ ผู้โจมตี ขณะนี้ผู้โจมตีสามารถเข้าถึงข้อมูลคุกกี้ส่วนตัวของคุณหรือแย่กว่านั้น
เพื่อหลีกเลี่ยงปัญหานี้ มีสองวิธี ประการแรกคือการไม่ไว้วางใจอินพุตและจำกัดสิ่งที่มีชื่อผู้ใช้อย่างเคร่งครัด ตัวอย่างเช่น คุณสามารถใช้นิพจน์ทั่วไปเพื่อตรวจสอบว่าชื่อมีเพียงชุดย่อยของอักขระทั่วไปและไม่ใหญ่เกินไป ข้อมูลโค้ด C# ต่อไปนี้แสดงวิธีดำเนินการขั้นตอนนี้:
Regex r = new Regex(@"^[w]{1,40}$");
if (r.Match(strName).Success) {
// ดี! สตริงก็โอเค
} อื่น {
//ไม่ดี! สตริงไม่ถูกต้อง
}
รหัสนี้ใช้นิพจน์ทั่วไปเพื่อตรวจสอบว่าสตริงประกอบด้วยตัวอักษรหรือตัวเลขเพียง 1 ถึง 40 ตัว นี่เป็นวิธีเดียวที่ปลอดภัยในการพิจารณาว่าค่าถูกต้องหรือไม่
ไม่มีทางที่ HTML หรือสคริปต์จะหลอกนิพจน์ทั่วไปนี้ได้! อย่าใช้นิพจน์ทั่วไปเพื่อค้นหาอักขระที่ไม่ถูกต้องและปฏิเสธคำขอหากพบอักขระที่ไม่ถูกต้องดังกล่าว เนื่องจากอาจพลาดบางสิ่งได้ง่าย
ข้อควรระวังประการที่สองคือการเข้ารหัส HTML อินพุตทั้งหมดเป็นเอาต์พุต ซึ่งจะช่วยลดแท็ก HTML ที่เป็นอันตรายเพื่อให้อักขระหลีกปลอดภัยยิ่งขึ้น คุณสามารถใช้ HttpServerUtility.HtmlEncode ใน ASP.NET หรือ Server.HTMLEncode ใน ASP เพื่อหนีสตริงที่อาจมีปัญหาได้
4. อย่าร้องขอการอนุญาต
การโจมตีความน่าเชื่อถืออินพุตครั้งสุดท้ายที่เราจะพูดถึงคือการแทรกโค้ด SQL นักพัฒนาจำนวนมากเขียนโค้ดที่รับอินพุตและใช้อินพุตนั้นเพื่อสร้างคำสั่ง SQL ที่สื่อสารกับที่เก็บข้อมูลแบ็กเอนด์ เช่น Microsoft® SQL Server™ หรือ Oracle
ดูข้อมูลโค้ดต่อไปนี้:
void DoQuery(string Id) {
SqlConnection sql=SqlConnection ใหม่(@"แหล่งข้อมูล=localhost;" +
"รหัสผู้ใช้=sa;รหัสผ่าน=รหัสผ่าน;");
sql.เปิด();
sqlstring= "SELECT hasshipped" +
" จากการจัดส่ง WHERE id='" + Id + "'";
SqlCommand cmd = SqlCommand ใหม่ (sqlstring, sql);
-
รหัสนี้มีข้อบกพร่องร้ายแรงสามประการ ขั้นแรก สร้างการเชื่อมต่อจากบริการบนเว็บไปยัง SQL Server ในฐานะบัญชีผู้ดูแลระบบ คุณจะเห็นข้อผิดพลาดของสิ่งนี้ในไม่ช้า ประเด็นที่สอง ให้ความสนใจกับแนวทางปฏิบัติอันชาญฉลาดในการใช้ "รหัสผ่าน" เป็นรหัสผ่านสำหรับบัญชี sa!
แต่ข้อกังวลที่แท้จริงคือการต่อสตริงที่สร้างคำสั่ง SQL หากผู้ใช้ป้อน 1001 สำหรับ ID คุณจะได้รับคำสั่ง SQL ต่อไปนี้ ซึ่งถูกต้องโดยสมบูรณ์
SELECT hasshipped FROM shipping WHERE id = '1001'
แต่ผู้โจมตีมีความคิดสร้างสรรค์มากกว่านั้นมาก พวกเขาจะป้อน "'1001' DROP table shipping --" สำหรับ ID ซึ่งจะดำเนินการค้นหาดังนี้:
SELECT จัดส่งแล้วจาก
การจัดส่ง WHERE id = '1001'
DROP table shipping -- ';
มันเปลี่ยนวิธีการทำงานของแบบสอบถาม รหัสนี้ไม่เพียงแต่พยายามตรวจสอบว่ามีของบางอย่างถูกจัดส่งไปแล้วหรือยัง แต่ยังดำเนินการลบ (ลบ) ตารางการจัดส่งด้วย! ตัวดำเนินการ -- คือตัวดำเนินการแสดงความคิดเห็นใน SQL ซึ่งช่วยให้ผู้โจมตีสร้างชุดคำสั่ง SQL ที่ถูกต้องแต่เป็นอันตรายได้ง่ายขึ้น!
ในเวลานี้ คุณอาจสงสัยว่าผู้ใช้สามารถลบตารางในฐานข้อมูล SQL Server ได้อย่างไร แน่นอนคุณพูดถูก มีเพียงผู้ดูแลระบบเท่านั้นที่สามารถทำงานดังกล่าวได้ แต่ที่นี่คุณกำลังเชื่อมต่อกับฐานข้อมูลในรูปแบบ sa และ sa สามารถทำทุกอย่างที่เขาต้องการบนฐานข้อมูล SQL Server อย่าเชื่อมต่อกับ SQL Server จากแอปพลิเคชันใดๆ วิธีที่ถูกต้องคือใช้ Windows Integrated Authentication หากเหมาะสม หรือเชื่อมต่อเป็นบัญชีที่กำหนดไว้ล่วงหน้าด้วยสิทธิ์ที่เหมาะสม
การแก้ไขปัญหาโค้ดแทรก SQL เป็นเรื่องง่าย เมื่อใช้ขั้นตอนและพารามิเตอร์ที่จัดเก็บไว้ของ SQL โค้ดต่อไปนี้จะแสดงวิธีสร้างแบบสอบถามดังกล่าว และวิธีใช้นิพจน์ทั่วไปเพื่อยืนยันว่าอินพุตนั้นถูกต้อง เนื่องจากธุรกรรมของเราระบุว่า ID การจัดส่งสามารถเป็นตัวเลขได้เพียง 4 ถึง 10 หลักเท่านั้น:
Regex r = Regex ใหม่(@"^d{4,10}$");
ถ้า (!r.Match(Id).Success)
โยนข้อยกเว้นใหม่ ("ID ไม่ถูกต้อง")
;
string str="sp_HasShipped";
SqlCommand cmd = SqlCommand ใหม่ (str, sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID",Id);
บัฟเฟอร์ล้น, การเขียนสคริปต์ข้ามไซต์ และการโจมตีโค้ดแทรก SQL ล้วนเป็นตัวอย่างของปัญหาอินพุตที่เชื่อถือได้ การโจมตีทั้งหมดนี้บรรเทาลงได้ด้วยกลไกที่ถือว่าข้อมูลทั้งหมดเป็นอันตราย เว้นแต่จะพิสูจน์เป็นอย่างอื่น
5. ใส่ใจกับรหัสเข้ารหัส!
เรามาดูสิ่งที่อาจทำให้เราประหลาดใจกันดีกว่า ฉันพบว่ารหัสความปลอดภัยมากกว่าสามสิบเปอร์เซ็นต์ที่เราตรวจสอบมีช่องโหว่ด้านความปลอดภัย บางทีช่องโหว่ที่พบบ่อยที่สุดคือรหัสเข้ารหัสของคุณเองซึ่งมีแนวโน้มที่จะมีช่องโหว่ อย่าสร้างรหัสเข้ารหัสของคุณเอง มันเป็นหน้าที่ของคนโง่ อย่าคิดว่าเพียงเพราะคุณมีอัลกอริธึมการเข้ารหัสของตัวเองซึ่งคนอื่นไม่สามารถทำลายมันได้ ผู้โจมตีสามารถเข้าถึงตัวแก้ไขจุดบกพร่องได้ และยังมีเวลาและความรู้ในการพิจารณาว่าระบบทำงานอย่างไร ซึ่งมักจะพังภายในไม่กี่ชั่วโมง คุณควรใช้ Win32® CryptoAPI เนมสเปซ System.Security.Cryptography มีอัลกอริธึมการเข้ารหัสที่ยอดเยี่ยมและผ่านการทดสอบมากมาย
6. ลดโอกาสที่จะถูกโจมตี
หากผู้ใช้มากกว่า 90% ไม่ร้องขอ ก็ไม่ควรติดตั้งคุณสมบัติตามค่าเริ่มต้น Internet Information Services (IIS) 6.0 ปฏิบัติตามคำแนะนำในการติดตั้งนี้ ซึ่งคุณสามารถอ่านได้ในบทความของ Wayne Berry เรื่อง "นวัตกรรมใน Internet Information Services Let You Tightly Guard Secure Data and Server Processes" ที่เผยแพร่ในเดือนนี้ แนวคิดเบื้องหลังกลยุทธ์การติดตั้งนี้คือ คุณจะไม่ใส่ใจกับบริการที่คุณไม่ได้ใช้ และหากบริการเหล่านั้นกำลังทำงานอยู่ ผู้อื่นก็อาจถูกนำไปใช้ประโยชน์ได้ หากมีการติดตั้งคุณลักษณะตามค่าเริ่มต้น คุณลักษณะนั้นควรทำงานภายใต้หลักการของการอนุญาตน้อยที่สุด นั่นคือไม่อนุญาตให้แอปพลิเคชันทำงานด้วยสิทธิ์ของผู้ดูแลระบบเว้นแต่จำเป็น ทางที่ดีควรทำตามคำแนะนำนี้
7. ใช้หลักการของการอนุญาตน้อยที่สุด
ระบบปฏิบัติการและรันไทม์ภาษาทั่วไปมีนโยบายความปลอดภัยด้วยเหตุผลหลายประการ หลายๆ คนคิดว่าเหตุผลหลักที่มีนโยบายความปลอดภัยนี้ก็เพื่อป้องกันไม่ให้ผู้ใช้จงใจก่อให้เกิดอันตราย เช่น การเข้าถึงไฟล์ที่พวกเขาไม่ได้รับอนุญาตให้เข้าถึง การกำหนดค่าเครือข่ายใหม่เพื่อให้เหมาะกับความต้องการ และพฤติกรรมที่ร้ายแรงอื่นๆ ใช่ การโจมตีจากวงในประเภทนี้เป็นเรื่องปกติและจำเป็นต้องได้รับการปกป้อง แต่ก็มีอีกเหตุผลหนึ่งที่ต้องยึดถือกลยุทธ์การรักษาความปลอดภัยนี้ นั่นคือการสร้างกำแพงป้องกันรอบ ๆ โค้ดเพื่อป้องกันไม่ให้ผู้ใช้สร้างความหายนะบนเครือข่ายผ่านการกระทำโดยตั้งใจหรือ (ซึ่งมักเกิดขึ้น) โดยไม่ได้ตั้งใจ ตัวอย่างเช่น ไฟล์แนบที่ดาวน์โหลดทางอีเมล เมื่อดำเนินการบนเครื่องของ Alice จะถูกจำกัดไว้เฉพาะทรัพยากรที่ Alice สามารถเข้าถึงได้ หากไฟล์แนบมีม้าโทรจัน กลยุทธ์การรักษาความปลอดภัยที่ดีคือการจำกัดความเสียหายที่มันสามารถทำได้
เมื่อคุณออกแบบ สร้าง และปรับใช้แอปพลิเคชันเซิร์ฟเวอร์ คุณจะไม่สามารถสรุปได้ว่าคำขอทั้งหมดมาจากผู้ใช้ที่ถูกต้องตามกฎหมาย หากผู้ร้ายส่งคำขอที่เป็นอันตรายถึงคุณ (หวังว่าจะไม่ส่ง) และโค้ดของคุณทำงานไม่ดี คุณต้องการให้แอปพลิเคชันของคุณมีการป้องกันทุกวิถีทางที่เป็นไปได้เพื่อจำกัดความเสียหาย ดังนั้นเราจึงเชื่อว่าบริษัทของคุณกำลังใช้นโยบายความปลอดภัยไม่เพียงเพราะไม่ไว้วางใจคุณหรือโค้ดของคุณ แต่ยังเพื่อปกป้องตัวเองจากโค้ดภายนอกที่เป็นอันตรายอีกด้วย
หลักการของการอนุญาตขั้นต่ำถือได้ว่าการอนุญาตขั้นต่ำที่โค้ดต้องการควรได้รับในระยะเวลาน้อยที่สุด กล่าวคือ ให้สร้างกำแพงป้องกันรอบโค้ดของคุณให้มากที่สุดเท่าที่จะเป็นไปได้ตลอดเวลา เมื่อมีสิ่งเลวร้ายเกิดขึ้น - ตามที่กฎของเมอร์ฟี่รับประกัน - คุณจะดีใจที่กำแพงป้องกันเหล่านั้นตั้งอยู่ ดังนั้น ต่อไปนี้เป็นวิธีการเฉพาะบางประการสำหรับการรันโค้ดโดยใช้หลักการอนุญาตน้อยที่สุด
เลือกสภาพแวดล้อมที่ปลอดภัยสำหรับรหัสเซิร์ฟเวอร์ของคุณที่อนุญาตให้เข้าถึงทรัพยากรที่จำเป็นในการทำงานเท่านั้น หากบางส่วนของโค้ดของคุณต้องการสิทธิ์ที่สูง ให้ลองแยกส่วนของโค้ดนั้นออกและเรียกใช้แยกกันด้วยสิทธิ์ที่สูงกว่า หากต้องการแยกรหัสนี้ที่ทำงานด้วยข้อมูลการรับรองความถูกต้องของระบบปฏิบัติการที่แตกต่างกันอย่างปลอดภัย วิธีที่ดีที่สุดคือเรียกใช้รหัสนี้ในกระบวนการแยกต่างหาก (ทำงานในสภาพแวดล้อมที่ปลอดภัยพร้อมสิทธิพิเศษที่สูงกว่า) ซึ่งหมายความว่าคุณจะต้องมีการสื่อสารระหว่างกระบวนการ (เช่น COM หรือ Microsoft .NET ระยะไกล) และคุณจะต้องออกแบบอินเทอร์เฟซสำหรับโค้ดนั้นเพื่อลดการเดินทางไปกลับให้เหลือน้อยที่สุด
หากคุณแยกโค้ดออกเป็นแอสเซมบลีในสภาพแวดล้อม .NET Framework ให้พิจารณาระดับสิทธิ์ที่จำเป็นสำหรับโค้ดแต่ละชิ้น คุณจะพบว่ามันเป็นกระบวนการที่ง่าย: แยกโค้ดที่ต้องใช้สิทธิ์ที่สูงกว่าออกเป็นแอสเซมบลีแยกต่างหากที่ให้สิทธิ์มากกว่า ในขณะที่ปล่อยให้แอสเซมบลีที่เหลือส่วนใหญ่ทำงานด้วยสิทธิ์ที่ต่ำกว่า เพื่อให้คุณสามารถเพิ่มตัวป้องกันรอบโค้ดของคุณได้มากขึ้น เมื่อทำเช่นนี้ อย่าลืมว่า เนื่องจาก Code Access Security (CAS) stack คุณกำลังจำกัดสิทธิ์ไม่เพียงแต่สำหรับแอสเซมบลีของคุณเอง แต่ยังรวมถึงแอสเซมบลีใดๆ ที่คุณเรียกใช้ด้วย
หลายๆ คนสร้างแอปพลิเคชันของตนเองเพื่อให้สามารถเสียบส่วนประกอบใหม่ๆ เข้ากับผลิตภัณฑ์ของตนได้หลังจากที่ทดสอบและเผยแพร่ให้กับลูกค้าแล้ว การรักษาความปลอดภัยแอปพลิเคชันประเภทนี้เป็นเรื่องยากมาก เนื่องจากคุณไม่สามารถทดสอบทุกเส้นทางโค้ดที่เป็นไปได้เพื่อค้นหาจุดบกพร่องและช่องโหว่ด้านความปลอดภัย อย่างไรก็ตาม หากแอปพลิเคชันของคุณโฮสต์อยู่ CLR ก็มีฟีเจอร์ที่ยอดเยี่ยมที่สามารถใช้เพื่อปิดจุดขยายเหล่านี้ได้ ด้วยการประกาศวัตถุการอนุญาตหรือชุดการอนุญาตและการเรียก PermitOnly หรือ Deny คุณจะเพิ่มเครื่องหมายลงในสแต็กของคุณซึ่งจะบล็อกการให้สิทธิ์แก่โค้ดใด ๆ ที่คุณเรียกใช้ โดยการทำเช่นนี้ก่อนที่จะเรียกปลั๊กอิน คุณสามารถจำกัดงานที่ปลั๊กอินสามารถทำได้ ตัวอย่างเช่น ปลั๊กอินสำหรับคำนวณการผ่อนชำระไม่จำเป็นต้องมีการเข้าถึงระบบไฟล์ นี่เป็นเพียงอีกตัวอย่างหนึ่งของสิทธิพิเศษขั้นต่ำโดยคุณต้องป้องกันตัวเองล่วงหน้า อย่าลืมสังเกตข้อจำกัดเหล่านี้ และโปรดทราบว่าปลั๊กอินที่มีสิทธิพิเศษสูงกว่าสามารถใช้คำสั่ง Assert เพื่อหลบเลี่ยงข้อจำกัดเหล่านี้ได้
8. ตระหนักถึงรูปแบบความล้มเหลว
และยอมรับมัน คนอื่นเกลียดการเขียนโค้ดการจัดการข้อผิดพลาดมากเท่ากับคุณ มีสาเหตุหลายประการที่ทำให้โค้ดล้มเหลว และเพียงคิดถึงเหตุผลเหล่านั้นก็อาจทำให้หงุดหงิดได้ โปรแกรมเมอร์ส่วนใหญ่รวมทั้งพวกเราด้วย ชอบที่จะมุ่งเน้นไปที่เส้นทางการดำเนินการตามปกติ นั่นคือสิ่งที่งานเสร็จสิ้นจริงๆ มาจัดการข้อผิดพลาดเหล่านี้ให้เสร็จสิ้นอย่างรวดเร็วและไม่ยุ่งยากเท่าที่จะเป็นไปได้ จากนั้นไปยังบรรทัดถัดไปของโค้ดจริง
น่าเสียดายที่อารมณ์นี้ไม่ปลอดภัย เราต้องให้ความสำคัญกับรูปแบบความล้มเหลวในโค้ดของเราให้มากขึ้น โค้ดนี้มักเขียนด้วยความสนใจในเชิงลึกเพียงเล็กน้อยและมักไม่ได้รับการทดสอบอย่างสมบูรณ์ จำครั้งสุดท้ายที่คุณแน่ใจอย่างแน่นอนว่าได้แก้ไขโค้ดทุกบรรทัดในฟังก์ชันแล้ว รวมถึงตัวจัดการข้อผิดพลาดเล็กๆ น้อยๆ ทุกตัวในนั้นด้วย
รหัสที่ไม่ได้รับการทดสอบมักนำไปสู่ช่องโหว่ด้านความปลอดภัย มีสามสิ่งที่สามารถช่วยคุณบรรเทาปัญหานี้ได้ ขั้นแรก ให้ให้ความสนใจกับตัวจัดการข้อผิดพลาดเล็กๆ น้อยๆ เหล่านั้นเหมือนกับโค้ดปกติของคุณ พิจารณาสถานะของระบบเมื่อเรียกใช้โค้ดการจัดการข้อผิดพลาดของคุณ ระบบอยู่ในสถานะที่มีประสิทธิภาพและปลอดภัยหรือไม่? ประการที่สอง เมื่อคุณเขียนฟังก์ชันแล้ว ให้ทำตามขั้นตอนและแก้ไขจุดบกพร่องอย่างละเอียดสองสามครั้ง ตรวจสอบให้แน่ใจว่าได้ทดสอบตัวจัดการข้อผิดพลาดทุกตัว โปรดทราบว่าถึงแม้จะมีเทคนิคดังกล่าว ก็อาจไม่พบข้อผิดพลาดด้านจังหวะที่ละเอียดอ่อนมาก คุณอาจต้องส่งพารามิเตอร์ข้อผิดพลาดไปยังฟังก์ชันของคุณ หรือปรับสถานะของระบบในทางใดทางหนึ่งเพื่อให้ตัวจัดการข้อผิดพลาดดำเนินการได้ การสละเวลาในการอ่านโค้ดของคุณจะทำให้คุณช้าลงและมีเวลาเพียงพอที่จะดูโค้ดและสถานะของระบบในขณะที่ทำงาน ด้วยการก้าวผ่านโค้ดในดีบักเกอร์อย่างระมัดระวัง เราค้นพบข้อบกพร่องมากมายในตรรกะการเขียนโปรแกรมของเรา นี่คือเทคโนโลยีที่ได้รับการพิสูจน์แล้ว กรุณาใช้เทคนิคนี้ สุดท้าย ตรวจสอบให้แน่ใจว่าชุดการทดสอบของคุณทำให้ฟังก์ชันของคุณล้มเหลว พยายามมีชุดทดสอบที่จะตรวจสอบโค้ดทุกบรรทัดในฟังก์ชัน วิธีนี้สามารถช่วยให้คุณมองเห็นรูปแบบต่างๆ โดยเฉพาะอย่างยิ่งเมื่อทำการทดสอบโดยอัตโนมัติและเรียกใช้การทดสอบทุกครั้งที่คุณสร้างโค้ด
มีสิ่งหนึ่งที่สำคัญมากที่จะพูดเกี่ยวกับโหมดความล้มเหลว ตรวจสอบให้แน่ใจว่าระบบของคุณอยู่ในสถานะที่ปลอดภัยที่สุดเมื่อโค้ดของคุณล้มเหลว รหัสที่มีปัญหาบางส่วนแสดงอยู่ด้านล่าง:
bool accessGranted = จริง; // มองโลกในแง่ดีเกินไป!
พยายาม {
// ดูว่าเราสามารถเข้าถึง c:test.txt ได้หรือไม่
ใหม่ FileStream(@"c:test.txt",
FileMode เปิด
FileAccess.Read).ปิด();
-
จับ (SecurityException x) {
// การเข้าถึงถูกปฏิเสธ
accessGranted = เท็จ;
-
จับ (...) {
//มีเรื่องเกิดขึ้นอีก
}
แม้ว่าเราจะใช้ CLR แต่เรายังคงได้รับอนุญาตให้เข้าถึงไฟล์ได้ ในกรณีนี้ SecurityException จะไม่ถูกส่งออกไป แต่จะเกิดอะไรขึ้นหาก Discretionary Access Control List (DACL) ของไฟล์ไม่อนุญาตให้เราเข้าถึง? ในเวลานี้ จะมีข้อยกเว้นประเภทอื่นเกิดขึ้น แต่เนื่องจากสมมติฐานในแง่ดีในบรรทัดแรกของโค้ด เราจะไม่มีวันรู้เรื่องนี้
วิธีที่ดีกว่าในการเขียนโค้ดนี้คือให้ระมัดระวัง:
bool accessGranted = false; // ระวัง!
พยายาม {
// ดูว่าเราสามารถเข้าถึง c:test.txt ได้หรือไม่
ใหม่ FileStream(@"c:test.txt",
FileMode เปิด
FileAccess.Read).ปิด();
//ถ้ายังอยู่ก็เยี่ยมเลย!
สิทธิ์การเข้าถึง = จริง;
-
catch (...) {}
สิ่งนี้จะมีเสถียรภาพมากขึ้น เพราะไม่ว่าเราจะล้มเหลวอย่างไร เราก็จะกลับสู่โหมดที่ปลอดภัยที่สุดเสมอ
9. การแอบอ้างบุคคลอื่นมีความเสี่ยงสูง
เมื่อเขียนแอปพลิเคชันเซิร์ฟเวอร์ คุณมักจะพบว่าตัวเองใช้คุณลักษณะที่มีประโยชน์ของ Windows ที่เรียกว่าการแอบอ้างบุคคลอื่น ทั้งทางตรงและทางอ้อม การเลียนแบบช่วยให้แต่ละเธรดในกระบวนการทำงานในสภาพแวดล้อมการรักษาความปลอดภัยที่แตกต่างกัน ซึ่งโดยทั่วไปจะเป็นของไคลเอ็นต์ ตัวอย่างเช่น เมื่อตัวเปลี่ยนเส้นทางระบบไฟล์ได้รับคำขอไฟล์ผ่านเครือข่าย ระบบจะตรวจสอบความถูกต้องของไคลเอ็นต์ระยะไกล ตรวจสอบเพื่อยืนยันว่าคำขอของไคลเอ็นต์ไม่ละเมิด DACL ในการแชร์ จากนั้นจึงแนบแฟล็กของไคลเอ็นต์เข้ากับเธรดที่จัดการคำขอ . เพื่อจำลองไคลเอนต์ เธรดนี้สามารถเข้าถึงระบบไฟล์ในเครื่องบนเซิร์ฟเวอร์โดยใช้สภาพแวดล้อมการรักษาความปลอดภัยของไคลเอ็นต์ สะดวกเนื่องจากระบบไฟล์ในเครื่องมีความปลอดภัยอยู่แล้ว โดยจะทำการตรวจสอบการเข้าถึงโดยคำนึงถึงประเภทของการเข้าถึงที่ร้องขอ DACL บนไฟล์ และแฟล็กการเลียนแบบบนเธรด หากการตรวจสอบการเข้าถึงล้มเหลว ระบบไฟล์ในเครื่องจะรายงานไปยังตัวเปลี่ยนเส้นทางระบบไฟล์ ซึ่งจากนั้นจะส่งข้อผิดพลาดไปยังไคลเอ็นต์ระยะไกล สิ่งนี้สะดวกสำหรับตัวเปลี่ยนเส้นทางระบบไฟล์อย่างไม่ต้องสงสัย เนื่องจากเพียงส่งคำขอไปยังระบบไฟล์ในเครื่องและปล่อยให้ตรวจสอบการเข้าถึงของตัวเอง เหมือนกับว่าไคลเอนต์อยู่ในเครื่อง
ทั้งหมดนี้เป็นสิ่งที่ดีสำหรับเกตเวย์ธรรมดาเช่นตัวเปลี่ยนเส้นทางไฟล์ แต่การจำลองมักใช้ในแอปพลิเคชันอื่นๆ ที่ซับซ้อนกว่า ใช้เว็บแอปพลิเคชันเป็นตัวอย่าง หากคุณเขียนโปรแกรม ASP ที่ไม่มีการจัดการแบบคลาสสิก ส่วนขยาย ISAPI หรือแอปพลิเคชัน ASP.NET และมีข้อกำหนดต่อไปนี้ในไฟล์ Web.config
<identity impersonate='true'>
สภาพแวดล้อมการทำงานของคุณจะมีสภาพแวดล้อมความปลอดภัยที่แตกต่างกันสองแบบ: คุณจะมี แท็กกระบวนการและแท็กเธรด โดยทั่วไป แท็กเธรดจะถูกใช้สำหรับการตรวจสอบการเข้าถึง (ดูรูปที่ 3) สมมติว่าคุณกำลังเขียนแอปพลิเคชัน ISAPI ที่ทำงานในกระบวนการของเว็บเซิร์ฟเวอร์ และสมมติว่าคำขอส่วนใหญ่ไม่ได้รับการรับรองความถูกต้อง แท็กเธรดของคุณอาจเป็น IUSR_MACHINE แต่แท็กกระบวนการของคุณคือ SYSTEM! สมมติว่าโค้ดของคุณอาจถูกเอารัดเอาเปรียบโดยนักแสดงที่ไม่ดีผ่านทางบัฟเฟอร์ล้น คุณคิดว่ามันจะพอใจกับการทำงานเป็น IUSR_MACHINE หรือไม่? ไม่แน่นอน รหัสการโจมตีของเขามักจะเรียก RevertToSelf เพื่อลบการตั้งค่าสถานะการแอบอ้างบุคคลอื่นโดยหวังว่าจะเพิ่มระดับสิทธิ์ของเขา ในกรณีนี้เขาจะประสบความสำเร็จอย่างง่ายดาย เขายังสามารถเรียก CreateProcess ได้อีกด้วย มันไม่ได้คัดลอกแท็กของกระบวนการใหม่จากแท็กการเลียนแบบ แต่จากแท็กกระบวนการเพื่อให้กระบวนการใหม่สามารถทำงานเป็นระบบ
แล้วจะแก้ไขปัญหาเล็กๆ น้อยๆ นี้ได้อย่างไร? นอกเหนือจากการทำให้แน่ใจว่าไม่มีบัฟเฟอร์ล้นเกิดขึ้นตั้งแต่แรกแล้ว ให้จำหลักการของการอนุญาตน้อยที่สุด หากโค้ดของคุณไม่จำเป็นต้องมีสิทธิ์พิเศษเท่ากับ SYSTEM อย่ากำหนดค่าเว็บแอปพลิเคชันของคุณให้ทำงานในกระบวนการของเว็บเซิร์ฟเวอร์ หากคุณเพียงแค่กำหนดค่าเว็บแอปพลิเคชันของคุณให้ทำงานในสภาพแวดล้อมที่มีการแยกระดับปานกลางหรือสูง แท็กกระบวนการของคุณจะเป็น IWAM_MACHINE คุณไม่มีสิทธิ์ใดๆ จริงๆ ดังนั้นการโจมตีนี้แทบจะไม่มีผลใดๆ เลย โปรดทราบว่าใน IIS 6.0 (เร็วๆ นี้จะเป็นส่วนประกอบของ Windows .NET Server) โค้ดที่ผู้ใช้เขียนจะไม่ทำงานเป็นระบบตามค่าเริ่มต้น จากความเข้าใจที่ว่านักพัฒนาทำผิดพลาด ความช่วยเหลือใดๆ ที่เว็บเซิร์ฟเวอร์สามารถให้ได้ในการลดสิทธิ์ที่มอบให้กับโค้ดจะมีประโยชน์ในกรณีที่มีปัญหาด้านความปลอดภัยในโค้ด
นี่เป็นข้อผิดพลาดอีกประการหนึ่งที่โปรแกรมเมอร์ COM อาจพบ COM มีแนวโน้มที่ไม่ดีที่จะละเว้นเธรด ถ้าคุณเรียกเซิร์ฟเวอร์ COM ในกระบวนการ และรุ่นของเธรดไม่ตรงกับรุ่นของเธรดที่เรียก COM ทำการเรียกบนเธรดอื่น COM ไม่เผยแพร่ค่าสถานะการเลียนแบบบนเธรดผู้เรียก ดังนั้นผลลัพธ์ก็คือว่า การโทรจะดำเนินการในบริบทความปลอดภัยของกระบวนการมากกว่าในบริบทความปลอดภัยของเธรดการเรียก เซอร์ไพรส์มาก!
นี่เป็นอีกตัวอย่างหนึ่งของข้อผิดพลาดของการจำลอง สมมติว่าเซิร์ฟเวอร์ของคุณยอมรับคำขอที่ส่งผ่านไปป์ที่มีชื่อ DCOM หรือ RPC คุณรับรองความถูกต้องของไคลเอนต์และเลียนแบบพวกเขา โดยเปิดอ็อบเจ็กต์เคอร์เนลในนามของลูกค้าผ่านการเลียนแบบ และคุณลืมปิดวัตถุใดวัตถุหนึ่ง (เช่น ไฟล์) เมื่อไคลเอ็นต์ตัดการเชื่อมต่อ เมื่อลูกค้ารายถัดไปเข้ามา คุณรับรองความถูกต้องและปลอมแปลงเป็นลูกค้ารายนั้น และคาดเดาว่าจะเกิดอะไรขึ้น? คุณยังคงสามารถเข้าถึงไฟล์ที่ไคลเอ็นต์ก่อนหน้านี้ "พลาด" ได้ แม้ว่าไคลเอ็นต์ใหม่จะไม่สามารถเข้าถึงไฟล์ก็ตาม เพื่อเหตุผลด้านประสิทธิภาพ เคอร์เนลจะทำการตรวจสอบการเข้าถึงออบเจ็กต์ในครั้งแรกที่เปิดเท่านั้น คุณยังคงสามารถเข้าถึงไฟล์นี้ได้แม้ว่าคุณจะเปลี่ยนสภาพแวดล้อมการรักษาความปลอดภัยในภายหลังเนื่องจากคุณแอบอ้างเป็นผู้ใช้รายอื่น
สถานการณ์ที่กล่าวมาข้างต้นมีไว้เพื่อเตือนคุณว่าการจำลองให้ความสะดวกแก่นักพัฒนาเซิร์ฟเวอร์ แต่ความสะดวกสบายนี้มีอันตรายซ่อนเร้นอยู่อย่างมาก เมื่อคุณรันโปรแกรมด้วยแฟล็กจำลอง ต้องแน่ใจว่าได้ใส่ใจกับโค้ดของคุณอย่างระมัดระวัง
10. เขียนแอปพลิเคชันที่ผู้ใช้ที่ไม่ใช่ผู้ดูแลระบบสามารถใช้งานได้จริง
นี่เป็นข้อพิสูจน์อย่างแท้จริงต่อหลักการของการอนุญาตขั้นต่ำ หากโปรแกรมเมอร์ยังคงพัฒนาโค้ดที่ต้องการให้ผู้ดูแลระบบทำงานอย่างถูกต้องบน Windows เราไม่สามารถคาดหวังที่จะปรับปรุงความปลอดภัยของระบบได้ Windows มีชุดคุณลักษณะด้านความปลอดภัยที่แข็งแกร่งมาก แต่ผู้ใช้ไม่สามารถใช้ประโยชน์จากคุณลักษณะเหล่านี้ได้หากต้องเป็นผู้ดูแลระบบจึงจะใช้งานคุณลักษณะเหล่านี้ได้
คุณจะปรับปรุงได้อย่างไร? ขั้นแรก ให้ลองด้วยตัวเองโดยไม่ต้องเรียกใช้ในฐานะผู้ดูแลระบบ ในไม่ช้า คุณจะได้เรียนรู้ถึงความเจ็บปวดของการใช้โปรแกรมที่ไม่ได้ออกแบบโดยคำนึงถึงความปลอดภัย วันหนึ่ง ฉัน (Keith) ติดตั้งซอฟต์แวร์ชิ้นหนึ่งที่ผู้ผลิตอุปกรณ์มือถือของฉันจัดเตรียมไว้ให้ ซึ่งจะซิงโครไนซ์ข้อมูลระหว่างคอมพิวเตอร์เดสก์ท็อปและอุปกรณ์มือถือของฉัน ตามปกติ ฉันออกจากระบบบัญชีผู้ใช้ปกติ เข้าสู่ระบบอีกครั้งโดยใช้บัญชีผู้ดูแลระบบในตัว ติดตั้งซอฟต์แวร์ จากนั้นเข้าสู่ระบบอีกครั้งในบัญชีปกติของฉัน และพยายามเรียกใช้ซอฟต์แวร์ เป็นผลให้แอปพลิเคชันปรากฏขึ้นกล่องโต้ตอบแจ้งว่าไม่สามารถเข้าถึงไฟล์ข้อมูลที่ต้องการได้ จากนั้นจึงแสดงข้อความแสดงการละเมิดการเข้าถึง เพื่อนๆ นี่คือผลิตภัณฑ์ซอฟต์แวร์ของผู้ผลิตอุปกรณ์พกพารายใหญ่ มีข้อแก้ตัวสำหรับข้อผิดพลาดนี้หรือไม่?
หลังจากเรียกใช้ FILEMON จาก http://sysinternals.com (เป็นภาษาอังกฤษ) ฉันค้นพบอย่างรวดเร็วว่าแอปพลิเคชันพยายามเปิดไฟล์ข้อมูลสำหรับการเข้าถึงการเขียนซึ่งติดตั้งในไดเร็กทอรีเดียวกันกับตัวกลางที่ปฏิบัติการได้ของแอปพลิเคชัน เมื่อแอปพลิเคชันได้รับการติดตั้งในไดเร็กทอรี Program Files ตามที่คาดไว้ แอปพลิเคชันเหล่านั้นจะต้องไม่พยายามเขียนข้อมูลลงในไดเร็กทอรีนั้น ไฟล์โปรแกรมมีนโยบายควบคุมการเข้าถึงที่เข้มงวดด้วยเหตุผลบางประการ เราไม่ต้องการให้ผู้ใช้เขียนไปยังไดเร็กทอรีเหล่านี้ เนื่องจากจะทำให้ผู้ใช้รายหนึ่งสามารถออกจากโทรจันเพื่อให้ผู้ใช้รายอื่นดำเนินการได้อย่างง่ายดาย ที่จริงแล้ว แบบแผนนี้เป็นหนึ่งในข้อกำหนดพื้นฐานของ Windos XP (ดู http://www.microsoft.com/winlogo [ภาษาอังกฤษ])
เราได้ยินมาว่าโปรแกรมเมอร์จำนวนมากเกินไปให้ข้อแก้ตัวว่าทำไมพวกเขาถึงเลือกที่จะทำงานในฐานะผู้ดูแลระบบเมื่อพัฒนาโค้ด หากเราเพิกเฉยต่อปัญหานี้ เราจะยิ่งทำให้เรื่องแย่ลงไปอีก เพื่อนๆ คุณไม่จำเป็นต้องมีสิทธิ์ของผู้ดูแลระบบในการแก้ไขไฟล์ข้อความ สิทธิ์ผู้ดูแลระบบไม่จำเป็นต้องแก้ไขหรือดีบักโปรแกรม เมื่อคุณต้องการสิทธิ์ของผู้ดูแลระบบ ให้ใช้ฟีเจอร์ RunAs ของระบบปฏิบัติการเพื่อเรียกใช้ 玎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [ภาษาอังกฤษ])
เราได้ยินมาว่าโปรแกรมเมอร์ให้ข้อแก้ตัวมากเกินไป เหตุใดพวกเขาจึงเลือกที่จะทำงานในฐานะผู้ดูแลระบบเมื่อพัฒนาโค้ด หากเราเพิกเฉยต่อปัญหานี้จะทำให้สิ่งต่าง ๆ แย่ลงเท่านั้น การแก้ไขไฟล์ข้อความไม่ต้องการสิทธิ์ของผู้ดูแลระบบ ให้ใช้คุณลักษณะ RunAs ของระบบปฏิบัติการเพื่อเรียกใช้โปรแกรมแยกต่างหากที่มีสิทธิ์ระดับสูง (ดูคอลัมน์บทสรุปด้านความปลอดภัย [ภาษาอังกฤษ] ในเดือนพฤศจิกายน 2544) หากคุณกำลังเขียนเครื่องมือสำหรับนักพัฒนา คุณมีความรับผิดชอบเพิ่มเติมสำหรับกลุ่มนี้ หยุดวงจรอันเลวร้ายของการเขียนโค้ดที่สามารถเรียกใช้ได้เฉพาะในฐานะผู้ดูแลระบบเท่านั้น เป้าหมายจะต้องเปลี่ยนแปลงโดยพื้นฐาน
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่นักพัฒนาสามารถเรียกใช้ในฐานะที่ไม่ใช่ผู้ดูแลระบบได้อย่างง่ายดาย โปรดดูเว็บไซต์ของ Keith ที่ http://www.develop com/kbrown (เป็นภาษาอังกฤษ) ลองอ่านหนังสือของ Michael เรื่อง Writing Secure Code (Microsoft Press, 2001) ซึ่งมีเคล็ดลับในการเขียนแอปพลิเคชันที่ทำงานได้ดีในสภาพแวดล้อมที่ไม่ใช่ผู้ดูแลระบบ