sourcecode

os.system() 호출에서 벗어나는 방법?

copyscript 2023. 9. 11. 22:00
반응형

os.system() 호출에서 벗어나는 방법?

os.system()을 사용할 때 명령어에 매개 변수로 전달되는 파일 이름 및 기타 인수를 피할 필요가 있는 경우가 많습니다.이거 어떻게 해요?여러 운영 체제/셸에서 작동하지만 특히 bash용으로 작동하는 것이 좋습니다.

저는 현재 다음과 같은 작업을 수행하고 있지만, 이를 위한 라이브러리 기능이 있거나, 적어도 보다 우아하고 견고하며 효율적인 옵션이 있어야 한다고 확신합니다.

def sh_escape(s):
   return s.replace("(","\\(").replace(")","\\)").replace(" ","\\ ")

os.system("cat %s | grep something | sort > %s" 
          % (sh_escape(in_filename), 
             sh_escape(out_filename)))

편집: 인용문을 사용한다는 단순한 답변을 받아들였는데, 왜 그런 생각을 못 했는지 모르겠습니다. 제가 Windows에서 와서 '와 '는 조금 다르게 행동하기 때문인 것 같습니다.

보안과 관련하여 우려를 이해하지만, 이 경우 os.system()에서 제공하는 빠르고 쉬운 솔루션에 관심이 있으며 문자열의 소스는 사용자가 생성하지 않았거나 최소한 신뢰할 수 있는 사용자(나)가 입력했습니다.

shlex.quote() python 3 이후로 당신이 원하는 것을 합니다.

(python 2와 python 3을 모두 지원하는 데 사용하지만, 주의하십시오.pipes.10후 3.13될 3.13입니다 3.10되었으며 3.13에 3.1후 3.1입니다 3.1될 3.13 3.1s 3.1

이것이 제가 사용하는 것입니다.

def shellquote(s):
    return "'" + s.replace("'", "'\\''") + "'"

셸은 항상 따옴표가 붙은 파일 이름을 허용하고 문제의 프로그램에 전달하기 전에 주변 따옴표를 제거합니다.특히 공백이나 기타 고약한 셸 메타문자가 포함된 파일 이름의 문제를 방지할 수 있습니다.

업데이트: Python 3.3 이상을 사용하는 경우 자신의 롤 대신 shlex.quote를 사용합니다.

아마도 당신은 사용하는 특정한 이유가 있을 것입니다.os.system(). 하지만 그렇지 않다면 모듈을 사용해야 할 것입니다.파이프를 직접 지정하고 셸을 사용하지 않을 수 있습니다.

다음은 PEP324에서 가져온 것입니다.

Replacing shell pipe line
-------------------------

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

아마도요.subprocess.list2cmdline더 좋은 샷일까요?

그 파이프들을 주목하세요.인용문은 실제로 파이썬 2.5 및 파이썬 3.1에서 깨져서 사용하기에 안전하지 않습니다. 0-길이 인수를 처리하지 않습니다.

>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1  arg3

Python issue 7476을 참조하십시오. Python 2.6 및 3.2 이상 버전으로 수정되었습니다.

os.system은 사용자를 위해 구성된 명령어 셸이 무엇이든 호출하기 때문에 플랫폼 독립적인 방식으로는 할 수 없다고 생각합니다.내 명령 셸은 bash, emacs, ruby 또는 wake3에서 무엇이든 될 수 있습니다.이런 프로그램들 중 일부는 당신이 그들에게 전달하는 논쟁의 종류를 기대하지 않고 있고, 설사 그렇게 했다 하더라도 똑같은 방법으로 탈출한다는 보장이 없습니다.

참고: Python 2.7.x에 대한 답변입니다.

출처에 의하면pipes.quote()문자열을 /bin/sh 대한 단일 인수로 안정적으로 인용하는 방법입니다. (버전 2.7 이후로 사용되지 않고 파이썬 3.3에서 최종적으로 공개적으로 공개적으로 다음과 같이 노출되지만)shlex.quote()기능을 합니다.

반면에.subprocess.list2cmdline()는 "MSC 런타임동일한 규칙을 사용하여 인수 시퀀스를 명령줄 문자열로 변환"하는 방법입니다.

여기, 명령 줄에 대한 문자열 인용의 플랫폼 독립적인 방법이 있습니다.

import sys
mswindows = (sys.platform == "win32")

if mswindows:
    from subprocess import list2cmdline
    quote_args = list2cmdline
else:
    # POSIX
    from pipes import quote

    def quote_args(seq):
        return ' '.join(quote(arg) for arg in seq)

용도:

# Quote a single argument
print quote_args(['my argument'])

# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)

제가 사용하는 기능은 다음과 같습니다.

def quote_argument(argument):
    return '"%s"' % (
        argument
        .replace('\\', '\\\\')
        .replace('"', '\\"')
        .replace('$', '\\$')
        .replace('`', '\\`')
    )

즉, 항상 인수를 큰따옴표로 묶은 다음, 큰따옴표 안에 특수한 문자만 백슬래시 인용합니다.

Bash와에서는 Bash와 UNIX를 사용할 수 .shlex.quote수됩니다. 예를 들어, 공백과 python 3의 셸은할지도는한을기해고이서s,e해고en기을할지도n한는leo .*캐릭터:

import os
import shlex

os.system("rm " + shlex.quote(filename))

하지만, 이것은 보안 목적으로는 충분하지 않습니다!명령 인수가 의도하지 않은 방식으로 해석되지 않도록 주의해야 합니다.예를 들어, 파일 이름이 실제로 다음과 같은 경로일 경우에는 어떨까요?../../etc/passwd ? os.system("rm " + shlex.quote(filename))삭제할 ./etc/passwd현재 디렉터리에 있는 파일 이름만 삭제할 것으로 예상했을 때!여기서 문제는 셸이 특수 문자를 해석하는 것이 아니라 파일 이름 인수를 해석하지 않는다는 것입니다.rm단순한 파일명으로, 실제로는 경로로 해석됩니다.

이름이 를 한 이 로 하는 에는 에는 )?-f 이름을 . ? 하는 만으로는 하지 을 하지 만으로는 하는 을 다음을 사용하여 옵션을 비활성화해야 합니다.--아니면 당신은 한 번의 대시로 시작하지 않는 길을 지나야 합니다../-f가 되는 것은 서 가 를 하는 가 은 가 하는 를 가 서 은 ,rmcommand는 인수를 파일 이름이나 경로 또는 대시로 시작하는 경우 옵션으로 해석합니다.

