Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

25 Commits

Folders and files

Repository files navigation

퐁퐁마켓

퐁퐁마켓은 화이트햇 스쿨 소프트웨어 개발 실습·시큐어 코딩 과제를 위해 만든 Django 기반 중고거래 플랫폼입니다. 회원, 상품, 실시간 전체채팅과 DM, 신고와 자동 차단, 원화형 테스트 지갑, 카카오페이 테스트 충전, 관리자 관리 기능을 하나의 로컬 실행 환경에서 확인할 수 있습니다.

이 README는 저장소를 처음 내려받은 사용자가 다른 문서를 먼저 읽지 않아도 다음 사항을 판단하고 재현할 수 있도록 작성했습니다.

  • 무엇이 구현되어 있고 무엇이 구현되어 있지 않은지
  • 일반 Python 실행과 Docker HTTPS/WSS 실행의 차이
  • 필요한 프로그램, 환경변수, Secret key와 콜백 설정
  • 일반 사용자·정지 사용자·staff·superuser의 권한 차이
  • 기능별 사용 방법과 주요 URL
  • 자동 테스트, 수동 점검, 데이터 백업·초기화 방법
  • 현재 보안 통제와 공개 운영 전 남아 있는 한계

1. 현재 구현 및 검증 상태

Phase 0부터 Phase 11까지의 구현과 보안 보완은 모두 main에 통합되어 있습니다. 실행과 기능 평가의 유일한 기준 브랜치는 main이며, 최종 제출 시점은 v1.0.0-submission 태그로 고정합니다.

최종 통합 검증에서 다음 결과를 확인했습니다.

구분 확인 결과
Django 호스트 테스트 142/142 통과
Docker 컨테이너 테스트 142/142 통과
일반 실행 Python 가상환경, Django 개발 서버, SQLite
Docker 실행 Caddy HTTPS/WSS, Daphne ASGI, SQLite named volume
실제 브라우저 흐름 회원가입·상품·전체채팅·DM·신고·지갑·관리자 통과
카카오페이 테스트 CID TC0ONETIME ready·QR·approve 확인
보안 점검 Django check, Bandit, pip-audit, Gitleaks, Grype, ZAP 및 수동 검증
저장소 공개 상태 2026-07-24 확인 기준 PUBLIC

카카오페이 검증은 실제 현금이 결제되는 운영 결제가 아닙니다. 개발용 Secret key와 테스트 CID TC0ONETIME을 사용하는 카카오페이 테스트 흐름입니다. 운영 CID, 현금 출금, 자동 환불, 사용자 간 실제 정산은 구현하지 않았습니다.

2. 구현된 기능 한눈에 보기

2.1 회원과 공개 프로필

  • Django 인증 기반 회원가입, 로그인, 로그아웃, 비밀번호 변경
  • 닉네임과 소개글을 관리하는 마이페이지
  • 로그인 username과 공개 닉네임·public_id 분리
  • 닉네임 기반 사용자 검색과 공개 프로필
  • 일반 로그인과 관리자 로그인 분리
  • 정지 사용자에 대한 주요 행동 제한
  • 로그인·관리자 로그인 반복 실패 제한

비밀번호와 지갑 2차 비밀번호는 평문으로 저장하지 않습니다. 공개 화면과 URL에는 순차 사용자 ID나 로그인 username 대신 UUID 기반 public_id와 닉네임을 사용합니다. UUID는 권한검사를 대신하지 않으므로 서버에서도 소유자·참여자 권한을 별도로 확인합니다.

2.2 상품

  • 상품 등록, 전체 목록, 상세, 검색, 수정, 삭제
  • 판매자만 자기 상품을 수정·삭제할 수 있는 서버 측 소유권 검사
  • 전자기기, 가구/인테리어, 의류, 도서/음반, 생활/주방, 스포츠/레저, 기타 카테고리
  • 등록된 카테고리 값을 기준으로 한 실제 필터링
  • 판매중·판매완료 상태
  • JPG, JPEG, PNG, WEBP 이미지 업로드
  • 업로드 이미지 디코드·재인코딩을 통한 메타데이터와 후행 바이트 제거
  • 차단 상품의 공개 목록·검색 제외

이미지는 최대 5MB, 최대 1,200만 픽셀입니다. 서버는 확장자만 믿지 않고 Pillow로 이미지를 해석하고 허용 형식으로 다시 저장합니다. 상품 설명은 최대 2,000자이며 가격은 음수가 될 수 없는 정수 원 단위입니다.

2.3 전체채팅과 DM

  • Django Channels와 WebSocket 기반 전체 실시간 채팅
  • 두 참여자만 접근할 수 있는 1:1 다이렉트 메시지
  • 상품 상세의 CSRF 보호 POST 버튼으로 판매자와 DM 시작
  • 새 DM 수신 알림과 읽지 않은 메시지 수
  • 모든 화면 우측 하단의 플로팅 메신저에서 전체채팅·DM 전환
  • /products/에서 상품 목록과 플로팅 전체채팅을 동시에 사용
  • 전송한 메시지의 데이터베이스 저장
  • 허용 Origin, 로그인 세션, 정지 상태의 연결·수신 시점 재검사

