간단한 보안점검 에이전트 만들기 / 대화내용 생성 (Hook 활용)

소개

안녕하세요 20기 안드로이드 스터디 뽀로리 입니다.


첫 오프라인 네트워킹 뒷풀이에서 여러 선배님들의 이야기를 듣고 그 중 가장 간단(?)하게 해볼만한 것들을 추려서 작업해 봐도 될 것 같다는 생각이 들었고 조공을 바치는 느낌으로 아래의 주제로 결정하였습니다.(잘 부탁드립니다 선생님들. 굽신굽신 ~ 하이호 붙어!!)

  • '웹 서비스를 만들고 싶은데 보안은 어떻게 해야할지 걱정이 된다.'

  • '사례게시글을 쓸때 CLI 기준 프롬프트를 챕쳐하기가 너무 힘들다.'

  • Hook 을 이용해볼 순 없을까?

위의 주제를 가지고 작업한 프로젝트소스보안 리포트 결과물 그리고 LLM과 소통한 내용(Transcripts)등은 아래 링크에 정리되어 있습니다.

Source : https://github.com/lodosswkor/security-test-app
Report/Transcripts : https://lodosswkor.github.io/security-test-app/

진행방법

위의 주제를 처리하기 위해 총 3개의 섹션으로 작업을 구분하여 진행합니다.

1. 보안 취약점 점검을 당할 테스트용 웹 서비스 구현 ( 연습가능한 컨테이너 DB 포함 )
2. 보안 취약점을 점검할 Claude 의 Agents (전문가들) 구현 및 사용
3. Human/LLM Transcript 정리 확인 및 Hook 연결

개발스택은 여러 선배님들이 Ruby on Rails를 학습하셨다는 얘기를 듣고 Postgres + ROR로 선정 하였습니다.

A) 사용 Tools

  1. AI Tools
    Claude Code
    Claude App

  2. Base/3rd Party Tools
    (인프라) Docker / docker compose
    (transcript 생성) claude-code-transcripts on python 3.x
    (DB관리툴) DBeaver

Docker Desktop
https://www.docker.com/products/docker-desktop/

python ( anaconda )
https://www.anaconda.com/download

claude-code-transcripts
https://github.com/simonw/claude-code-transcripts

DBeaver (옵션)
https://dbeaver.io/download/

B) 사전작업

AI 를 동작시키기 전에, 우선 기간시스템인 Docker와 anaconda(혹은 Python)은 설치되어 있어야 합니다. 설치 및 사용법에 대해서 모두 작성할 순 없어서 아래 링크로 대체합니다.

- Anaconda 설치 : https://blog.naver.com/codeitofficial/223497697349
- Docker 설치 : https://loui3.tistory.com/278
- DBever 설치 : https://wikidocs.net/215908

C) AI 작업

1) 섹션1 : 보안 취약점 점검을 당할 테스트용 웹 서비스 구현 [전체 대화내역 확인]

보안 점검을 위한 테스트용 웹 페이지를 아래 내용을 참고해서 계획을 하고 필요한게 무엇인지 정리해봐
---
# 기술스택 
- infra : docker container / docker-compose
- language / framework : ruby on rails 
- DB : postgres
# 기능 
- 회원가입
- 로그인 
- 회원목록 
# 흐름 
- 회원가입 -> 로그인 -> 회원목록 -> 로그아웃의 흐름, 로그인한 회원만 목록을 볼 수 있다. 
---

위의 질문으로 claude code Opus 는 docker를 인프라로 사용하는 ruby on rails 프로젝트 구조를 저한테
제시하였고, 내용을 확인하고 "테스트를 위해 취약점을 내포한 방식으로 진행해줘." 라고 명령을 내렸습니다.

Claude : 보안 점검용 테스트 웹 페이지 계획을 정리하겠습니다.  
.... 생략 ...
security-test-app/
├── docker-compose.yml
├── Dockerfile
├── Gemfile
├── Gemfile.lock
└── app/
    ├── controllers/
    │   ├── sessions_controller.rb    # 로그인/로그아웃
    │   ├── users_controller.rb       # 회원가입/회원목록
    │   └── application_controller.rb
    ├── models/
    │   └── user.rb
    ├── views/
    │   ├── users/
    │   │   ├── new.html.erb          # 회원가입 폼
    │   │   └── index.html.erb        # 회원목록
    │   └── sessions/
    │       └── new.html.erb          # 로그인 폼
    └── ...

... 이하 생략 ...

뽀로리 : 테스트를 위해 취약점을 내포한 방식으로 진행해줘.

... 열라 계획함 ...

클로드코드 : 완료 
뽀로리 : 구현해줘. (해줘해줘해줘)
클로드코드 : 살려줘.. (농담)

[ 상세 내용 확인 클릭 ]

이렇게 두 프롬프트 만에 프로젝트가 제 Antigravity에 로딩되었습니다.


그럼 이제 정상적으로 동작하나 실행을 해보겠습니다. Antigravity / VSCode 의 새 terminal/cmd를 열고 아래의 명령어를 치고 enter 를 누릅니다.

> docker compose up --build 

