본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 19. 01:04

AI가 생성한 Bash 스크립트를 실행하기 전 보안 점검하기

요약

AI가 생성한 Bash 스크립트의 잠재적 보안 위험과 파괴적인 오류를 방지하기 위한 체크리스트를 제공합니다. 안전한 실행을 위해 엄격한 프라그마 설정, 변수 확장 시 따옴표 사용, 단계별 실패 대응 방안을 확인해야 합니다.

핵심 포인트

  • set -euo pipefail 및 IFS 설정을 통해 스크립트의 안정성 확보
  • 변수 확장 시 반드시 따옴표를 사용하여 의도치 않은 경로 삭제 방지
  • 명령어 실패 시 스크립트가 중단되도록 설계하여 데이터 손실 예방
  • AI 생성 코드의 특성상 발생하는 논리적 허점과 보안 취약점 점검 필요

Bash는 AI가 작성하기 가장 쉬운 언어인 동시에, 파괴적인 결과물을 얻기 가장 쉬운 언어이기도 합니다. "그저 오래된 파일을 정리할 뿐"이라는 20줄짜리 스크립트가 모델이 변수가 항상 설정되어 있을 것이라고 가정했기 때문에 홈 디렉터리를 재귀적으로 삭제할 수 있습니다. "단순한 로그 전송기"가 디버깅을 위해 set -x를 사용하고 이를 제거하는 것을 잊어버려 사용자의 비밀 정보를 원격 서버로 전송할 수도 있습니다.

저 또한 실행해서는 안 될 AI 생성 Bash를 실행한 적이 있습니다. 제가 아는 대부분의 엔지니어들도 마찬가지입니다. 몇 번의 아찔한 상황을 겪은 후, 최악의 상황을 방지할 수 있는 짧은 체크리스트를 만들었습니다. 바로 이 체크리스트입니다.

AI 생성 Bash를 실행하기 전 확인해야 할 5가지 사항

1. 엄격한 프라그마(pragma)로 시작하는가?

사소하지 않은 모든 Bash 스크립트의 첫 줄은 다음과 같아야 합니다:

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

각 설정의 역할:

  • set -e — 명령어가 실패하면 종료합니다. 이것이 없으면 5행에서의 실패가 스크립트의 6~50행이 즐겁게 실행되는 것을 막지 못합니다.
  • set -u — 정의되지 않은 변수에 대해 에러를 발생시킵니다. 이는 rm -rf $UNDEFINED/와 같은 상황으로부터 당신을 구해주는 설정입니다.
  • set -o pipefail — 파이프(pipe)를 통해 실패를 전파합니다. 이것이 없으면 failing-command | grep something은 grep이 성공하기 때문에 전체 명령이 성공한 것으로 간주됩니다.
  • IFS=$'\n\t' — 합리적인 필드 분할(field splitting)을 수행합니다. 파일 이름의 단어 분할(word-splitting) 버그를 방어합니다.

만약 AI가 생성한 스크립트에 이 설정들이 없다면, 이를 추가하고 스크립트를 다시 읽으십시오. 그러면 프라그마가 이제 막 잡아내기 시작한 버그들을 자주 발견하게 될 것입니다.

2. 모든 변수 확장(variable expansion)에 따옴표가 붙어 있는가?

# 잘못된 예
rm -rf $TARGET_DIR

...

잘못된 버전은 "루트 디렉터리를 삭제해 버렸다"는 이야기들의 원인이 됩니다. 만약 $TARGET_DIR이 비어 있거나 공백을 포함하고 있다면, 명령어는 rm -rf (현재 디렉터리 삭제) 또는 rm -rf foo bar (의도하지 않은 두 가지 삭제)가 됩니다.

모델들은 절반 정도의 확률로 잘못된 버전을 기본값으로 제시하는데, 그 이유는 올바른 버전이 채팅에서 작성하기 더 어렵고("따옴표를 이스케이프(escape)하세요!") 잘못된 버전이 대부분의 블로그에서 보여주는 방식이기 때문입니다.

