ตรวจสอบให้แน่ใจว่าไม่ได้ใช้รหัสที่ไม่ปลอดภัย


10

ฟังก์ชั่นf()ใช้eval()(หรือบางสิ่งที่อันตราย) กับข้อมูลที่ฉันสร้างและเก็บไว้ในlocal_fileเครื่องที่ใช้โปรแกรมของฉัน:

import local_file

def f(str_to_eval):
    # code....
    # .... 
    eval(str_to_eval)    
    # ....
    # ....    
    return None


a = f(local_file.some_str)

f() ปลอดภัยที่จะเรียกใช้เนื่องจากสตริงที่ฉันกำหนดให้เป็นของฉันเอง

แต่ถ้าฉันเคยตัดสินใจที่จะใช้มันสำหรับบางสิ่งบางอย่างที่ไม่ปลอดภัย (เช่นผู้ใช้ป้อนข้อมูล) สิ่งที่จะไปผิดมหันต์ นอกจากนี้หากlocal_fileหยุดเป็นท้องถิ่นแล้วมันจะสร้างช่องโหว่เนื่องจากฉันจะต้องเชื่อถือเครื่องที่ให้ไฟล์นั้นเช่นกัน

ฉันจะแน่ใจได้อย่างไรว่าฉันไม่เคย "ลืม" ว่าฟังก์ชั่นนี้ไม่ปลอดภัยที่จะใช้ (เว้นแต่ว่าตรงตามเกณฑ์ที่กำหนด)

หมายเหตุ: eval()มีอันตรายและมักจะถูกแทนที่ด้วยสิ่งที่ปลอดภัย


1
ฉันมักจะชอบไปกับคำถามที่ถามแทนที่จะพูดว่า "คุณไม่ควรทำเช่นนี้" แต่ฉันจะให้ข้อยกเว้นในกรณีนี้ ฉันค่อนข้างแน่ใจว่าไม่มีเหตุผลว่าทำไมคุณควรใช้ eval ฟังก์ชั่นเหล่านี้มีมาพร้อมกับภาษาเช่น PHP และ Python เพื่อใช้เป็นหลักฐานพิสูจน์แนวคิดว่าสามารถแปลภาษาได้มากน้อยเพียงใด มันเป็นพื้นฐานที่ไม่น่าเชื่อว่าคุณสามารถเขียนรหัสที่สร้างรหัส แต่คุณไม่สามารถรหัสตรรกะเดียวกันนั้นลงในฟังก์ชั่น งูหลามจะมีการเรียกกลับและการผูกฟังก์ชั่นที่จะช่วยให้คุณทำสิ่งที่คุณต้องมีเพื่อให้
user1122069

1
" ฉันจะต้องเชื่อถือเครื่องที่ให้ไฟล์นั้นด้วย " - คุณต้องเชื่อถือเครื่องที่คุณใช้รหัสของคุณอยู่เสมอ
Bergi

ใส่ความเห็นในไฟล์ว่า "สตริงนี้ได้รับ eval'd โปรดอย่าใส่รหัสที่เป็นอันตรายที่นี่"?
user253751

@immibis ความคิดเห็นคงไม่เพียงพอ แน่นอนฉันจะมีขนาดใหญ่ "คำเตือน: ฟังก์ชั่นนี้ใช้ eval ()" ในเอกสารของฟังก์ชั่น แต่ฉันจะพึ่งพาสิ่งที่ปลอดภัยกว่านั้น ตัวอย่างเช่น (เนื่องจากมีเวลา จำกัด ) ฉันไม่ได้อ่านเอกสารซ้ำของฟังก์ชัน ฉันไม่คิดว่าฉันควรจะเป็น มีวิธีที่เร็วกว่า (นั่นคือมีประสิทธิภาพมากกว่า) ในการรู้ว่ามีบางสิ่งไม่ปลอดภัยเช่นวิธีการที่Murphy เชื่อมโยงในคำตอบของเขา
Fermi บุคคลที่ผิดธรรมดา

@Fermiparadox เมื่อคุณนำ eval ออกจากคำถามโดยไม่มีตัวอย่างที่เป็นประโยชน์ ตอนนี้คุณกำลังพูดถึงฟังก์ชั่นที่ไม่ปลอดภัยหรือไม่เนื่องจากการดูแลโดยเจตนาเช่นไม่หนีพารามิเตอร์ SQL คุณหมายถึงรหัสทดสอบไม่ปลอดภัยสำหรับสภาพแวดล้อมการผลิตหรือไม่ อย่างไรก็ตามตอนนี้ฉันอ่านความคิดเห็นของคุณเกี่ยวกับคำตอบของ Amon แล้วโดยทั่วไปคุณกำลังมองหาวิธีรักษาความปลอดภัยของฟังก์ชั่นของคุณ
user1122069

คำตอบ:


26

กำหนดประเภทเพื่อตกแต่งอินพุตที่ "ปลอดภัย" ในฟังก์ชั่นของคุณf()ยืนยันว่าอาร์กิวเมนต์มีประเภทนี้หรือโยนข้อผิดพลาด def g(x): return f(SafeInput(x))สมาร์ทจะเพียงพอที่จะไม่กำหนดทางลัด

ตัวอย่าง:

class SafeInput(object):
  def __init__(self, value):
    self._value = value

  def value(self):
    return self._value

def f(safe_string_to_eval):
  assert type(safe_string_to_eval) is SafeInput, "safe_string_to_eval must have type SafeInput, but was {}".format(type(safe_string_to_eval))
  ...
  eval(safe_string_to_eval.value())
  ...

