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

Rails Security: การป้องกัน Authentication Vulnerabilities

ภาคหนึ่งของซีรีส์นี้ ครอบคลุม 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 ของพวกเขา

Rails Security: การป้องกัน Authentication Vulnerabilities

Facebook's login page.

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

Rails Security: การป้องกัน Authentication Vulnerabilities

Invalid username message.

ดังนั้น attacker รู้ว่า application บอกว่า user registered หรือไม่ ฟรีๆ

หาก attacker มี list of emails (ไม่ว่าจะ random generated หรือซื้อมา) ยังสามารถถาม application ว่า password ถูกต้องหรือไม่:

Rails Security: การป้องกัน Authentication Vulnerabilities

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:

Rails Security: การป้องกัน Authentication Vulnerabilities

The user doesn't exist.

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

Rails Security: การป้องกัน Authentication Vulnerabilities

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 ที่พวกเขาจะได้รับ:

Rails Security: การป้องกัน Authentication Vulnerabilities

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:

Rails Security: การป้องกัน Authentication Vulnerabilities

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 ต่อไป:

Rails Security: การป้องกัน Authentication Vulnerabilities

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 ต่อไปจะแสดง:

Rails Security: การป้องกัน Authentication Vulnerabilities

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!