채팅 메시지는 최대 1,000자입니다. 기본 발송 제한은 대화 대상별 5초 동안 5개, 60초 동안 30개입니다. 현재 제한 저장소와 channel layer는 메모리 기반이므로 한 Daphne 프로세스 또는 한 개발 서버에서 사용하는 로컬 과제 환경에 적합합니다.

2.4 신고와 자동 필터링

  • 상품 신고와 사용자 신고
  • 신고 사유 최대 1,000자 저장
  • 자기 자신 또는 자기 상품 신고 거부
  • 같은 신고자의 같은 대상 중복 신고 거부
  • 서로 다른 유효 신고자 3명 이상이면 상품 자동 차단
  • 서로 다른 유효 신고자 3명 이상이면 사용자 자동 정지
  • 신고 접수·상품 차단·사용자 정지의 별도 moderation 감사 기록
  • Django Admin에서 신고와 감사 기록 검토

정지 사용자는 상품 등록·수정, 신고, 채팅, 송금을 할 수 없습니다. 정지 전에 등록한 자기 상품을 삭제하는 것은 허용합니다. 단순 임계치 방식이므로 공개 운영 서비스에 필요한 이의제기와 수동 승인 절차까지 제공하는 것은 아닙니다.

2.5 지갑, 사용자 간 송금, 카카오페이 테스트 충전

  • 신규 사용자별 원화형 테스트 지갑
  • 가입 시 테스트용 기본 잔액 10,000원
  • 카카오페이 결제수단과 6자리 2차 비밀번호를 설정하는 최초 지갑 설정
  • 닉네임·공개 UUID를 사용하는 사용자 간 내부 테스트 송금
  • 송금 내역과 카카오페이 충전 내역
  • 행 잠금과 데이터베이스 transaction을 사용한 원자적 잔액 변경
  • 카카오페이 테스트 ready·approve 후 정확히 한 번만 잔액 증가
  • 불확실하거나 불일치한 외부 승인 결과의 REVIEW_REQUIRED 격리
제한 값
신규 지갑 기본 잔액 10,000원
지갑 최대 잔액 1,000,000,000원
한 번의 내부 송금 최대값 1,000,000,000원
카카오페이 최소 충전 1,000원
카카오페이 최대 충전 1,000,000원

송금액은 1원 이상이어야 하고 잔액을 초과할 수 없으며, 자기 자신에게 송금할 수 없습니다. 송신자와 수신자 모두 활성 사용자여야 합니다. 카카오페이 충전은 브라우저가 성공 URL로 돌아왔다는 이유만으로 반영하지 않고 서버가 제공자 승인 응답의 TID, 주문, 사용자, 금액, AID를 확인한 뒤 한 번만 반영합니다.

2.6 관리자

  • 별도 관리자 로그인 페이지
  • 실제 데이터로 집계되는 관리자 대시보드
  • 가입자·상품 등록 추이와 신고 유형 분포
  • 사용자, 프로필, 상품, 신고, 채팅, 지갑, 거래, 카카오페이 충전 관리
  • 대시보드에서 실제 Django Admin 관리 목록으로 이동
  • 일반 사용자 페이지를 관리자 계정으로 점검 가능
  • 기본 /admin/과 /admin/login/에 대한 404 응답

관리자 URL을 다르게 두는 것은 노출을 줄이는 보조 수단일 뿐 접근통제의 대체물이 아닙니다. 서버는 항상 is_staff, is_superuser, Django Admin 모델 권한을 검사합니다.

3. 역할과 권한

기능 비로그인 일반 회원 정지 회원 staff superuser
홈·공개 상품 조회 가능 가능 가능 가능 가능
회원가입·일반 로그인 가능 가능 계정 상태에 따라 제한 관리자 계정 로그인 거부 관리자 계정 로그인 거부
상품 등록·수정 불가 본인 상품만 불가 일반 사용자 기능 점검 가능 일반 사용자 기능 점검 가능
자기 상품 삭제 불가 가능 가능 권한에 따라 가능 가능
신고·채팅·송금 불가 가능 불가 일반 사용자 정책과 Admin 권한 적용 가능
Django Admin 불가 불가 불가 부여된 모델 권한만 전체
통합 관리자 대시보드 불가 불가 불가 불가 가능

staff와 superuser는 일반 로그인 페이지가 아니라 관리자 전용 로그인 페이지를 사용합니다. 로그인 후 일반 사용자 화면도 열어 기능을 확인할 수 있지만, Django Admin의 실제 관리 범위는 부여된 권한에 따라 결정됩니다.

4. 시스템 구성

4.1 일반 실행

브라우저
  ├─ HTTP 요청 ───────────────┐
  └─ WebSocket 연결 ──────────┤
                              ▼
                 Django 개발 서버 / ASGI
                              │
                  SQLite + local media

일반 실행은 설정과 기능 확인이 가장 간단합니다. DEBUG=1이며 기본 주소는 http://127.0.0.1:8000입니다. 데이터는 구현 폴더의 db.sqlite3와 media/에 저장됩니다.

4.2 Docker 실행

브라우저
   │ HTTPS / WSS
   ▼