a = f(SafeInput(local_file.some_str))

ในขณะที่สิ่งนี้สามารถหลีกเลี่ยงได้ง่ายมันทำให้ยากขึ้นโดยบังเอิญ ผู้คนอาจวิจารณ์การตรวจสอบแบบเข้มงวดเช่นนี้ แต่พวกเขาก็พลาดประเด็นนี้ไป ตั้งแต่SafeInputประเภทเป็นเพียงประเภทข้อมูลและไม่ได้เป็นระดับเชิงวัตถุที่สามารถ subclassed ตรวจสอบตัวตนสำหรับประเภทที่มีความปลอดภัยมากขึ้นกว่าการตรวจสอบสำหรับประเภทเข้ากันได้กับtype(x) is SafeInput isinstance(x, SafeInput)เนื่องจากเราต้องการบังคับใช้เฉพาะบางประเภทการเพิกเฉยต่อประเภทที่ชัดเจนและการพิมพ์เป็ดโดยปริยายก็ไม่เป็นที่น่าพอใจเช่นกัน Python มีระบบการพิมพ์ดังนั้นลองใช้มันเพื่อตรวจจับข้อผิดพลาดที่อาจเกิดขึ้นได้!


5
อาจจำเป็นต้องมีการเปลี่ยนแปลงเล็กน้อย assertถูกลบโดยอัตโนมัติเนื่องจากฉันจะใช้รหัสหลามที่ดีที่สุด สิ่งที่ต้องการif ... : raise NotSafeInputErrorจะเป็น
Fermi เส้นขนาน

4
หากคุณกำลังจะลงที่ถนนนี้ฉันขอแนะนำอย่างยิ่งให้ย้ายeval()ไปยังวิธีการSafeInputเพื่อให้คุณไม่จำเป็นต้องตรวจสอบประเภทที่ชัดเจน ตั้งชื่อให้บางส่วนที่ไม่ได้ใช้ในคลาสอื่น ๆ ของคุณ ด้วยวิธีนี้คุณยังสามารถเยาะเย้ยสิ่งทั้งหมดเพื่อการทดสอบ
Kevin

@Kevin: การเห็นว่าevalมีการเข้าถึงตัวแปรภายในเครื่องอาจเป็นไปได้ว่ารหัสนั้นขึ้นอยู่กับว่าตัวแปรจะกลายพันธุ์อย่างไร ด้วยการย้าย eval ไปยังวิธีอื่นมันจะไม่สามารถเปลี่ยนตัวแปรท้องถิ่นได้อีกต่อไป
Sjoerd Job Postmus

ขั้นตอนต่อไปอาจจะให้คอนSafeInputสตรัคเตอร์ใช้ชื่อ / หมายเลขอ้างอิงของไฟล์โลคัลและทำการอ่านเองเพื่อให้แน่ใจว่าข้อมูลนั้นมาจากไฟล์โลคัลแน่นอน
โอเว่น

1
ฉันไม่ค่อยมีประสบการณ์กับงูหลาม แต่มันจะดีกว่าหรือที่จะมีข้อยกเว้น? ในภาษาส่วนใหญ่สามารถปิดได้และเป็นเพราะฉันเข้าใจพวกเขา) มากขึ้นสำหรับการแก้จุดบกพร่องกว่าเพื่อความปลอดภัย ข้อยกเว้นและการออกจากคอนโซลรับประกันว่าจะไม่สามารถเรียกใช้รหัสถัดไปได้
user1122069

11

เมื่อโค้ดของ Joel ชี้ให้ทำรหัสผิดให้ดูผิด (เลื่อนลงไปที่หัวข้อ "ทางออกที่แท้จริง" หรืออ่านได้ดีขึ้นอย่างสมบูรณ์มันก็คุ้มค่าแม้ว่ามันจะอยู่ที่ตัวแปรแทนที่จะเป็นชื่อฟังก์ชั่น) ดังนั้นฉันแนะนำอย่างนอบน้อมให้เปลี่ยนชื่อฟังก์ชั่นของคุณเป็นอย่างอื่นf_unsafe_for_external_use()- คุณอาจจะคิดแบบย่อ ๆ ด้วยตัวเอง

แก้ไข: ฉันคิดว่าข้อเสนอแนะของ amon เป็นรูปแบบที่ดีมาก OO ในรูปแบบเดียวกัน :-)


0

การทำตามคำแนะนำโดย @amon คุณสามารถคิดถึงการรวมรูปแบบกลยุทธ์ที่นี่รวมถึงรูปแบบวัตถุวิธีการ

class AbstractFoobarizer(object):
    def handle_cond(self, foo, bar):
        """
        Handle the event or something.
        """
        raise NotImplementedError

    def run(self):
        # do some magic
        pass

และจากนั้นการดำเนินการ 'ปกติ'

class PrintingFoobarizer(AbstractFoobarizer):
    def handle_cond(self, foo, bar):
        print("Something went very well regarding {} and {}".format(foo, bar))

และการใช้งานของผู้ดูแลระบบอินพุต eval

class AdminControllableFoobarizer(AbstractFoobarizer):
    def handle_cond(self, foo, bar):
        # Look up code in database/config file/...
        code = get_code_from_settings()
        eval(code)

(หมายเหตุ: ด้วยตัวอย่างของโลกแห่งความจริงฉันอาจให้ตัวอย่างที่ดีกว่า)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.