안전하지 않은 코드가 실수로 사용되지 않도록하십시오


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

파일에 "이 문자열은 평가판입니다. 악의적 인 코드를 여기에 두지 마십시오"라는 주석을 추가 하시겠습니까?
user253751

@immibis 의견이 충분하지 않습니다. 물론 함수 문서에서 거대한 "경고 :이 함수는 eval ()"을 사용하지만 그보다 훨씬 안전한 것에 의존 할 것입니다. 예를 들어 (제한된 시간으로 인해) 항상 함수의 문서를 다시 읽는 것은 아닙니다. 나도 내가해야한다고 생각하지 않습니다. Murphy가 그의 답변에 링크 한 방법 같이 안전하지 않은 것을 알 수있는 더 빠른 (즉, 더 효율적인) 방법이 있습니다.
페르미 역설

@ Fermiparadox 당신이 질문에서 평가를 취할 때 유용한 예가되지 않습니다. 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타입은 단지 데이터 타입이고 서브 클래 싱 될 수있는 객체 지향 클래스가 아니기 때문에 와 type(x) is SafeInput타입 호환성을 검사하는 것보다 타입 아이덴티티를 검사 하는 것이 더 안전 isinstance(x, SafeInput)합니다. 특정 유형을 적용하고 싶기 때문에 명시 적 유형을 무시하고 암시 적 오리 타이핑을 수행하는 것만으로는 만족스럽지 않습니다. 파이썬에는 타입 시스템이 있으므로 가능한 실수를 잡기 위해 그것을 사용합시다!


5
그래도 약간의 변경이 필요할 수 있습니다. assert최적화 된 파이썬 코드를 사용하기 때문에 자동으로 제거됩니다. 같은 if ... : raise NotSafeInputError것이 필요할 것입니다.
Fermi paradox

4
어쨌든이 길을 가고 있다면, 나는 것 강하게 이동 추천 eval()의 방법으로 SafeInput당신이 명시 적 타입 검사가 필요하지 않습니다 그래서. 메소드에 다른 클래스에서 사용되지 않는 이름을 지정하십시오. 그렇게하면 테스트 목적으로 모든 것을 조롱 할 수 있습니다.
Kevin

@Kevin : eval지역 변수에 대한 액세스 권한이있는 것처럼 보이면 코드가 변수를 어떻게 변경하는지에 따라 코드가 달라질 수 있습니다. eval을 다른 방법으로 옮기면 더 이상 지역 변수를 돌연변이시킬 수 없습니다.
Sjoerd Job Postmus

추가 단계는 SafeInput생성자가 로컬 파일의 이름 / 핸들을 가져 와서 읽기 자체를 수행하여 데이터가 실제로 로컬 파일에서 오는지 확인하는 것입니다.
Owen

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))

그리고 관리자 입력 평가 구현

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.