5가지 교훈
1. 「설치·확장」 기능은 서버 실행 권한입니다
관리 화면의 플러그인·어댑터·패키지 설치 기능이 일반 로그인 사용자에게 열려 있었고, 설치 때 스크립트 실행이 허용되었으며, 앱은 root 로 돌고 있었습니다. 공격자는 가입 후 악성 패키지를 수백 번 «설치»했고, 그때마다 서버에서 명령이 실행됐습니다. 결과는 오류 응답에 실어 가져갔습니다.
AI 에게 이렇게 요청하세요: 「외부 패키지·URL·파일을 설치하거나 실행하는 기능은 관리자만 쓰게 하고, 설치 스크립트 실행은 끄고, 앱은 root 가 아닌 전용 계정으로 실행해 줘.」
2. 서버 하나가 뚫리면 SSH 키 하나로 전부 열립니다
한 서버에 있던 SSH 개인키가 외부로 나갔고, 그 키가 다른 서버 5대에 모두 등록돼 있어 공격자가 같은 키로 차례로 root 로그인했습니다. 서버 간 접속이 「편해서」 만든 신뢰가 피해 범위를 키웠습니다.
서버마다 다른 키를 쓰고, 키 등록에 접속 허용 IP(from=)를 걸고, 접속 가능한 서버 목록을 줄이세요.
3. 비밀은 생각보다 훨씬 많은 곳에 평문으로 있습니다
.env 만이 아닙니다. 백업 폴더, 셸 명령 이력, 에디터 편집 기록, AI 코딩 도구의 허용 규칙 파일, 예제 파일에까지 비밀번호가 적혀 있었습니다. AI 도구는 편의를 위해 사본을 계속 만듭니다. 한 곳을 바꿔도 다른 사본이 남습니다.
비밀 값으로 «전체 검색» 해서 남은 사본을 찾고, 교체한 뒤 다시 검색해 0건인지 확인하세요.
4. 같은 비밀번호를 여러 곳에 쓰면 한 번에 무너집니다
데이터베이스 슈퍼유저 하나를 앱 9개가 같이 쓰고 있었습니다. 앱 하나만 뚫려도 모든 데이터가 열립니다. 비밀번호가 약하거나 서비스 이름과 같은 경우도 있었습니다.
앱마다 DB 계정을 따로 만들고 필요한 권한만 주세요. 비밀번호는 무작위 32자 이상으로 하세요.
5. 지갑·거래소 키에는 반드시 한도와 잠금을 거세요
서버에서 복호화된 지갑 키가 있으면 서버가 뚫린 순간 자금이 움직입니다. 앱이 정한 한도는 키를 가진 공격자에게 아무 소용이 없었습니다. 알림도 «가스 부족» 같은 평범한 경고에 섞여 무시됐습니다.
거래소 키는 출금 권한 끄기 + 접속 IP 허용 목록. 지갑은 운영에 필요한 최소 금액만 두고, 자금 이동 알림은 따로 눈에 띄게 만드세요.
5분 자가 점검 (내 서버에서, 읽기만 합니다)
아래는 값을 바꾸지 않는 점검입니다. 결과가 이상하면 먼저 비밀을 교체하고, 그다음에 원인을 찾으세요.
서버가 인터넷에 열어 둔 포트
ss -tlnp # 0.0.0.0 으로 열린 것 중 모르는 것이 있으면 의심
모르는 SSH 키가 등록돼 있나
cat ~/.ssh/authorized_keys # 내가 등록하지 않은 줄이 있으면 즉시 조사
최근 로그인 출처
last -i | head -30 # 낯선 IP 가 있나
앱이 root 로 도나
ps -eo user,comm | grep -E 'node|python|java' | sort | uniq -c
코드·설정에 비밀이 박혀 있나
grep -rIlE 'BEGIN (RSA|OPENSSH|EC) PRIVATE KEY|AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9]{20,}' --exclude-dir=node_modules --exclude-dir=.git .
아래 공격 출발지가 내 로그에 있나
grep -hE '23\.95\.130\.140|107\.174\.144\.145|31\.58\.136\.212' /var/log/nginx/access.log* /var/log/auth.log* 2>/dev/null | head