해결책 (Fix): AI가 생성한 Bash 스크립트를 읽을 때, 모든 $VAR에 따옴표가 있는지 머릿속으로 확인하세요. 누락되었다면 추가하세요. 이것이 Bash 재앙의 가장 큰 원인입니다.

3. 중간에 단계가 실패하면 어떻게 되나요?

AI는 다음과 같이 쾌활하게 작성할 것입니다:

mkdir -p /opt/new-app
cd /opt/new-app
tar xzf $TARBALL
...

만약 tar xzf가 실패한다면(손상된 tarball, 디스크 가득 참 등) 어떻게 될까요? set -e를 사용 중이라면 스크립트가 중단됩니다. 좋습니다. 하지만 set -e가 없다면, 스크립트는 계속 진행되어 rm $TARBALL을 실행하고 백업도 없이 tarball을 삭제해 버립니다.

상태를 변경하는 모든 스크립트에 대해 스스로에게 질문하세요: 각 단계에서, 해당 단계가 실패할 경우의 복구 경로(recovery path)는 무엇인가? 만약 대답이 "자동화된 것이 없음"이라면, 스크립트는 적어도 이전 단계가 성공했는지 확인하기 전에는 데이터를 삭제하지 말아야 합니다.

AI는 스스로 이런 점을 거의 고려하지 않습니다.

4. 로그에 비밀 정보(secrets)가 노출되나요?

AI가 생성한 Bash가 비밀 정보를 유출하는 가장 흔한 방식은 set -x를 통한 것입니다:

set -x  # 디버깅 (debugging)
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/...

set -x를 사용하면 확장된 변수를 포함하여 모든 명령어가 출력됩니다. 이제 당신의 API 토큰은 스크립트 출력물에 포함되고, 이는 CI 로그에 남게 되며, 프로젝트 접근 권한이 있는 누구에게나 노출됩니다.

해결책은 선택적입니다:

set +x  # 추적 (trace) 비활성화
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/...
set -x  # 다시 활성화

또는 디버깅이 끝나면 단순히 set -x를 제거하세요. 모델은 이를 자주 남겨둡니다.

5. 불필요하게 root 권한으로 실행되나요?

AI는 때때로 필요하지 않은 명령에까지 sudo를 모든 곳에 작성하곤 합니다. 또는 스크립트가 root로 실행된다고 가정하고, 쓰기 권한을 위해 root가 필요한 절대 경로를 사용하기도 합니다.

원칙: 명령어를 일반 사용자(non-root user)로 실행할 수 있다면, 그렇게 해야 합니다. 권한 부여 범위(privileged surface)가 작을수록, 피해 범위(blast radius)도 작아집니다.

이는 코드를 다운로드하고 실행하는 스크립트에서 특히 중요합니다. 흔한 패턴은 다음과 같습니다:

# 위험함: 권한이 부여된 다운로드 + 실행
sudo bash -c 'curl https://example.com/install.sh | bash'

...

모델이 첫 번째 패턴을 생성한다면, 반드시 두 번째 패턴으로 교체하세요. 항상 그래야 합니다.

실제 사례

지난달 저는 Claude에게 CI 러너(runner) 호스트에서 30일이 지난 Docker 이미지를 정리하는 스크립트를 작성해 달라고 요청했습니다. 첫 번째 초안은 다음과 같았습니다:

#!/bin/bash

DOCKER_IMAGES=$(docker images --format '{{.ID}} {{.CreatedAt}}')
...