Caddy proxy : 127.0.0.1:80, 127.0.0.1:443
   │ 내부 Docker network의 HTTP / WebSocket
   ▼
Daphne + Django Channels : app:8000
   │
   ├─ SQLite named volume
   ├─ media/static named volume
   └─ 읽기 전용 runtime secret files

Docker 경로는 공개 운영 배포가 아니라 localhost에서 HTTPS/WSS와 컨테이너 경계를 재현하는 선택형 실습 환경입니다. 호스트에는 Caddy의 80/443만 127.0.0.1로 공개하고 Daphne 8000은 Docker 내부 network에서만 사용합니다.

4.3 Django 앱과 핵심 모델

앱 핵심 모델 책임
accounts Profile 공개 UUID, 닉네임, 소개, 정지 상태
products Product 판매자, 상품 정보, 카테고리, 이미지, 공개·판매 상태
reports Report, ModerationAuditLog 신고 대상·사유, 자동 차단·정지 감사 기록
chat GlobalChatMessage, PrivateChatRoom, PrivateChatMessage 전체채팅, 두 참여자 DM, 읽음 시각
wallet Wallet, PointTransaction, KakaoPayCharge 잔액, 내부 송금 원장, 외부 테스트 충전 상태

사용자 삭제와 연결 데이터의 관계는 Django model의 CASCADE 또는 PROTECT 정책을 따릅니다. 카카오페이 충전 기록은 원장 추적을 위해 참조 사용자가 존재하는 동안 보호하고, 일반 상품·채팅·신고 테스트 데이터는 관련 사용자를 삭제할 때 정해진 cascade 정책으로 함께 정리됩니다. Django Admin에서 지갑 잔액이나 외부 승인 상태를 임의 수정하는 것은 정상 운영 절차로 사용하지 않습니다.

4.4 네트워크 연결

방향 대상 목적
브라우저 → Django/Caddy HTTP(S) HTML, 폼, 이미지, 관리자
브라우저 → Channels WS 또는 WSS 전체채팅과 DM
Django → open-api.kakaopay.com HTTPS 테스트 결제 ready·approve
브라우저 → KakaoPay HTTPS redirect 테스트 QR·사용자 승인
브라우저 → Google Fonts·jsDelivr HTTPS 웹폰트, Material Symbols, Bootstrap CSS

외부 인터넷이 차단되어도 서버와 핵심 Django 기능은 실행할 수 있지만 CDN 웹폰트·아이콘·Bootstrap 일부 표현은 대체 글꼴이나 단순한 모양으로 보일 수 있습니다. 카카오페이 충전은 외부 네트워크와 개발자 설정이 없으면 사용할 수 없습니다.

5. 저장소 구조

secure-coding/
├── AGENTS.md
├── README.md
├── README_CODEX_SETUP.md
├── docs/                       설계·테스트·보안·보고서
└── tiny-secondhand-platform/
    ├── manage.py
    ├── accounts/
    ├── products/
    ├── reports/
    ├── chat/
    ├── wallet/
    ├── config/
    ├── templates/
    ├── static/
    ├── deploy/
    ├── Dockerfile
    ├── compose.yaml
    ├── requirements.lock
    └── .env.example

모든 Django 명령과 Docker 명령은 tiny-secondhand-platform/에서 실행합니다. Markdown 문서는 저장소 최상위 또는 docs/에만 둡니다.

6. 기술 스택과 필요 환경

6.1 사용 기술

  • Python 3.12
  • Django 5.2.16
  • Django Channels 4.3.2
  • Daphne 4.2.2
  • Pillow 12.3.0
  • SQLite
  • Django Templates와 Bootstrap
  • Docker Compose
  • Caddy 2.11.4

정확한 Python 의존성과 해시는 tiny-secondhand-platform/requirements.lock에 고정되어 있습니다.

6.2 일반 실행에 필요한 프로그램

  • Git
  • Python 3.12
  • Python venv와 pip
  • WSL2 Ubuntu 24.04 또는 동등한 Linux 환경
  • 카카오페이 충전까지 확인하려면 카카오페이 개발자 애플리케이션과 개발용 Secret key

Ubuntu에서 python3 -m venv가 동작하지 않으면 python3-venv 패키지가 필요합니다.

6.3 Docker 실행에 추가로 필요한 프로그램

  • Docker Desktop
  • Docker Compose v2
  • Docker Desktop의 WSL integration
  • 호스트의 127.0.0.1:80과 127.0.0.1:443을 사용할 수 있는 상태

이미 다른 프로그램이 80 또는 443 포트를 사용하면 Compose proxy가 시작되지 않습니다.

7. 처음 내려받아 일반 실행하기

7.1 Clone과 가상환경

git clone https://github.com/aoistar-kr/secure-coding.git
cd secure-coding/tiny-secondhand-platform

python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --require-hashes -r requirements.lock

저장소가 아직 PRIVATE이면 GitHub 접근 권한이 있는 계정으로 인증해야 clone할 수 있습니다.

7.2 환경파일

cp .env.example .env
chmod 600 .env

카카오페이 충전까지 포함한 전체 기능을 확인하려면 .env를 다음과 같이 설정합니다.

