Bash에서 "mv"를 사용하여 심볼릭 링크 경쟁 조건 방지

Bash에서 "mv"를 사용하여 심볼릭 링크 경쟁 조건 방지

다음과 같이 "echo"(또는 다른 Bash 내장 기능)를 사용하여 대상 파일(일반 Linux에서 루트로)에 안전하게 쓰고 싶습니다.

echo "foo" > /destination/dir/filename

그러나 문제는 일반 시스템 사용자가 /destination/dir에 액세스할 수 있으므로 심볼릭 링크 조건이 발생할 위험이 있다는 것입니다.

C를 사용할 때 TOC-TOU를 방지하는 방법에 대한 모든 "방법"을 읽었으므로 심볼릭 링크를 확인하거나 제거한 다음 열지 마십시오(일반적인 조언은 open()에 O_NOFOLLOW를 사용하는 것 같습니다).

그러나 이 모든 것(커널 open() 및 해당 플래그에 대한 액세스)은 Bash를 통해 가능하지 않습니다(또는 제가 틀렸습니까?).

그때 나는 생각했다

  • mktemp를 사용하여 임시 파일 만들기
  • chown+chmod 임시 파일을 적절히
  • 임시 파일에 쓸 내용을 씁니다.
  • Bash 매개변수 "-T"를 사용하여 임시 파일을 대상 디렉터리로 이동합니다.

따라서 일부 Bash 의사 코드(어떤 곳에서는 오류 검사가 없음)

TEMPFILE=$(mktemp)

chown root:root $TEMPFILE
chmod 0600 $TEMPFILE
echo "contents" > $TEMPFILE
mv -T $TEMPFILE /destination/dir/filename

방금 시스템 파일에 대한 심볼릭 링크로 "/destination/dir/filename"을 사용하여 테스트했지만 작동합니다. "mv"는 임시 파일을 "filename"으로 올바르게 이동하고 심볼릭 링크는 제거됩니다(내 의도였습니다). , 파일을 덮어쓰지 않습니다.

안전/경쟁 조건 등의 문제가 누락되었다고 생각합니까?

감사해요:-)

답변1

안전하게 존재한다고 가정해보자 /destination/dir. (그렇지 않은 경우 루트가 아닌 사용자가 하위 디렉터리에 액세스하거나 생성할 수 없도록 충분히 제한된 권한을 사용하여 최상위 디렉터리를 만듭니다. 그런 다음 계층 구조가 완료되면 권한을 완화합니다.)

mktemp한 가지 방법은 대상 디렉터리 내에 임시이지만 고유한 이름을 가진 파일을 만드는 것입니다. 그런 다음 해당 내용이 기록되고 마지막으로 mv대상에 기록됩니다.

중요한 점은 mv동일한 파일 시스템에서 소스와 대상을 사용할 때 이름 바꾸기 프로세스의 일부로 대상이 제거된다는 것입니다. 이는 rename(2)시스템 호출을 통해 커널 자체에서 수행되는 원자적 작업입니다.

이미 존재 하는 경우 newpath해당 항목에 액세스하려는 다른 프로세스에서 누락된 항목을 찾을 수 없도록 자동으로 교체됩니다 newpath. 그러나 이름이 바뀌는 파일을 참조하는 oldpath창이 나타날 수 있습니다 .newpath

여러분의 구현과 매우 유사한 간단한 구현은 다음과 같습니다.

base='/destination/dir'                # Use '.' for current directory
file='filename'

tf=$(mktemp "$base/XXXXXXXXXX.tmp")    # Created with mode 600
echo "contents" >"$tf"                 # Always double-quote variables when used
if ! mv -Tf "$tf" "$base/$file"
then
    echo "Error writing to $base/$file" >&2
    rm -f "$tf"                        # Clean up temporary file
fi

POSIX 세계에서는 이에 상응하는 것을 달성하는 mv -T것이 더 어렵습니다. 여기서는 임시 디렉토리를 생성할 수 있는 능력에 의존합니다. 실제 상황에서는 성공할 때까지 다른 디렉토리 이름을 반복하는 루프에서 처리하는 것이 가장 좋을 것입니다 mkdir. 그러나 여기서는 한 번만 생성하려고 합니다.

base='/destination/dir'                # Use '.' for current directory
file='filename'

td="$base/dir.$$.tmp"                  # Must be unique
if mkdir -m700 "$td"                   # Will fail if not unique
then
    echo "contents" >"$td/$file"
    if ! mv -f "$td/$file" "$base/"    # Overwrite/replace $base/filename, or fail
    then
        echo "Error writing to $base/$file" >&2
    fi
    rm -rf "$td"                       # Clean up temporary directory
else
    echo "Error creating temporary directory $td" >&2
fi

mktemp명령도 POSIX는 아니지만 다음이 있습니다.권장 사항 구현이를 위해 사용 가능합니다.

관련 정보