역시나 한번에 되는건 없습니다. 빌드시 에러가 발생하는군요.


에러 메세지를 읽어보면 디펜던시를 찾아내려가는데 psych 라는 번들러가 설치가 제대로 안되고 있는것 같습니다. 따라서 아래와 같이 프롬프트를 날려서 클로드 코드에게 명령합니다.

18.11 18.11 An error occurred while installing psych (5.3.1), and Bundler cannot continue. 18.11 18.11 In Gemfile: 18.11 rails was resolved to 7.1.6, which depends on 18.11 railties was resolved to 7.1.6, which depends on 18.11 irb was resolved to 1.16.0, which depends on 18.11 rdoc was resolved to 7.1.0, which depends on 18.11 psych

이 번들러가 설치되지 않는데 디펜던시 에러인가 같아. 확인 좀 제대로 하자

[ 상세 내용 확인 클릭 ]

!) 이 에러를 포함하여 2가지 정도 오류가 더 발생하였습니다. 제가 공유한 전체 transcript에서 확인 가능합니다.

수정이 완료되고, 다시 terminal/cmd 창에서 container 를 docker compose 로 띄워봅니다.

> docker compose up --build 
..... 실행중 .....
> docker ps # 컨테이너가 실행되고 있나 확인 
CONTAINER ID   IMAGE                   COMMAND                  CREATED        STATUS        PORTS                                         NAMES
7ffbf9887bd9   security-test-app-web   "entrypoint.sh bash …"   15 hours ago   Up 13 hours   0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp   security-test-app-web-1
68e6e0045144   postgres:15             "docker-entrypoint.s…"   15 hours ago   Up 13 hours   0.0.0.0:5432->5432/tcp, [::]:5432->5432/tcp   security-test-app-db-1

위와 같이 docker ps 후 정상적으로 컨테이너가 떠 있는 것으로 확인 되면 브라우져에서 http://localhost:3000 으로 접근하여 제가 원하는 기능이 다 되어 있는지 확인 합니다.

이로써 테스트 당할 웹 서비스를 구성이 완료 되었습니다.

2) 섹션2 : 보안 취약점을 점검할 Claude 의 Agents 구현 및 사용 [전체 대화내역 확인]

참조문서 : 소프트웨어 개발 보안 가이드

- Claude Code 에러 발생!

위의 문서를 PDF로 다운받아 프로젝트 최상위 디렉토리에 붙여넣고 클로드 코드에게 아래와
같이 명령하였습니다. 그런데...

뽀로리 : 
블랙박스 테스트를 진행하는 (화이트 해커) 클로드 '화이트해커' 전문 agent를 만들고 싶어. 외부에서 테스트하는 화이트 해커이기 때문에 DB내부 암호화 같은 부분과 인프라적인 것들을 제외하고 순수하게 '모의해킹'을 통해 취약점을 검출해 줘야해. @행정안전부_소프트웨어_개발보안가이드_2021129.pdf 문서를 참고하여, agent가 할 수 있는 모든것들은 먼저 정리해줘 

클로드 코드 : PDF too larget, please double press esc to edit your message ....

용량이 크다는 에러가 나네요. 그래서 Claude App에서 진행해보기로 했습니다. (어제 되었다 안됬다 이상..)
먼저 저는 fileserver mcp 를 설치하지 않은 상태에서 파일을 제 컴퓨터에 직접 write 할수 없는 상태이므로,
아래와 같은 방식으로 진행하도록 했습니다.

Claude APP : 파일을 포함한 질문 > 결과 확인 > agent_structure.md 생성 > Claude Code로 전달
Claude Code : @agent_structure.md 를 프로젝트로 복사, 이 문서를 참고로 agent/skills 생성



위의 생성된 whitehacker_agent_structure.md 파일을 복사해 프로젝트 디렉토리에 agent_structure.md 파일로 붙여 넣고 Claude Code 에서 아래와 같이 시킵니다.

뽀로리 : @agent_structure.md 파일을 참고하여 '해당 프로젝트에 맞게' claude project scope에 skill(s)/agent(s) 구현해줘.
 
클로드코드 : 살려줘 ..... 

그럼 저같은 경우 root에 skill 디렉토리를 만들고 작업은 완료하였는데, 실제로는 .claude 디렉토리 밑에 넣어야 하기 때문에 손으로 폴더를 이동시켰습니다. ( @.claude/ 밑에 작업해줘 라는 말을 까먹었네요. )

Skill 실행 : security-checker skill을 이용한 Report 뽑기. [전체 대화내용 확인]

위에처럼 프로젝트의 .claude 디렉토리에 만들어진 skills 넣으면 디렉터로 명으로 skills가 로딩됩니다.
claude code를 exit 하고 다시 실행합니다.

> claude --continue --dangerously-skip-permissions 

실행이 되면 / 를 치면 아래와 같이 skills가 로딩된것을 볼 수 있습니다.


이제 일을 시켜 봅시다!

뽀로리 : /security-checker http://localhost:3000 을 분석해줘
클로드코드 : 스킬 사용중 ......
뽀로리 : 보고서를 @docs/ 디렉터리에 html로 만들어줘
클로드코드 : 죽여줘...

이렇게 일을 시키면 html 파일로 만들어 줍니다.

3) 섹션3 : Human/LLM Transcript 정리 확인 및 Hook 연결 (파이썬 필수)

'Claude 와 대화한 내역과 그 친구가 무슨생각을 했고 어떻게 답변했는지'를 편하게 확인할 수 있는 방법이
있는지 찾아보다가 github에 있는 'claude-code-transcripts'를 활용해 보도록 하겠습니다.

Python 이 설치되어 있다는 조건에서 아래의 명령어로 설치합니다.

> pip install claude-code-transcripts # 설치

설치 후 실행을 할때 아래와 같이 합니다.

> claude-code-transcripts -o ./{대상디렉토리} 

현 위치의 my 라는 폴더에 우리가 선택한 세션을 transcript를 정리해서 저장(기본 html) 하게 됩니다.

- hook 생성으로 Session이 끝날때 자동 저장

https://code.claude.com/docs/ko/hooks-guide

SessionEnd : 세션이 끝날 때 실행되는 훅을 활용합니다. claude-code-transcripts 의 경우 SessionID를 인자로 받아 처리하는 기능이 있으므로, 그것을 활용하여 hook을 구성하였습니다.

Project scope 훅이므로, 아래와 같은 디렉토리 구조로 구성되며, 클로드코드 세션을 닫으면
'project_root/transcripts/{timestamp}_{sessionId 8자리}/*.html' 디렉토리에 자동으로 저장됩니다.


Sources:

# .claude/settings.json 
{
  "hooks": {
    "SessionEnd": [ # session이 끝나면~ 
      {
        "hooks": [
          {
            "type": "command",
            "command": "python .claude/hooks/session_export.py", 
            # session_export.py 실행해줘  
            "timeout": 120
          }
        ]
      }
    ]
  }
}

# .claude/hooks/session_export.py
# 맥에서 만든것이므로 Window 사용자의 경우 이 파일과 함께 
# 'Window 환경에서 동작하도록 변경해줘' 등으로 파일을 변경하도록 함. 
"""
Claude Code SessionEnd Hook
세션 종료 시 transcript를 HTML로 변환하여 저장
"""
import sys
import json
import subprocess
from pathlib import Path
from datetime import datetime


def main():
    # stdin에서 SessionEnd 데이터 읽기
    try:
        input_data = json.load(sys.stdin)
    except json.JSONDecodeError:
        sys.exit(0)

    session_id = input_data.get('session_id', 'unknown')
    transcript_path = input_data.get('transcript_path')
    cwd = input_data.get('cwd', '')
    reason = input_data.get('reason', '')  # 'exit', 'interrupt' 등

    # transcript 파일 확인
    if not transcript_path:
        sys.exit(0)
    
    transcript_file = Path(transcript_path)
    if not transcript_file.exists():
        sys.exit(0)

    # 출력 디렉토리 설정
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

    # transcript 경로 프로젝트 root/transcripts/{타임스템프}_{세션아이디앞 8자리}/*
    output_base = Path(cwd) / 'transcripts'
    output_dir = output_base / f'{timestamp}_{session_id[:8]}'
    output_dir.mkdir(parents=True, exist_ok=True)
    

    # claude-code-transcripts로 HTML 변환
    try:
        subprocess.run(
            [
                'claude-code-transcripts', 'json',
                str(transcript_file),
                '-o', str(output_dir),
                '--json'  # 원본 JSONL도 함께 저장
            ],
            check=True,
            capture_output=True,
            timeout=60
        )
    except subprocess.CalledProcessError:
        sys.exit(0)
    except subprocess.TimeoutExpired:
        sys.exit(0)
    except FileNotFoundError:
        # claude-code-transcripts가 설치되지 않은 경우
        sys.exit(0)

    sys.exit(0)


if __name__ == '__main__':
    main()


결과와 배운 점

실제 Claude app 에서 보안점검 Agents 설계에선 recon (서비스 탐색) 부분이 있었는데,
Claude Code 에서 markdown 을 확인하여 '이 프로젝트에 맞게' 라는 문장이 프롬프트에 들어가서 서비스 검색(정찰)부분이 축소되고 해당 테스트 프로젝트에 맞게만 agent/skills 가 구성된것 같아 아쉽다.

이 agent/skills를 확장시켜 클로드 코드에서 활용시 playwright MCP로 브라우져를 자동동작시키며
증거를 수집하게 하는게 더 직관적일 듯 하고, 이 설계대로 python + selenium 같은 것으로 AI 없이
동작시킬 수 있는 범용테스팅 툴을 만들 수 있을까? 하는 생각이 든다.


!) 디비확인법 : 위의 앱을 순차적으로 진행시키면. 프로젝트에 docker-compose.yml 파일이 생기는데 그 파일을 열면 아래와 같은 부분이 있으니 확인하시고 https://wikidocs.net/215908 를 참조해서 연결해 주세요.. 체력의 한계...

도움 받은 글 (옵션)

3
3개의 답글

뉴스레터 무료 구독

👉 이 게시글도 읽어보세요