테스트에 사용할 카카오페이 개발용 Secret key 값은 저장소와 별도로 제출되는 보고서의 맨 마지막 페이지에 기재되어 있습니다. 해당 값을 복사해 아래 KAKAOPAY_SECRET_KEY= 오른쪽에 입력합니다. Secret key가 포함된 보고서는 비공개 제출용이므로 공개 GitHub 저장소에 추가하거나 다른 사람에게 공유하면 안 됩니다.

DJANGO_SECRET_KEY=
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_CSRF_TRUSTED_ORIGINS=https://localhost
KAKAOPAY_MODE=live
KAKAOPAY_CID=TC0ONETIME
KAKAOPAY_SECRET_KEY=여기에_별도로_전달받은_개발용_Secret_key를_붙여넣기
KAKAOPAY_PUBLIC_BASE_URL=http://127.0.0.1:8000

실제 입력 방법은 다음과 같습니다.

nano .env

.env에서 아래 줄을 찾습니다.

KAKAOPAY_SECRET_KEY=

등호 오른쪽에 별도 제출 보고서의 마지막 페이지에 기재된 카카오페이 개발용 Secret key 값을 그대로 붙여 넣습니다. 등호 앞뒤에 공백을 넣거나 <, > 같은 안내용 문자를 함께 입력하면 안 됩니다.

KAKAOPAY_SECRET_KEY=실제로_전달받은_개발용_키_값

저장 후 일반 실행에서는 다음 명령으로 .env를 현재 셸에 적용합니다.

set -a
source .env
set +a

키 값을 화면에 출력하지 않고 설정 여부만 확인하려면 다음 명령을 사용합니다.

python -c 'import os; print("카카오페이 키 설정됨" if os.getenv("KAKAOPAY_SECRET_KEY") else "카카오페이 키 미설정")'

카카오페이 키 설정됨이 출력되면 python manage.py runserver로 일반 실행을 시작할 수 있습니다. Docker도 같은 .env를 사용하므로 값을 입력한 뒤 docker compose up -d를 실행하면 됩니다. 실제 키 값을 확인한다는 이유로 echo, printenv, 스크린샷을 사용하지 마십시오.

일반 DEBUG=1 실행은 DJANGO_SECRET_KEY가 비어 있어도 임시 키를 생성합니다. 서버 재시작 후에도 로그인 세션을 유지하려면 다음 명령으로 값을 만들어 .env에 넣습니다.

python -c 'import secrets; print(secrets.token_urlsafe(64))'

카카오페이 개발키가 없고 회원·상품·채팅·신고·내부 송금만 확인하려면 .env에서 다음과 같이 충전을 끌 수 있습니다.

KAKAOPAY_MODE=disabled
KAKAOPAY_SECRET_KEY=

KAKAOPAY_MODE=live인데 개발키가 없으면 DEBUG=1 서버의 다른 기능은 열 수 있지만 충전 요청은 설정 오류로 실패합니다. Docker는 DEBUG=0으로 시작하므로 live 모드에서 키가 없으면 안전하게 시작을 거부합니다.

7.3 환경 적용, DB 준비, 실행

Django는 임의의 .env 파일을 자동으로 읽지 않으므로 일반 실행에서는 같은 셸에 직접 export해야 합니다.

set -a
source .env
set +a

python manage.py migrate
python manage.py check
python manage.py runserver

브라우저에서 http://127.0.0.1:8000/에 접속합니다. KAKAOPAY_PUBLIC_BASE_URL을 http://127.0.0.1:8000으로 설정했다면 실제 접속 주소도 같은 origin을 사용하는 것이 안전합니다.

7.4 관리자 생성

서버와 별도의 터미널에서 같은 가상환경과 환경변수를 적용한 뒤 실행합니다.

python manage.py createsuperuser

생성한 계정은 일반 로그인 화면이 아니라 다음 관리자 로그인에서 사용합니다.

http://127.0.0.1:8000/secure-pongpong-admin/login/

8. 환경변수 전체 설명

변수 일반 실행 기본·권장값 Docker 동작 설명
DJANGO_DEBUG 기본 1 Compose가 0으로 고정 개발 오류 표시 여부
DJANGO_SECRET_KEY DEBUG에서는 비어 있어도 임시 생성 필수 세션·서명용 비밀키
DJANGO_ALLOWED_HOSTS localhost,127.0.0.1 같은 .env 값을 사용 허용 Host 목록
DJANGO_CSRF_TRUSTED_ORIGINS 필요 origin 목록 기본 https://localhost HTTPS CSRF 신뢰 origin
DJANGO_SESSION_COOKIE_AGE 기본 28800 동일 세션 수명, 300~86400초
DJANGO_DB_PATH 기본 db.sqlite3 /data/db/db.sqlite3 고정 SQLite 파일 경로
DJANGO_MEDIA_ROOT 기본 media/ /data/media 고정 업로드 저장 경로
DJANGO_STATIC_ROOT 기본 staticfiles/ /data/static 고정 collectstatic 결과
SECURITY_MAX_FAILURES 기본 5 동일 로그인·PIN 실패 제한 횟수
SECURITY_LOCKOUT_SECONDS 기본 300 동일 실패 제한 유지 시간
CHAT_RATE_LIMIT_BURST_MESSAGES 기본 5 동일 짧은 구간 메시지 수
CHAT_RATE_LIMIT_BURST_SECONDS 기본 5 동일 짧은 제한 구간
CHAT_RATE_LIMIT_SUSTAINED_MESSAGES 기본 30 동일 긴 구간 메시지 수
CHAT_RATE_LIMIT_SUSTAINED_SECONDS 기본 60 동일 긴 제한 구간
KAKAOPAY_MODE live 또는 disabled live 고정 외부 테스트 충전 활성화
KAKAOPAY_CID TC0ONETIME 운영 CID 거부 테스트 결제 CID
KAKAOPAY_SECRET_KEY 전체 기능 사용 시 필수 live 시작 시 필수 개발자 애플리케이션 Secret key
KAKAOPAY_PUBLIC_BASE_URL http://127.0.0.1:8000 https://localhost 고정 제공자 콜백 기준 origin
KAKAOPAY_HTTP_TIMEOUT_SECONDS 기본 10 동일 외부 API timeout, 1~10초