체크리스트를 적용해 보겠습니다:

  1. 엄격한 프라그마(strict pragma) 부재. set -euo pipefail이 누락되었습니다.
  2. 따옴표로 감싸지 않은 $DOCKER_IMAGES, $ID, $DATE. 각각이 잠재적인 버그가 될 수 있습니다.
  3. 실패 처리(Failure handling). 이미지가 사용 중인 경우 docker rmi는 실패합니다. 스크립트는 계속 진행하며, 사용 중인 모든 이미지에 대해 조용히 실패(silently fails)합니다. 우리는 어떤 이미지가 정리되었고 어떤 것이 되지 않았는지 알 수 없습니다.
  4. 비밀 정보(secrets) 없음 (여기서 Docker는 이를 노출하지 않음). 하지만 스크립트가 무엇을 하고 있는지 로그를 남기지 않으므로, 나중에 감사(audit)할 수 없습니다.
  5. sudo 없음. 좋습니다. 사용자가 Docker 소켓(socket) 접근 권한을 가지고 있다고 가정하며, 이는 합리적입니다.

보안이 강화된(hardened) 버전:

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
...

이 수정에는 2분이 걸렸습니다. 체크리스트가 없었다면, 저는 원래 스크립트를 실행한 뒤 며칠이 지나서야 이미지의 절반이 사용 중이라 디스크 사용량이 실제로 줄어들지 않았다는 사실을 깨달았을지도 모릅니다.

Bash 린팅(linting)에 관한 짧은 참고 사항

shellcheck는 이러한 문제 대부분을 자동으로 잡아냅니다. 이 글에서 도구를 하나만 채택한다면, 그것은 바로 shellcheck여야 합니다:

shellcheck cleanup-images.sh

이 도구는 따옴표로 감싸지 않은 변수, 엄격 모드(strict mode) 누락, 그리고 수십 가지의 다른 패턴들을 표시해 줍니다. AI가 생성한 Bash 스크립트는 보통 적어도 하나 이상의 shellcheck 경고를 포함합니다.

저는 이제 스크립트를 실행하기 전에 모든 스크립트에 대해 shellcheck를 실행합니다. 2초밖에 걸리지 않으면서 제가 놓칠 수 있는 부분들을 잡아내 줍니다.

AI가 올바르게 작성할 때

공정하게 말하자면, 모델은 안전한 Bash를 생성할 능력이 충분히 있는 경우가 많습니다. 만약 명시적으로 프롬프트를 작성한다면 — "set -euo pipefail을 사용하여 작성하고, 모든 변수를 따옴표로 감싸며, 오류 발생 시 명확하게 실패하도록 작성해줘" — 깔끔한 스크립트를 얻을 수 있을 것입니다.

문제는 그러한 프롬프트 없이 "X를 수행하는 스크립트를 작성해줘"라고 요청하면, 가장 일반적인 (common) 형태의 스크립트, 즉 안전하지 않은 형태의 스크립트를 얻게 된다는 점입니다. 따라서 다음과 같은 원칙을 기억하세요.

프롬프트에 항상 안전 요구 사항을 포함하세요. 또는, 출력물을 반드시 강화 (hardening)가 필요한 초안으로 취급하세요. 이 두 가지 원칙 중 하나라도 지키지 않은 채 AI가 작성한 Bash 스크립트를 실행하지 마세요.

결론 (The bottom line)

AI가 생성한 Bash 스크립트는 빠르게 만들어지지만, 잘못 읽기(오해하기) 쉽습니다. 체크리스트는 간단합니다. 엄격한 프라그마 (strict pragma), 따옴표로 감싼 확장 (quoted expansions), 실패 경로 (failure paths), 로그 내 비밀 정보 (secrets in logs), 불필요한 권한 (unnecessary privilege) 등을 확인하는 것이며, 이를 적용하는 데는 스크립트당 몇 분밖에 걸리지 않습니다. 이를 건너뛰었을 때의 부작용은 "사소한 정리 실수"에서부터 "커리어에 치명적인 사고"에 이르는 넓은 범위에 걸쳐 있습니다. 점검을 하지 않을 핑계는 없습니다.

Bash에 특화된 프롬프트에 대해서는 bash-script-code-review와 관련 있는 linux-server-hardening 프롬프트를 참조하세요. 두 프롬프트 모두 관련 영역을 다루고 있습니다.

이 기사는 원래 DevOps AI ToolKit — 클라우드 엔지니어를 위한 실용적인 AI 워크플로우 — 에서 게시되었습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0