0Pricing
Linux Command Line & Bash Scripting Mastery · 강의

안전한 비밀 정보 처리 및 환경 관리

표준 입력, 파일 및 정리된 환경을 사용하여 자격 증명이 프로세스 목록과 로그에 노출되지 않도록 합니다.

안전한 비밀 정보 처리 및 환경 관리은(는) CoddyKit의 무료 Linux Command Line & Bash Scripting Mastery 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Linux Command Line & Bash Scripting Mastery 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Linux Command Line & Bash Scripting Mastery 강의에는 총 4개의 강의가 포함되어 있습니다.

시크릿 위생이 중요한 이유

시크릿(API 키, 비밀번호, 토큰)은 모든 시스템에서 가장 민감한 데이터입니다. Bash 스크립트에서 이를 잘못 처리하는 것은 가장 흔하고 피해가 큰 보안 실수 중 하나입니다.

  • 프로세스 목록: 명령에 전달된 인수는 ps aux, /proc/<pid>/cmdline, 시스템 감사 로그에 표시되며 호스트의 모든 사용자가 볼 수 있습니다.
  • 셸 기록: 대화형으로 입력한 명령(때로는 스크립트도)은 ~/.bash_history에 기록됩니다.
  • 로그 파일: set -x 추적 정보, 애플리케이션 로그, CI/CD 출력에 변수 값이 포함될 수 있습니다.
  • 환경 유출: 자식 프로세스는 내보내진 시크릿을 포함하여 부모의 전체 환경을 상속합니다.

보안이 강화된 스크립트는 시크릿을 방사성 물질처럼 취급합니다. 노출 시간을 최소화하고 노출 범위를 제한하며 외부로 내보내기 전에 모든 것을 정제합니다.

프로세스 목록 공격 표면

시크릿을 명령줄 인수로 전달하면 시스템의 모든 사용자가 ps를 통해 즉시 읽을 수 있습니다. 이는 이론적인 문제가 아니며, 공유 호스팅과 컨테이너 환경에서 반복적으로 악용됩니다.

아래 코드는 문제와 해결 방법을 나란히 보여 줍니다.

#!/usr/bin/env bash
# DANGEROUS: password visible in 'ps aux' output
# curl -u "admin:SuperSecret123" https://api.example.com/data

# SAFE: pass credentials via stdin or a flag that reads from a file
# Many tools support reading secrets from stdin with '-' or dedicated flags:

# Option 1 — pipe the secret so it never appears in argv
echo 'SuperSecret123' | curl -u 'admin' --password-stdin \
  https://api.example.com/data 2>/dev/null || true

# Option 2 — write a temporary netrc and point curl at it
# (covered in a later scene)
echo 'Secret never touches the command line this way'

표준 입력에서 시크릿 읽기

가장 안전한 대화형 방식은 read -rs를 사용하여 실행 중에 시크릿을 읽는 것입니다. -s 플래그는 에코를 억제하여 문자가 화면에 표시되지 않도록 하고, -r은 백슬래시 해석을 방지합니다.

핵심 사항은 다음과 같습니다.

  • 변수를 내보내지 않으므로 자식 프로세스가 /proc/<pid>/environ을 통해 볼 수 없습니다.
  • 사용한 후에는 노출 시간을 줄이기 위해 즉시 변수 설정을 해제하십시오.
  • echo "$SECRET"은 피하십시오. 값이 후행 줄 바꿈으로 손상되는 것을 막고 추적 정보에도 표시되지 않도록 printf '%s'를 사용하십시오.
#!/usr/bin/env bash
set -euo pipefail

# Prompt on stderr so stdout stays clean for piping
read -rsp 'Enter API token: ' API_TOKEN <&2
printf '\n' >&2