Docker의 HTTPS redirect, Secure cookie와 HSTS 관련 값은 compose.yaml에 로컬 검증용 값으로 명시되어 있습니다. 공개 운영 환경에 그대로 복사하지 말고 실제 도메인과 인증서 정책에 맞게 별도 검토해야 합니다.

9. 카카오페이 개발자 설정

카카오페이 개발자 웹 플랫폼에는 실제 사용할 origin을 등록합니다.

일반 실행:

http://127.0.0.1:8000

브라우저에서 localhost를 실제로 사용할 경우 추가:

http://localhost:8000

Docker:

https://localhost

등록하는 값은 origin이며 UUID가 포함된 개별 callback 경로가 아닙니다. 애플리케이션이 각 결제 준비 요청에서 다음 형태의 동적 URL을 전달합니다.

/wallet/charge/kakaopay/<charge_public_id>/approve/
/wallet/charge/kakaopay/<charge_public_id>/cancel/
/wallet/charge/kakaopay/<charge_public_id>/fail/

KAKAOPAY_PUBLIC_BASE_URL은 브라우저에서 사용하는 등록 origin과 정확히 일치해야 합니다. Secret key, TID, pg_token, 세션 쿠키는 README, Git, 스크린샷, 브라우저 JavaScript에 넣으면 안 됩니다.

10. Docker로 HTTPS/WSS 실행하기

10.1 비밀값 준비

cd secure-coding/tiny-secondhand-platform
cp .env.example .env
chmod 600 .env
python -c 'import secrets; print(secrets.token_urlsafe(64))'

출력값을 .env의 DJANGO_SECRET_KEY에 넣고 카카오페이 개발용 Secret key도 입력합니다. Compose는 KAKAOPAY_MODE=live를 사용하므로 전체 스택을 그대로 시작하려면 카카오페이 키가 필요합니다.

10.2 Build와 실행

docker compose config --quiet
docker compose build --pull
docker compose up -d
docker compose ps

정상 상태에서는 secret-init가 성공 후 종료되고, app과 proxy가 healthy가 됩니다. 앱은 시작할 때 migration과 collectstatic을 실행한 뒤 Daphne을 시작합니다.

docker compose logs --tail=100 app proxy

브라우저 주소:

https://localhost/

10.3 로컬 CA 신뢰

Caddy는 공개 인증기관이 아니라 프로젝트 로컬 CA로 localhost 인증서를 만듭니다. 신뢰 등록 전 브라우저 인증서 경고는 정상입니다.

공개 루트 인증서를 무시 경로로 복사합니다.

mkdir -p .local-certs
docker compose cp proxy:/data/caddy/pki/authorities/local/root.crt .local-certs/root.crt

Windows 현재 사용자 인증서 저장소에 선택적으로 등록합니다.

certutil -user -addstore Root "\\wsl.localhost\Ubuntu-24.04\home\<WSL사용자>\<저장소경로>\secure-coding\tiny-secondhand-platform\.local-certs\root.crt"

현재 실행한 Compose proxy에서 복사한 인증서인지 확인한 뒤 등록해야 합니다. 검증 후에는 certmgr.msc의 현재 사용자 → 신뢰할 수 있는 루트 인증 기관에서 해당 Caddy Local Authority 지문을 확인하고 제거할 수 있습니다. Caddy 개인 CA 키가 들어 있는 Docker volume은 공유하면 안 됩니다.

10.4 컨테이너 보안 경계

  • secret-init만 무시된 .env를 Compose secret으로 읽습니다.
  • secret-init는 Django와 KakaoPay 값을 0440 파일로 나누고 종료합니다.
  • 앱은 비밀파일을 읽기 전용으로 마운트하며 실제 키를 이미지나 앱 환경에 넣지 않습니다.
  • 앱은 UID/GID 10001, read-only root, 모든 capability 제거 상태로 실행됩니다.
  • proxy는 read-only root에서 필요한 NET_BIND_SERVICE capability만 가집니다.
  • SQLite 디렉터리와 파일은 시작 시 각각 0700과 0600으로 제한합니다.
  • 앱 8000 포트는 호스트에 publish하지 않습니다.
  • 앱·proxy·init에는 PID와 메모리 제한, health check가 적용됩니다.