다음은 보다 안전한 구현입니다.

if os.sep in filename:
     raise Exception("Did not expect to find file path separator in file name")

os.system("rm -- " + shlex.quote(filename))

저는 이러한 답변들이 윈도우 상에서 명령 줄 논쟁을 피하기에는 좋지 않은 생각이라고 생각합니다.그 결과를 바탕으로, 사람들은 '나쁜' 캐릭터를 모두 얻었다고 가정하고(그리고 바라면서) 블랙리스트 접근법을 적용하려고 노력하고 있습니다.윈도우는 매우 복잡하고 공격자가 명령줄 인수를 가로챌 수 있는 미래의 모든 종류의 문자가 발견될 수 있습니다.

나는 이미 몇몇 대답들이 윈도우에서 기본 메타문자들을 필터링하는 것을 소홀히 하는 것을 보았습니다 (세미콜론과 같은).제가 생각하는 접근 방식은 훨씬 간단합니다.

  1. 허용되는 ASCII 문자 목록을 만듭니다.
  2. 해당 목록에 없는 문자를 모두 제거합니다.
  3. 슬래시와 큰따옴표를 피합니다.
  4. 명령 인수를 악의적으로 꺾어서 공백으로 명령어를 전달할 수 없도록 전체 명령어를 큰따옴표로 둘러쌉니다.

기본적인 예:


def win_arg_escape(arg, allow_vars=0):
    allowed_list = """'"/\\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-. """
    if allow_vars:
        allowed_list += "~%$"

    # Filter out anything that isn't a
    # standard character.
    buf = ""
    for ch in arg:
        if ch in allowed_list:
            buf += ch

    # Escape all slashes.
    buf = buf.replace("\\", "\\\\")

    # Escape double quotes.
    buf = buf.replace('"', '""')

    # Surround entire arg with quotes.
    # This avoids spaces breaking a command.
    buf = '"%s"' % (buf)

    return buf

이 함수에는 환경 변수 및 기타 셸 변수를 사용할 수 있는 옵션이 있습니다.이 옵션을 활성화하면 위험이 커지므로 기본적으로 비활성화됩니다.

언급URL : https://stackoverflow.com/questions/35817/how-to-escape-os-system-calls

반응형