# Use the secret — printf keeps it out of argv
response=$(printf '%s' "$API_TOKEN" | curl -sS -X POST \
  -H 'Content-Type: application/json' \
  --data-binary @- \
  https://httpbin.org/post 2>/dev/null) || true

echo "Request sent."

# Scrub immediately — unset removes it from shell memory
unset API_TOKEN

파일에 저장된 비밀 정보: 권한과 소유권

비밀 정보를 디스크에 저장해야 할 때(예: 서비스 계정 키), 파일 권한이 가장 중요한 방어 수단입니다.

  • 모드 0600 — 소유자만 읽고 쓸 수 있습니다. 그룹과 다른 사용자에게는 접근 권한이 없습니다.
  • 모드 0400 — 소유자에게 읽기 권한만 있습니다. 실수로 덮어써서는 안 되는 키에는 이 설정을 우선 사용하십시오.
  • 비밀 정보 파일은 ~/.secrets/ 또는 /run/secrets/와 같은 전용 디렉터리에 저장하십시오(후자는 많은 Linux 시스템에서 RAM을 기반으로 하는 tmpfs이며 재부팅할 때까지만 유지됩니다).
  • 확실한 .gitignore 없이 git이 추적하는 디렉터리 안에 비밀 정보 파일을 절대 두지 마십시오.
#!/usr/bin/env bash
set -euo pipefail

SECRETS_DIR="${HOME}/.secrets"
mkdir -p "$SECRETS_DIR"
chmod 700 "$SECRETS_DIR"   # directory: only owner can list contents

KEY_FILE="${SECRETS_DIR}/api_token"

# Write secret — atomically restrict permissions before writing content
install -m 0600 /dev/null "$KEY_FILE"
printf '%s' 'my-super-secret-token' > "$KEY_FILE"

echo "Permissions:"
ls -la "$KEY_FILE"

# Read back safely — no subshell, no echo
API_TOKEN=$(< "$KEY_FILE")
echo "Token length: ${#API_TOKEN} chars (value not printed)"
unset API_TOKEN

.netrc 파일을 사용한 curl

curl은 호스트 이름과 인증 정보를 연결하는 ~/.netrc 파일(또는 --netrc-file을 통한 임의의 경로)을 지원합니다. 이를 사용하면 인증 데이터를 명령줄과 스크립트 본문에서 완전히 분리할 수 있습니다.

파일 형식은 간단합니다.

machine api.example.com
  login admin
  password s3cr3t

권장 사항:

  • 항상 chmod 0600 ~/.netrc을 설정하십시오. 일부 시스템에서는 다른 사용자가 읽을 수 있는 파일을 curl이 거부합니다.
  • --netrc-file /run/secrets/netrc을 사용하여 tmpfs 기반 비밀 정보나 컨테이너에 주입된 비밀 정보를 가리키십시오.
  • trap을 사용해 EXIT 시 임시 netrc 파일을 정리하십시오.
#!/usr/bin/env bash
set -euo pipefail

TMP_NETRC=$(mktemp)
chmod 0600 "$TMP_NETRC"

# Trap ensures cleanup even on error or signal
trap 'rm -f "$TMP_NETRC"' EXIT

# Write credentials to the temp netrc
cat > "$TMP_NETRC" <<'EOF'
machine httpbin.org
  login myuser
  password mypassword
EOF

curl -fsS --netrc-file "$TMP_NETRC" \
  https://httpbin.org/basic-auth/myuser/mypassword \
  -o /dev/null -w 'HTTP %{http_code}\n' || true

# trap fires here: $TMP_NETRC is deleted
echo 'Temp netrc cleaned up by trap.'

환경 변수 관리

환경 변수는 스크립트에 비밀 정보를 주입하는 일반적인 방법입니다(12요소 앱, CI/CD 파이프라인). 그러나 환경 변수는 모든 자식 프로세스로 유출되며, 프로세스가 실행되는 동안 /proc/<pid>/environ에 나타납니다.

방어 패턴:

  • 비밀 정보를 즉시 로컬 변수로 가져온 다음 환경 변수를 해제하여 자식 프로세스가 이를 상속하지 못하게 하십시오.
  • 전체 상속 환경 대신 env -i 또는 인라인 할당을 사용하여 특정 명령에만 비밀 정보를 전달하십시오.
  • 비밀 정보 변수는 절대 export하지 마십시오. 가능하면 할당만 사용하십시오(export하지 않음).
#!/usr/bin/env bash
set -euo pipefail

# Simulate a secret arriving via environment (e.g., from CI system)
export DB_PASSWORD='hunter2'   # set by CI — we did not choose this

# Capture locally, then strip from environment immediately
db_password="$DB_PASSWORD"
unset DB_PASSWORD

# Verify the env var is gone before spawning any child process
if printenv DB_PASSWORD 2>/dev/null; then
  echo 'ERROR: DB_PASSWORD still in environment!' >&2
  exit 1
fi

echo 'Secret captured and env var scrubbed.'
echo "Password length: ${#db_password}"
unset db_password

set -x 추적에 비밀 정보가 나타나지 않게 하기

set -x(xtrace)는 디버깅에 매우 유용하지만, 비밀 정보를 포함하여 확장하는 모든 변수의 값을 표준 오류로 출력합니다. 이러한 추적 내용은 CI 로그나 시스템 로그에 남는 경우가 많습니다.

추적 기능을 유용하게 유지하면서 비밀 정보를 보호하는 전략:

  • { set +x; } 2>/dev/null을 사용하여 민감한 작업 주변에서 일시적으로 추적을 비활성화하십시오.
  • 그 후 set -x로 다시 활성화하십시오.
  • xtrace 출력을 별도의 파일 디스크립터로 리디렉션하여 공개 로그 스트림이 아닌 보호된 로그 파일로 보내십시오.
#!/usr/bin/env bash
set -euo pipefail
set -x   # tracing ON — safe for non-sensitive sections

echo 'Building application...'
SRC_DIR='/tmp/build'
mkdir -p "$SRC_DIR"

# Disable xtrace around secret handling (suppress the set +x line itself)
{ set +x; } 2>/dev/null

read -rsp 'Token (hidden from trace): ' SECRET_TOKEN <&2
printf '\n' >&2
token_len=${#SECRET_TOKEN}
unset SECRET_TOKEN

set -x  # tracing back ON

echo "Token captured (length=$token_len). Continuing build..."
ls "$SRC_DIR"

로그 파일에서 비밀 정보 제거하기

주의를 기울이더라도 비밀 정보가 로그 출력에 들어가는 경우가 있습니다. 특히 상세한 스크립트나 오래된 스크립트에서 자주 발생합니다. 알려진 패턴을 가리는 로그 기록 래퍼 함수는 추가적인 안전망이 됩니다.

이 패턴은 모든 로그 출력에 정규 표현식 기반 치환을 적용합니다. 이는 이미 다룬 다른 관리 방법을 대신하는 것이 아니라 최후의 방어 계층입니다.

#!/usr/bin/env bash
set -euo pipefail

# A logging function that scrubs common secret patterns before writing
log() {
  local line
  # Replace anything that looks like key=VALUE or password=VALUE
  line=$(printf '%s\n' "$*" \
    | sed -E 's/(password|token|secret|key)=[^[:space:]]*/\1=***REDACTED***/gi')
  printf '[%s] %s\n' "$(date -u '+%T')" "$line"
}

# Usage:
log 'Connecting to database with password=hunter2'
log 'Loaded API token=sk-abc123xyz secret'
log 'Build step completed successfully'   # unchanged

env -i를 사용한 격리된 환경

env -i는 완전히 비어 있는 환경에서 명령을 시작하여, 실수로 포함된 비밀 정보를 비롯한 상속된 변수가 자식 프로세스에 전달되지 않도록 합니다. 그런 다음 필요한 항목만 명시적으로 전달합니다.

환경 데이터를 외부로 유출할 수 있는 신뢰할 수 없는 스크립트, 빌드 도구 또는 타사 유틸리티를 실행할 때 특히 유용합니다.

#!/usr/bin/env bash
set -euo pipefail

# Polluted parent environment (simulating a CI runner)
export AWS_SECRET_ACCESS_KEY='AKIAIOSFODNN7EXAMPLE'
export GITHUB_TOKEN='ghp_faketoken123'
export HOME="$HOME"
export PATH="$PATH"

echo '--- Child sees full environment:'
env | grep -E 'AWS|GITHUB' | head -5

echo '--- Sanitised child (env -i) sees nothing secret:'
env -i HOME="$HOME" PATH="$PATH" TERM="${TERM:-dumb}" \
  bash -c 'env | grep -E "AWS|GITHUB" || echo "No secrets visible"'

unset AWS_SECRET_ACCESS_KEY GITHUB_TOKEN

tmpfs에 임시 비밀 정보 파일 저장하기

tmpfs는 RAM을 기반으로 하는 파일 시스템입니다. 여기에 기록한 파일은 디스크로 플러시되지 않으므로 스왑, 디스크 캐시 또는 스냅샷에 비밀 정보가 남을 위험이 사라집니다.

  • Linux에서는 /dev/shm과 /run/user/<uid>가 일반적으로 tmpfs 마운트입니다.
  • 항상 tmpfs 사용과 trap EXIT을 함께 사용하여 스크립트가 끝날 때 파일을 삭제하십시오.
  • 컨테이너(Docker, Kubernetes)에서는 비밀 정보를 tmpfs 볼륨으로 마운트하여 /run/secrets에 직접 연결할 수 있습니다.
#!/usr/bin/env bash
set -euo pipefail

# Prefer /run/user/$UID (user-owned tmpfs) or /dev/shm (world-readable dir!)
if [[ -d "/run/user/$UID" ]]; then
  TMPFS_DIR="/run/user/$UID"
elif [[ -d '/dev/shm' ]]; then
  TMPFS_DIR='/dev/shm'
else
  # Fallback: warn that disk will be used
  echo 'WARNING: No tmpfs available; using /tmp (disk-backed)' >&2
  TMPFS_DIR='/tmp'
fi

SECRET_FILE=$(mktemp "${TMPFS_DIR}/secret.XXXXXX")
chmod 0600 "$SECRET_FILE"
trap 'shred -u "$SECRET_FILE" 2>/dev/null || rm -f "$SECRET_FILE"' EXIT

printf '%s' 'my-runtime-token' > "$SECRET_FILE"
echo "Secret stored in: $SECRET_FILE"
df -T "$SECRET_FILE" | awk 'NR==2 {print "Filesystem type:", $2}'

# Use the secret...
token=$(< "$SECRET_FILE")
echo "Token length: ${#token}"
unset token
# trap fires on exit: file shredded

모든 기법 결합하기: 보안을 강화한 배포 스크립트

다음 스크립트는 이 과정에서 배운 모든 기법을 실제적인 배포 도우미로 결합합니다. 각 방어 계층이 서로를 어떻게 보강하는지 살펴보십시오.

  • 표준 입력 읽기와 -s — 터미널에 입력이 표시되지 않음
  • trap으로 정리하는 tmpfs 비밀 정보 파일
  • 환경 변수 정리 — 하위 프로세스를 실행하기 전에 비밀 정보를 해제함
  • xtrace 보호 — 민감한 코드 주변에서 추적을 일시 중지함
  • 로그 비식별화 — 로그에 기록하기 전에 안전망 역할을 하는 정규 표현식을 적용함
#!/usr/bin/env bash
set -euo pipefail

### 1. Redacting logger
log() {
  local msg
  msg=$(printf '%s' "$*" \
    | sed -E 's/(password|token|secret|key)=[^[:space:]]*/\1=***/gi')
  printf '[%s] %s\n' "$(date -u +%T)" "$msg"
}

### 2. tmpfs secret store
TMPFS_DIR="${XDG_RUNTIME_DIR:-/tmp}"
SECRET_FILE=$(mktemp "${TMPFS_DIR}/deploy_token.XXXXXX")
chmod 0600 "$SECRET_FILE"
trap 'rm -f "$SECRET_FILE"; log "Secret file cleaned up."' EXIT

### 3. Read secret without trace
{ set +x; } 2>/dev/null
read -rsp 'Deploy token: ' _tok <&2; printf '\n' >&2
printf '%s' "$_tok" > "$SECRET_FILE"
unset _tok
set -x

### 4. Scrub inherited env vars before subprocess
unset DEPLOY_TOKEN 2>/dev/null || true

log 'Starting deployment...'
# Simulate deploy using secret from file (token never in argv)
# curl -H "Authorization: Bearer $(< $SECRET_FILE)" https://api.example.com/deploy
log 'Deployment complete. token=hidden_by_redactor'

echo 'Done.'

지식 확인: 프로세스 목록을 통한 비밀 정보 노출

프로세스 목록을 통해 비밀 정보가 유출되는 방식과 이를 방지하는 방법을 제대로 이해했는지 확인해 보십시오.

과정 요약: 안전한 비밀 정보 처리

안전한 비밀 정보 처리와 환경 변수 관리 과정을 완료하셨습니다. 지금까지 다룬 내용을 간결하게 정리하면 다음과 같습니다.

  • 프로세스 목록: 비밀 정보를 명령줄 인수로 전달하지 마십시오. ps aux와 /proc/<pid>/cmdline에 나타납니다. 대신 표준 입력 파이프나 --netrc-file을 사용하십시오.
  • 표준 입력 읽기: read -rs를 사용하여 터미널에 입력이 표시되거나 셸 기록에 남지 않도록 대화형으로 비밀 정보를 받으십시오.
  • 파일 권한: 비밀 정보 파일에는 chmod 0600(또는 0400)을 적용해야 합니다. 원자적으로 생성하려면 install -m 0600을 사용하십시오.
  • netrc 파일: --netrc-file로 지정한 임시 파일에 인증 정보를 맡기고 trap EXIT으로 정리하십시오.
  • 환경 변수 관리: 비밀 정보를 로컬에 저장한 직후 비밀 정보 환경 변수를 unset하십시오. 불필요하게 export하지 말고, 자식 프로세스를 격리할 때는 env -i를 사용하십시오.
  • xtrace 보호: 민감한 코드를 { set +x; } 2>/dev/null ... set -x로 감싸 디버그 추적에 값이 유출되지 않게 하십시오.
  • 로그 비식별화: 최후의 안전망으로 sed 기반 로그 기록기를 사용하십시오.
  • tmpfs: 런타임 비밀 정보는 /run/user/$UID 또는 /dev/shm에 저장하여 디스크에 기록되지 않게 하고, 종료할 때 완전히 삭제하십시오.

심층 방어가 핵심 사고방식입니다. 하나의 조치만으로는 충분하지 않지만, 여러 조치를 겹쳐 적용하면 비밀 정보 유출을 매우 어렵게 만들 수 있습니다.

자주 묻는 질문

“안전한 비밀 정보 처리 및 환경 관리” 강의는 무료인가요?

네 — “안전한 비밀 정보 처리 및 환경 관리” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Linux Command Line & Bash Scripting Mastery 강의 전체를 잠금 해제할 수 있습니다. Linux Command Line & Bash Scripting Mastery 강의에는 총 4개의 강의가 포함되어 있습니다.

“안전한 비밀 정보 처리 및 환경 관리”에서 뭘 배우나요?

표준 입력, 파일 및 정리된 환경을 사용하여 자격 증명이 프로세스 목록과 로그에 노출되지 않도록 합니다. 브라우저에서 직접 실행하는 실습 코드로 Linux Command Line & Bash Scripting Mastery을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Linux Command Line & Bash Scripting Mastery을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Linux Command Line & Bash Scripting Mastery은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“안전한 비밀 정보 처리 및 환경 관리” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Linux Command Line & Bash Scripting Mastery 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Linux Command Line & Bash Scripting Mastery 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 명령 및 인수 주입 방지
  2. 안전한 비밀 정보 처리 및 환경 관리
  3. 최소 권한 실행 및 sudo 관리
  4. ShellCheck를 활용한 정적 분석 및 감사
← Linux Command Line & Bash Scripting Mastery(으)로 돌아가기