이 구성은 로컬 과제의 방어 수준을 높이기 위한 것이며 외부 Secret Manager, 운영 DB 역할, 중앙 로그, 다중 인스턴스 가용성을 제공하는 것은 아닙니다.

10.5 종료와 데이터 보존

docker compose down

이 명령은 컨테이너와 network를 내리지만 named volume 데이터는 보존합니다.

docker compose down -v

-v는 SQLite, 업로드 이미지, 정적 파일, runtime secret volume과 로컬 CA를 삭제하는 파괴적 초기화입니다. 필요한 DB·media 백업과 대상 Compose project 이름을 확인한 경우에만 사용합니다.

11. 주요 URL

아래 URL은 일반 실행 기준입니다. Docker에서는 host를 https://localhost로 바꿉니다.

URL 로그인 용도
/ 선택 홈, 카테고리, 최근 상품
/accounts/signup/ 불필요 회원가입
/accounts/login/ 불필요 일반 회원 로그인
/accounts/mypage/ 필요 내 프로필과 활동
/accounts/profile/edit/ 필요 닉네임·소개 수정
/accounts/password/change/ 필요 비밀번호 변경
/accounts/users/ 필요 닉네임 기반 사용자 검색
/accounts/users/<public_id>/ 필요 공개 프로필
/products/ 선택 전체 상품·카테고리와 플로팅 채팅
/products/search/ 선택 상품명·카테고리 검색
/products/new/ 필요 상품 등록
/products/<product_public_id>/ 선택 상품 상세
/products/<product_public_id>/edit/ 판매자 상품 수정
/products/<product_public_id>/delete/ 판매자 상품 삭제
/reports/product/<product_public_id>/ 필요 상품 신고
/reports/user/<public_id>/ 필요 사용자 신고
/chat/global/ 필요 전체채팅 확장 화면
/chat/ 필요 DM 방 목록
/chat/private/start/<public_id>/ 필요·POST DM 시작
/chat/private/rooms/<room_public_id>/ 참여자 DM 대화
/wallet/ 필요 잔액·송금·충전 내역
/wallet/setup/ 필요 카카오페이·6자리 PIN 설정
/wallet/transfer/ 필요 내부 테스트 송금
/wallet/charge/kakaopay/ 필요 카카오페이 테스트 충전
/secure-pongpong-admin/login/ 관리자 관리자 로그인
/secure-pongpong-admin/ superuser 통합 대시보드
/secure-pongpong-admin/django/ staff 권한 Django Admin
/healthz/ 불필요 최소 health check

로그아웃과 DM 시작 등 상태를 바꾸는 작업은 CSRF 보호 POST 요청으로 처리합니다.

WebSocket endpoint:

/ws/chat/global/
/ws/chat/private/<room_public_id>/

브라우저 코드가 현재 페이지의 HTTP/HTTPS에 맞춰 WS/WSS를 선택하므로 사용자가 주소를 직접 열 필요는 없습니다.

12. 기능 직접 확인 순서

12.1 일반 사용자 통합 흐름

  1. 첫 번째 일반 계정으로 회원가입합니다.
  2. 마이페이지에서 닉네임과 소개를 수정합니다.
  3. 지갑을 열고 카카오페이 결제수단과 6자리 2차 비밀번호를 설정합니다.
  4. 카테고리와 유효한 이미지를 선택해 상품을 등록합니다.
  5. 로그아웃하고 두 번째 일반 계정을 만듭니다.
  6. /products/에서 해당 카테고리로 필터링해 상품을 찾습니다.
  7. 상품 상세에서 판매자에게 DM을 시작하고 메시지를 보냅니다.
  8. 플로팅 메신저에서 전체채팅을 전송하고 DM 알림을 확인합니다.
  9. 상품 또는 사용자를 한 번 신고해 신고 건수가 반영되는지 확인합니다.
  10. 두 계정 모두 지갑 설정 후 1원 이상의 테스트 금액을 송금합니다.
  11. 카카오페이 키가 있다면 1,000원 이상 테스트 충전을 진행하고 승인 후 잔액이 한 번만 증가하는지 확인합니다.

자동 차단·정지는 서로 다른 신고자 세 명이 필요합니다. 검증 계정을 만들 때 비밀번호나 PIN을 문서·스크린샷에 남기지 말고 검증 후 생성한 데이터와 이미지를 삭제합니다.

12.2 관리자 흐름

  1. python manage.py createsuperuser로 관리자 계정을 만듭니다.
  2. /secure-pongpong-admin/login/에서 로그인합니다.
  3. 대시보드의 사용자·상품·신고·채팅·지갑 카드를 확인합니다.
  4. 각 바로가기를 눌러 실제 Django Admin 목록으로 이동합니다.
  5. 신고 대상, 차단 상태, moderation 감사 기록을 확인합니다.
  6. 카카오페이 충전 상태와 마스킹된 제공자 식별자를 확인합니다.
  7. 일반 상품 페이지로 이동해 관리자 계정으로 사용자 화면을 점검합니다.

일반 로그인 /accounts/login/은 staff와 superuser 인증을 거부하도록 의도적으로 분리되어 있습니다.

