ภาคหนึ่งของซีรีส์นี้ ครอบคลุม Injection Attacks
ในบทความที่สองของซีรีส์เกี่ยวกับ OWASP Top 10 Web Application Security Risks เราจะดำดิ่งสู่ universe ของ broken authentication และ data exposure threats
โดยเฉพาะ เราจะพูดถึงว่ามันง่ายแค่ไหนที่ hacker จะหลอกโค้ดที่คุณสร้างและทำ attacks เพื่อได้ users' data:
- User enumeration: เมื่อพวกเขา exploit login pages ของคุณด้วย brute-force testing リストของ possible users เพื่อเช็คว่ามีอยู่ใน database หรือไม่
- Weak passwords: เมื่อ system อนุญาต weak passwords hackers สามารถ brute force attack ทาย passwords ของ users
- Unrestricted cookies: เมื่อ system เก็บ sensitive data ใน cookies โดยไม่มี proper security settings hackers ขโมยข้อมูลผ่าน XSS attacks
เราจะลึกซึ้งเกี่ยวกับ sensitive data ที่ไม่ได้ protected เพียงพอ ทำให้เกิด vulnerabilities เช่น:
- Unsafe sensitive storage: เมื่อ sensitive data ถูก encrypt ด้วย weak algorithms เช่น MD5
- Exposing sensitive data: เมื่อ developers unintentionally expose unencrypted sensitive data ใน URLs หรือ hidden fields ตัวอย่างเช่น
เหมือนบทความแรกในซีรีส์ เราจะใช้ RailsGoat สำรวจแต่ละ threat ในทางปฏิบัติ หากคุณใหม่ที่นี่ โปรดอ้างอิงบทความก่อนหน้าเพื่อ setup และ run app
มาเริ่มเลย!
Authentication Threats (ภัยคุกคามการยืนยันตัวตน)
เราไม่สามารถมีชีวิตโดยไม่มี authentication ไม่ว่าจะบน back-end APIs หรือใน front-end forms มันเป็นหนึ่งในขั้นตอนสำคัญที่สุดใน application development เพราะมัน highlight border limits ของ security measures ของคุณ
ไม่ได้แค่ authentication เอง แตสำหรับสิ่งที่มาต่อไป: session management คุณจะเก็บ passwords และ tokens ที่ไหน? พวกมัน encrypted อย่างถูกต้องหรือไม่? คุณใช้ algorithm ที่น่าเชื่อถือหรือไม่? Password ของคุณ complex พอหรือไม่?
มีคำถามมากที่ต้องระวัง มาแยกส่วนและเข้าใจ common attacks ที่เกี่ยวกับ authentication และ session management ภายใน Rails applications
User Enumeration (การนับผู้ใช้)
User enumeration เป็น famous technique ที่ attackers ใช้เพื่อ check (ผ่าน brute force) ว่าข้อมูลที่ให้มามีอยู่ใน system ของคุณหรือไม่
หนึ่งใน notorious examples ของ attack นี้เกิดขึ้นกับ Facebook login page ปัจจุบัน หากคุณใส่ invalid และ random credentials เหมือนในรูปด้านล่าง Facebook จะ process request และ check existence ของ user ใน database ของพวกเขา

Facebook's login page.
เมื่อคุณคลิกปุ่ม Log In Facebook จะ return error message ว่า username (ซึ่งอาจเป็น email หรือ phone number) ไม่ valid

Invalid username message.
ดังนั้น attacker รู้ว่า application บอกว่า user registered หรือไม่ ฟรีๆ
หาก attacker มี list of emails (ไม่ว่าจะ random generated หรือซื้อมา) ยังสามารถถาม application ว่า password ถูกต้องหรือไม่:

Invalid password message.
เมื่อ attacker รู้ว่า system ตอบต่างกันสำหรับแต่ละ validation list สามารถสร้าง possible users และ common/weak passwords ได้ Brute-force tests จึงสามารถทำซ้ำๆ ต่อ system จนกว่าจะได้ access
แน่นอน Facebook developers รู้เรื่องนี้ และนั่นคือเหตุผลที่พวกเขา implement additional protections เช่น invisible captchas และ validations over number of requests จาก specific IP address
หนึ่งในสิ่งที่คุณทำได้เพื่อหลีกเลี่ยง user enumerations คือ validate username และ password ร่วมกัน และ return generic message มาเห็น approach นี้ใน action!
The Threat in Action (ภัยคุกคามในการกระทำ)
ใน RailsGoat app เปิด file user.rb ในโฟลเดอร์ app/models และ locate method authenticate ภายใน คุณอาจพบ code snippet ต่อไปนี้:
raise "#{email} doesn't exist!" if !(user) if user.password == Digest::MD5.hexdigest(password) auth = user else raise "Incorrect Password!" end ตรงจุด! Messages ถูก set อย่างที่จะให้ attackers รู้ว่า user ไม่มีอยู่ หรือ password ผิด
ทดสอบดู ไปที่ RailsGoat login page พิมพ์ random email และ password คุณอาจเห็น error message:

The user doesn't exist.
มิฉะนั้น หาก user มีอยู่ (เช่น ken@metacorp.com) แต password ผิด message ต่อไปจะแสดง:

Incorrect password.
พิจารณาว่า app ของคุณอนุญาตแค่ strong passwords attacker ยังสามารถสร้าง list of enumerated valid client emails และ target phishing emails ให้พวกเขา สร้าง impression ว่าคุณคือคน request malicious action
How to Resolve This Issue (วิธีแก้ไขปัญหานี้)
Quickest action ที่คุณทำได้เพื่อ safer คือเปลี่ยน message และทำให้ชีวิต hacker ยากขึ้น
ภายใน sessions_controller.rb (app/controllers folder) locate create method และเปลี่ยน code snippet ต่อไปนี้
flash[:error] = e.message เป็น:
flash[:error] = "Your credentials aren't valid." ตอนนี้ ทุกครั้งที่ users พิมพ์ wrong username หรือ password นั่นคือ message ที่พวกเขาจะได้รับ:

Invalid credentials.
อีกวิธีหนึ่งคือเปลี่ยนสอง messages ภายใน users.rb model
Weak Passwords (รหัสผ่านอ่อนแอ)
เราไม่สามารถ stress นี้พอ Require users ให้ input strong passwords และสร้าง validation code เช็คว่าพวกเขา meet criteria สำหรับ strong password
นี่เป็นหนึ่งในขั้นตอนสำคัญที่สุดในการป้องกัน user enumerations
The Threat In Action (ภัยคุกคามในการกระทำ)
ใน RailsGoat เปิด file user.rb และ locate บรรทัดแรกของโค้ดก่อน class definition:
validates :password, presence: true, confirmation: true, length: { within: 6..40 }, ... นี่เป็น clear example ของ password being weakly validated เนื่องจากเช็คแค่ length
How to Resolve This Issue (วิธีแก้ไขปัญหานี้)
Solution ค่อนข้าง simple validate password ตาม stronger requirements เช่น:
- อย่างน้อย 1 lowercase และ 1 uppercase letter
- อย่างน้อย 1 digit
- อย่างน้อย 10 chars
ยิ่งเพิ่ม requirements ยิ่ง passwords safe แค่แน่ใจว่าไม่ push too much เพราะอาจเพิ่ม complexity ใน usability และ password recovery flow
เพื่อแก้ใน RailsGoat แค่แทนที่ length property ด้วย:
:format => {:with => /\A.*(?=.*[a-zA-Z])(?=.*[0-9])(?=.{10,}).*\z/}, จากนั้นไป sign-up page เติม fields และให้ weak password เมื่อ submit นี่จะเป็น error message:

The password is invalid.
Unrestricted Cookies (คุกกี้ไม่มีข้อจำกัด)
ในบทความก่อนหน้า เราใช้เวลาสำหรับเข้าใจ XSS attacks เกิดอย่างไร เนื่องจากเกิดจากการอนุญาต attackers รัน malicious scripts ข้อมูลสำคัญสามารถโดนขโมยจาก session cookies หากเราไม่ prevent access ไป attributes เช่น document.cookie ใน JavaScript code
จำได้ว่า dispute ระหว่าง Web storage vs. cookies บ่อยครั้ง discussed ใน community Web storage practical กว่า แต attacker สามารถ gain full access ไป objects ที่เก็บไว้ที่นั่นโดยไม่มี proper protection จาก XSS threats
Cookies กลับกัน safer หากคุณทำ right steps ทำให้มัน safe เช่น set HttpOnly flag
สรุป cookie ที่เก็บ session information และ set ด้วย HttpOnly flag ไม่สามารถ access จาก JavaScript Document.cookie API ได้ วิธีนี้ only server จะรับมัน
ทางเลือกเรายังมี Secure attribute ซึ่งจะ ensure cookie ส่งไป server ก็ต่อเมื่อ (และก็ต่อเมื่อ) request เกิดขึ้นภายใน HTTPS (ไม่เคยใน HTTP) นี่จะทำให้ requests safer หากมีคน sniff พวกมันเป็น man-in-the-middle
The Threat In Action (ภัยคุกคามในการกระทำ)
Rails ทำขั้นตอนให้คุณโดย auto set ทุก cookies ด้วย HttpOnly flag นี่ great เพราะช่วย unaware developers หลีกเลี่ยงการโดน hack apps
เพื่อทดสอบตัวอย่างนี้ เราต้อง disable feature นี้ ซึ่ง RailsGoat ทำ explicitly ภายใน file session_store.rb ในโฟลเดอร์ config/initializers ดูเลย!
จากนั้นไป registration page อีกครั้ง เติม fields ให้ถูกต้อง และใส่เนื้อหาต่อไปนี้ใน field Name:
<script> alert(document.cookie); </script> เมื่อ submit form user จะถูกสร้าง พร้อม alert message ต่อไป:

Alert message exposing RailsGoat session cookie.
How to Resolve This Issue (วิธีแก้ไขปัญหานี้)
ในกรณีนี้ straightforward มาก แค่ ensure ไม่ disable HttpOnly flag บน Rails apps
ดังนั้น ลบ setting httponly: false และ restart server เมื่อคุณลองทำ operation เดิม alert message ต่อไปจะแสดง:

Empty alert message.
Other Scenarios (สถานการณ์อื่นๆ)
จินตนาการว่าคุณ access Web application จาก computers ที่ไม่ secure เช่น public library computers หรือ LAN houses หาก application ไม่ configured properly log you out หลัง specified period of inactivity session ของคุณจะยังอยู่
การปิด browser tab เพียงอย่างเดียว ไม่พอที่จะ log out ของ application
Common issue อีกอย่างในโค้ดของ developers คือเมื่อคุณ expose IDs ใน front end มันไม่ได้ยากที่จะคิด scenarios ที่เก็บ user's ID ใน hidden input หรือแม้ใน URL จะทำชีวิตคุณง่ายขึ้นเมื่อ user request อะไรเพิ่มจาก server
อย่างไรก็ตาม นี่คือ halfway through a stealing attack
Sensitive Data Exposure (การเปิดเผยข้อมูลละเอียดอ่อน)
หัวข้อนี้อาจเป็นหนึ่งใน most underestimated ในแง่ของการ dedicated effort เพื่อ ensure security ของ sensitive information
ไม่ว่าข้อมูลของคุณจะใน constant transit หรือ at rest มัน extremely important ที่จะ separate ordinary data จาก sensitive data Sensitive data รวมถึง credit card numbers และ codes, passwords, personal identifier numbers หรืออะไรก็ตามที่ relate to compliance หรือ privacy laws (เช่น EU's GDPR และ PCI)
นอกจากนี้ ขึ้นอยู่กับ specific business area ที่ application รัน อย่างสำคัญต้อง consult local legislation ว่า compliance rules อื่นๆ ใช้ได้หรือไม่
นี่คือ clear examples ของ vulnerability นี้:
- ข้อมูลของคุณ encrypted หรือ transported as plain text ผ่าน Web หรือไม่?
- หากคุณ encrypt data algorithm ไหนใช้? มัน strong และ reliable ต่อ newest crypto attacks?
- คุณใช้ default cryptographic keys หากไม่มี provided?
- คุณเช็คแล้วหรือว่า HTTP requests enforced by proper security headers?
มา analyze common scenarios
Unsafe Sensitive Storage (การเก็บข้อมูลละเอียดอ่อนไม่ปลอดภัย)
หากคุณทำงานกับ Web applications มาสักพัก โอกาสคือคุณเคยได้ยิน (หรืออาจใช้) MD5 message-digest algorithm แม้ยัง widely used สำหรับ hashing passwords มันได้ proven เป็น extremely weak
ที่นี่สำคัญต้องเข้าใจความต่างระหว่าง hashing และ encrypting information Encryption ควรเกิดเมื่อ key ใดๆ ถูกใช้ decrypt data Hashing หมายถึง method of conversion คุณสามารถ turn data into hash แตไม่ใช่ vice versa
นี่ apply to all sorts of sensitive information แต MD5 mostly known สำหรับใช้กับ password hashing หาก application เก็บ user's Social Security Number (SSN) ตัวอย่าง ต้อง ensure ไม่ได้แค่ store safely ใน database แตต้อง watch ว่าข้อมูล transmitted ผ่าน app ไป databases อื่นๆ และโดยเฉพาะ browser
The Threat In Action (ภัยคุกคามในการกระทำ)
เหมือนที่เราเห็น RailsGoat purposely stores user's password as MD5 hashes คุณเห็นได้ใน user.rb model:
def hash_password if self.password.present? self.password = Digest::MD5.hexdigest(password) end end ทุกครั้งที่ user logs in app hash provided password และ check ว่าผลลัพธ์ equal หรือไม่ มิฉะนั้น passwords ไม่ match ดังนั้น error thrown
How to Resolve This Issue (วิธีแก้ไขปัญหานี้)
มีหลายวิธี tackle issue นี้ แต perhaps หนึ่งใน famous ที่สุดคือ salt hashes เช่นที่ BCrypt ให้
แม้ Rails มาพร้อม default capabilities deal กับ Bcrypt แต notorious lib ที่ widely used วัตถุประสงค์นี้: bcrypt-ruby:
gem install bcrypt เมื่อคุณ map model กับมัน วิธีที่ password set และ obtained จาก database ถูก define lib adapts ทุกอย่างอัตโนมัติ:
require 'bcrypt' class User < ActiveRecord include BCrypt def password @password ||= Password.new(password_hash) end def password=(new_password) @password = Password.create(new_password) self.password_hash = @password end end อย่างไรก็ตาม flow ต้องการ additional string column (password_hash) ที่ table เพื่อ store password hash สำหรับ BCrypt algorithm
วิธีนี้ คุณสามารถ set password value โดยตรงไป model object และ BCrypt จะจัดการ hashing อย่างปลอดภัย สิ่งเดียวกันจะเกิดเมื่อ password retrieved เพื่อ compare กับ user's input
Exposing Too Much (เปิดเผยมากเกินไป)
ไม่ว่าคุณทำงานกับ REST APIs หรือ GraphQL endpoint ตัวอย่าง ต้อง ensure return แค่สิ่งที่ necessary ทำให้ client works
หาก JavaScript client request information จาก API และใช้แค่ portion ของมัน มันไม่ impede attacker จากการ grab endpoint และ call อีกครั้งเพื่อ retrieve whole response หรือ sniff ด้วย proxy tool
Always review APIs เพื่อ certify ว่า regardless of sensitive information ที่ return มันจะทำได้แค่กับ proper encryption และใน right places
The Threat In Action (ภัยคุกคามในการกระทำ)
เมื่อมาถึง user's data มันสำคัญที่จะสร้าง secure mechanisms ensure sensitive information ไม่ leaked
เปิด file users_controller.rb ในโฟลเดอร์ api/v1 ที่นั่นคุณจะพบ method:
def show respond_with @user.as_json end Simple as it is เมื่อ accessed by Web client endpoint นี้จะ return all user's fields populated ใน response รวมถึง password
ไม่ได้แค่ user model แต models อื่นๆ ที่เก็บ sensitive information ต้องมีวิธี select attributes ที่จะ visible to APIs
How to Resolve This Issue (วิธีแก้ไขปัญหานี้)
โชคดีที่ Rails มี easy way deal กับมัน มา override as_json method ด้วย:
def as_json super(only: [:id, :email]) end ตอนนี้ แทนที่จะ expose ทุกอย่างโดย default เรา respond แค่ required data สำหรับแต่ละ model ต้อง select important fields และ apply rule of thumb เดียวกัน
Wrapping Up (สรุป)
วันนี้เรา navigate ผ่านน้ำของ broken authentication และ sensitive data exposure ด้วยการ follow rules เหล่านี้ คุณจะ surely guarantee safer application สำหรับ users และ clients
เพิ่มเติม ความสำคัญของการอ่าน official Ruby on Rails Security Documentation ไม่สามารถ overemphasized ได้ ที่นั่นคุณอาจพบข้อมูลเพิ่มเติมเกี่ยวกับ session hijacking, storage mechanisms และ strategies เพื่อ encrypt data ใน best way possible
See you at our next stop!