관리형 플랫폼 바깥의 세계
Vercel로 배포하고, Supabase로 DB 쓰고, 클릭 몇 번으로 대부분을 해결하다 보면 서버라는 게 존재한다는 사실을 잊기 쉽다. Claude Code에 "배포해줘"라고 하면 알아서 처리해 주니까.
그러다 어느 날 이런 말이 나온다.
"이건 AWS EC2에 올려야 해." "DigitalOcean Droplet 하나 빌려서 직접 돌려봐."
그 순간 터미널에 처음 보는 명령어가 나타난다.
ssh -i my-key.pem ubuntu@52.xx.xx.xx
이 한 줄이 뭔지 이해하지 못하면 서버에 발도 못 들인다. 뭔지 모른 채 어찌어찌 배포했다가 사고가 나는 경우도 있다. 2026년 기준, GitHub에서 유출된 비밀정보(API 키, SSH 키 등)가 2,865만 건에 달한다. 그중 상당수는 AI 코딩 도구로 만든 프로젝트에서 나왔다. AI가 제안하는 코드를 확인 없이 그대로 실행한 결과다.
비밀번호로 서버를 열어두면 무슨 일이 벌어지는가
서버를 처음 켜면 공인 IP 주소가 생긴다. 인터넷에 연결된 모든 컴퓨터에서 이 IP로 접속을 시도할 수 있다는 뜻이다. 비밀번호 방식으로 SSH를 열어두면 수십 초 안에 자동화된 봇들이 두드리기 시작한다.
아무 클라우드 서버에나 22번 포트를 열고 비밀번호 로그인을 허용해 두면 하루에 수천 번의 접속 시도가 들어온다. 이걸 무차별 대입 공격(brute-force attack)이라고 한다. "admin / admin123", "root / password", "ubuntu / ubuntu"처럼 흔히 쓰는 조합을 수백만 가지 순서로 자동으로 시도한다. 봇 입장에서는 그냥 숫자 돌리는 일이다.
"비밀번호를 복잡하게 만들면 괜찮지 않을까?" 사람이 기억할 수 있는 수준의 복잡도는 컴퓨터에겐 그리 어렵지 않다. 그래서 비밀번호 기반 SSH 로그인은 처음부터 끄는 게 원칙이다.
SSH 키가 작동하는 방식
SSH 키는 공개키(public key)와 개인키(private key), 두 개가 한 쌍이다.
공개키는 자물쇠고 개인키는 열쇠다. 자물쇠는 서버에 달아둔다. 누가 봐도 상관없다. 열쇠는 내 컴퓨터에만 보관한다. 접속할 때 내 컴퓨터는 "이 열쇠로 저 자물쇠를 열 수 있다"는 사실을 수학적으로 증명하는데, 이 과정에서 비밀번호나 열쇠 자체는 네트워크로 오가지 않는다.
핵심은 역산이 불가능하다는 것이다. 자물쇠(공개키)를 뜯어봐도 열쇠(개인키)를 만들어낼 수 없다. 개인키가 없으면 접속 자체가 막힌다.
2026년 기준 권장 알고리즘은 Ed25519다. 예전엔 RSA 4096비트를 많이 썼지만, Ed25519는 더 짧고 빠르면서도 더 강하다.
# SSH 키 생성 (Ed25519 방식)
ssh-keygen -t ed25519 -C "your@email.com"
실행하면 두 파일이 생긴다.
~/.ssh/id_ed25519: 개인키. 절대로 공유하지 않는다. GitHub에 올라가면 안 된다.~/.ssh/id_ed25519.pub: 공개키. 서버에 등록하거나 GitHub에 올리는 파일이다.
공개키를 서버에 등록하는 법
서버의 ~/.ssh/authorized_keys 파일에 공개키를 한 줄 추가하면 끝이다.
# 공개키를 서버에 자동으로 복사
ssh-copy-id -i ~/.ssh/id_ed25519.pub ubuntu@서버IP
이후에는 비밀번호 없이 키만으로 접속된다.
키 생성 시 패스프레이즈(passphrase)도 설정해 두는 게 좋다. 일종의 키 전용 비밀번호인데, 설정해 두면 개인키 파일 자체를 탈취당하더라도 패스프레이즈 없이는 쓸 수 없다. 이중 잠금 장치인 셈이다.
포트란 무엇이고 왜 닫아야 하는가
서버 하나에는 포트가 65,535개 있다. 건물 출입문에 비유하면 이해하기 쉽다. 서버가 건물이라면 각 서비스는 정해진 문 앞에서 대기한다. 누군가 그 문을 두드리면 응답하는 구조다.
자주 쓰는 포트들이다.
| 포트 | 용도 |
|---|---|
| 22 | SSH (서버 직접 접속) |
| 80 | HTTP (일반 웹사이트) |
| 443 | HTTPS (암호화된 웹사이트) |
| 5432 | PostgreSQL |
| 3306 | MySQL |
| 3000 | 로컬 개발 서버 (Node.js, Next.js 등) |
포트를 열어두면 외부에서 접근할 수 있다. 쓰지 않는 문을 열어두는 것과 같다. 문이 많을수록 공격받을 수 있는 지점도 늘어난다.
방화벽이 하는 일
방화벽은 포트 단위로 접근을 허용하거나 차단하는 문지기다. AWS에서는 보안 그룹(Security Group), DigitalOcean에서는 Firewall, 서버 내부에서는 ufw 명령어로 설정한다.
# Ubuntu 서버 방화벽 기본 설정
sudo ufw default deny incoming # 들어오는 접속은 기본 차단
sudo ufw default allow outgoing # 나가는 건 허용
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable # 방화벽 활성화
이렇게 하면 22, 80, 443번만 열리고 나머지는 모두 막힌다. 데이터베이스 포트(5432, 3306 등)는 외부에 열어두지 않는다. 같은 서버 안에서만, 즉 백엔드 코드에서만 접근하도록 제한하는 게 맞다.
SSH 기본 포트를 바꾸는 이유
22번 포트는 SSH의 기본값이다. 봇들이 가장 먼저 두드리는 번호이기도 하다. 22번을 다른 번호(예: 2222)로 바꾸는 것만으로 자동화 봇 공격 시도가 눈에 띄게 줄어든다. 완벽한 방어는 아니지만, 가장 손쉬운 첫 번째 층이다.
# /etc/ssh/sshd_config 파일에서 수정
Port 2222 # 기본 22 대신
# 저장 후 재시작
sudo systemctl restart sshd
이후 접속할 때는 포트 번호를 명시한다.
ssh -p 2222 -i ~/.ssh/id_ed25519 ubuntu@서버IP
Claude Code에 서버 작업을 시킬 때
Claude Code, GitHub Copilot, Cursor 같은 AI 코딩 에이전트에게 서버 관련 작업을 맡길 때 한 가지만 기억하면 된다. 개인키는 코드에 절대 들어가면 안 된다.
"서버 배포 자동화해줘"라고 하면, 간혹 코드 안에 SSH 개인키 내용을 직접 넣는 방식을 제안할 때가 있다. 그 제안은 따르지 않는다.
# 이렇게 하면 안 된다
PRIVATE_KEY="-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEA...
-----END OPENSSH PRIVATE KEY-----"
GitHub Actions 같은 CI/CD에서 서버에 접속해야 한다면, 키는 GitHub의 Secrets에 저장하고 환경변수로 불러온다.
# GitHub Actions에서 올바른 방식
- name: 서버에 배포
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
echo "$SSH_PRIVATE_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
ssh -i /tmp/deploy_key ubuntu@서버IP "cd /app && git pull"
GitHub Actions용 SSH 키와 내가 직접 접속할 때 쓰는 키도 분리하는 것이 좋다. 자동화 키가 유출되더라도 내 계정 전체로 피해가 번지지 않도록 하는 것이다.
서버 처음 세팅할 때 확인할 것들
새 서버를 켰을 때 순서대로 처리할 항목이다.
- SSH 키 등록 후,
/etc/ssh/sshd_config에서PasswordAuthentication no설정 - SSH 기본 포트(22)를 다른 번호로 변경
- 방화벽 활성화, 필요한 포트만 열기 (SSH + 80 + 443)
- 데이터베이스 포트는 외부에 열지 않기
- 개인키는 로컬
~/.ssh/에만 보관, Git에는 커밋하지 않기 - CI/CD용 키는 별도 생성 후 플랫폼의 Secrets에만 저장
.gitignore에*.pem,*.key추가
Vercel이나 Cloudflare Pages 같은 관리형 플랫폼을 쓸 때는 대부분 신경 쓸 필요가 없다. 플랫폼이 처리해 준다. 직접 서버를 다루는 순간 이 기초들이 내 책임이 된다.
AI가 코드를 짜주는 건 좋지만, AI는 보안의 맥락을 모를 때가 있다. 2026년에 AI 생성 코드에서 비밀 유출 사고가 급증한 것도 AI 제안을 검토 없이 그대로 실행했기 때문이다. 어떤 포트를 열고, 어떤 키를 어디에 두는지, 이 판단만큼은 직접 내려야 한다.