13. 자동 테스트와 설정 검증

13.1 일반 실행

cd secure-coding/tiny-secondhand-platform
source .venv/bin/activate

python manage.py check
python manage.py makemigrations --check --dry-run
python manage.py test --verbosity 1

전체 테스트는 현재 142개입니다. 카카오페이 테스트는 provider-shaped test double을 사용하므로 이 명령이 실제 외부 결제를 만들지 않습니다.

Django 배포 경고를 별도로 확인하려면 카카오페이를 비활성화한 임시 환경에서 실행할 수 있습니다.

DJANGO_DEBUG=0 \
DJANGO_SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(64))')" \
DJANGO_ALLOWED_HOSTS="market.example.com" \
KAKAOPAY_MODE=disabled \
python manage.py check --deploy

live 카카오페이를 포함한 non-DEBUG 설정을 검사하려면 실제 개발키와 경로 없는 HTTPS KAKAOPAY_PUBLIC_BASE_URL도 필요합니다.

13.2 Docker

docker compose exec -T app python manage.py check
docker compose exec -T -e DJANGO_SETTINGS_MODULE=config.settings_test app \
  python manage.py test --verbosity 1

config.settings_test는 실행 중인 서비스의 HTTPS 설정을 약화하지 않고 Django 테스트 client만 정상적으로 동작하게 하는 운영 파생 테스트 설정입니다.

13.3 수동으로 확인해야 하는 부분

자동 테스트만으로 다음을 증명하지는 않습니다.

  • 실제 브라우저에서 CSS·반응형 화면이 정상인지
  • trusted localhost 인증서에서 HTTPS/WSS가 동작하는지
  • 두 브라우저 세션 사이 DM과 새 메시지 알림이 보이는지
  • 카카오페이 개발자 플랫폼 origin과 Secret key가 올바른지
  • 관리자 대시보드 집계와 Django Admin 링크가 실제 데이터에 맞는지

14. 데이터 위치, 백업, 복구와 초기화

14.1 일반 실행

데이터 기본 위치
SQLite tiny-secondhand-platform/db.sqlite3
업로드 이미지 tiny-secondhand-platform/media/
collectstatic 결과 tiny-secondhand-platform/staticfiles/
비밀 환경파일 tiny-secondhand-platform/.env

백업할 때는 서버를 먼저 중지하고 DB와 media를 같은 시점에 복사합니다. .env는 소스 백업이나 제출물에 포함하지 말고 별도 비밀 저장소에서 관리합니다.

새 로컬 DB로 다시 시작하려면 기존 DB와 media를 즉시 삭제하기보다 다른 이름의 백업 위치로 옮긴 뒤 python manage.py migrate를 실행하는 편이 안전합니다. 새 DB에는 superuser가 없으므로 다시 생성해야 합니다.

14.2 Docker

Compose는 다음 named volume을 사용합니다.

  • runtime_secrets
  • sqlite_data
  • media_data
  • static_data
  • caddy_data
  • caddy_config

확인:

docker volume ls --filter label=com.docker.compose.project=pongpong-market

백업은 앱 쓰기를 중지한 상태에서 SQLite와 media volume을 같은 시점으로 보존해야 합니다. 단순히 docker compose down한 뒤 다시 시작하면 데이터가 유지됩니다. docker compose down -v는 백업 없이 사용하면 안 됩니다.

일반 실행의 db.sqlite3와 Docker의 sqlite_data는 서로 다른 데이터셋입니다. 한쪽에서 만든 계정이나 상품이 다른 실행 방식에 자동으로 나타나지 않습니다.

15. 적용된 주요 보안 통제

  • Django 기본 인증·비밀번호 해시·CSRF middleware 사용
  • Django ORM 사용, 애플리케이션 raw SQL 없음
  • 상품 소유권과 DM 참여자 서버 측 검사
  • 공개 UUID와 로그인 username 분리
  • 신고 중복·자기 신고 방지와 정지 사용자 정책
  • 지갑 transaction·행 잠금·DB 제약·최대값 검증
  • 카카오페이 고정 API origin, 테스트 CID 강제, 중복 승인 방지
  • 업로드 크기·픽셀·형식 검사와 이미지 재인코딩
  • WebSocket Origin·세션·사용자 상태 재검사
  • 로그인·PIN·채팅 rate limit
  • 로그인된 응답의 Cache-Control: private, no-store
  • 요청별 nonce 기반 CSP, X-Frame-Options, nosniff, Permissions-Policy
  • 운영 모드의 HTTPS redirect, Secure cookie, HSTS
  • 안전한 400·403·404·429·500 화면
  • Docker 비루트 실행, read-only root, 제한된 capability와 내부 app port
  • 해시 고정 Python 의존성, 비밀파일과 로컬 DB의 Git 제외

세션은 기본 8시간이며 브라우저 종료 시 만료되고 요청마다 만료 시점이 갱신됩니다.

16. 알려진 제한과 평가 범위

README만으로 현재 환경을 평가할 때 다음 한계를 반드시 함께 봐야 합니다.

  • 일반 runserver는 개발용이며 공개 운영 서버가 아닙니다.
  • Docker도 localhost 실습 환경이며 공개 도메인·공인 인증서를 제공하지 않습니다.
  • SQLite는 DB 사용자 역할 분리, 네트워크 접근통제, 고가용성을 제공하지 않습니다.
  • InMemoryChannelLayer와 메모리 rate limit은 여러 app 프로세스가 공유하지 못합니다.
  • 관리자 MFA와 중요 작업 재인증은 구현하지 않았습니다.
  • 중앙 로그 수집·경보, 외부 Secret Manager, 자동 백업·복구 훈련은 없습니다.
  • CSP의 script에는 unsafe-inline을 허용하지 않지만 기존 동적 표현 때문에 style-src-attr 'unsafe-inline'은 남아 있습니다.
  • 사용자 검색은 닉네임, 상품 검색은 제목과 저장된 카테고리 기준이며 가격 범위·고급 정렬·pagination은 없습니다.
  • 이메일 인증·비밀번호 재설정 메일, 다중 상품 이미지, 즐겨찾기, 주문·배송 workflow는 과제 범위에 포함되지 않습니다.
  • 채팅은 기본 전송·저장·DM 알림을 제공하지만 메시지 수정·삭제·첨부파일·운영자 실시간 모니터링은 없습니다.
  • 외부 CDN에 접속하지 못하면 웹폰트·아이콘·Bootstrap 표현이 일부 달라질 수 있습니다.
  • 신고 임계치 정책은 단순하며 이의제기·오탐 복구 workflow가 없습니다.
  • 카카오페이 자동 환불, 주문 조회 기반 reconciliation, 출금, 현금 정산, chargeback은 없습니다.
  • Docker 이미지 스캔에는 upstream에서 수정 버전을 제공하지 않은 잔여 항목이 있으므로 “취약점 0개”라고 주장하지 않습니다.
  • 저장소가 private인 동안에는 권한 없는 평가자가 clone할 수 없습니다.

따라서 현재 구현은 “교수자가 로컬로 내려받아 모든 과제 기능과 보안 통제를 재현하는 환경”에는 적합하지만, 인터넷에 공개해 실제 돈과 개인정보를 처리하는 운영 서비스 수준은 아닙니다.

17. 문서와 증거

문서 확인할 내용
AGENTS.md 개발 규칙, 승인 범위, 완료 기준
docs/ORIGINAL_REQUIREMENTS.md 강의 슬라이드 원본 요구사항
docs/PROJECT_SPEC.md 프로젝트 기능 명세
docs/REQUIREMENTS_TRACEABILITY.md 요구사항·구현·검증 연결
docs/ARCHITECTURE.md 앱, 모델, trust boundary
docs/DEVELOPMENT_LOG.md Phase 0~11 의사결정과 검증 기록
docs/TEST_CHECKLIST.md 자동·브라우저 점검 항목
docs/SECURITY_REVIEW.md 누적 보안 검토
docs/PHASE11_SECURITY_ASSESSMENT.md 최종 공격표면·도구·잔여 위험
docs/KAKAOPAY_INTEGRATION.md 카카오페이 상태·콜백·보안 설계
docs/MAINTENANCE.md 유지보수·백업·향후 개선
docs/FINAL_REPORT.md Markdown 최종 보고서

스캐너 원본과 브라우저 캡처는 저장소에서 무시되는 output/ 아래에 보관될 수 있습니다. 결과 수치와 해석은 문서에 남기되 Secret key, 비밀번호, PIN, 세션, CSRF token, provider token, private CA key는 증거에 포함하지 않습니다.

18. 최종 제출 기준과 브랜치 정책

현재 GitHub 저장소는 Public이며 main이 유일한 실행·평가 기준입니다. Phase 1~11 작업 브랜치는 모두 main에 병합된 뒤 삭제했고, 개발 과정은 main의 커밋 기록과 docs/DEVELOPMENT_LOG.md에 보존합니다.

최종 제출 상태:

branch: main
tag: v1.0.0-submission

이후 수정이 필요한 경우 main에서 직접 작업하지 않고 다음처럼 짧고 목적이 드러나는 브랜치를 만듭니다.

codex/fix-간단한제목
codex/docs-간단한제목

변경을 검증해 main에 병합한 뒤 작업 브랜치는 삭제합니다. 현재 제출 이후 추가 수정은 예정되어 있지 않습니다.

공개 전 최종 확인에서는 다음을 완료했습니다.

  1. 변경사항을 main에 커밋하고 원격 저장소에 푸시했습니다.
  2. GitHub repository visibility를 Public으로 변경했습니다.
  3. 인증 없는 요청에서 저장소 페이지와 Raw README의 HTTP 200 응답을 확인했습니다.
  4. 현재 Git 이력과 제출 변경분에서 Gitleaks 비밀정보 0건을 확인했습니다.
  5. .env, SQLite, 업로드 media, 인증서 개인키, 비공개 보고서와 로컬 증거를 Git에서 제외했습니다.
  6. 일반 실행과 Docker 실행, 142개 테스트, 실제 브라우저 통합 검증 결과를 문서와 일치시켰습니다.

저장소 주소:

https://github.com/aoistar-kr/secure-coding

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages