PHP2Shell 공지를 보고 점검했더니 PHP가 하나도 없었습니다

저녁 스탠드 불빛 아래 나무 책상 위에 쌓인 낡은 서류철과 먼지 앉은 외장 하드, 그 옆에 덮여 있는 노트북

10월 21일에 PHP와 Node.js가 같은 날 보안 패치를 냈습니다. PHP 쪽은 사람들이 PHP2Shell이라고 부르고 있고, Node 쪽은 Node2Shell이라는 이름이 붙었습니다. 이름부터가 좀 무섭습니다. 뒤에 Shell이 붙으면 대개 남의 서버에서 명령을 실행할 수 있다는 뜻이니까요.

회사에서도 바로 지시가 내려왔습니다. 전 프로젝트 점검하고, 외부 IP 붙은 서버는 전수조사하라는 내용이었습니다. 회사 쪽 일은 회사 쪽대로 하고, 저는 퇴근하고 개인 프로젝트랑 예전에 알바로 했던 것들을 한번 열어 봤습니다.

그런데 열어 보고 나니 이번 글은 패치 이야기가 아니라 7년 전 이야기가 됐습니다.

PHP2Shell과 Node2Shell, 둘이 같은 무게는 아닌 것 같습니다

찾아오신 분들은 아마 이게 뭔지, 내 서버가 해당되는지부터 궁금하실 것 같아서 그것부터 적습니다.

PHP2Shell은 CVE 두 건입니다. CVE-2026-109618과 CVE-2026-109619이고, 둘 다 인증 없이 원격에서 코드를 실행할 수 있는 취약점입니다. CVSS 4.0 기준 9.2점입니다. 하나는 메모리가 부족할 때 PHP가 요청 데이터를 반쯤 만든 상태에서 멈추고, 정리 단계에서 그 망가진 데이터를 멀쩡한 걸로 취급하는 문제입니다. 다른 하나는 요청이 끝난 뒤에도 그 요청이 쓰던 상태가 남아 있다가 같은 워커가 받은 다음 요청에서 다시 쓰이는 문제입니다.

PHP 공식 공지 기준으로 고쳐진 버전은 8.5.12, 8.4.27, 8.3.36, 8.2.35입니다. 그보다 낮은 버전은 전부 해당됩니다. 8.1 이하는 PHP 프로젝트가 더 이상 정규 릴리스를 내지 않습니다. 대신 보안팀이 7.4, 8.0, 8.1용으로 최소한의 소스 백포트를 따로 공개해 뒀고, 배포판이나 LTS 벤더 중에도 백포트를 내는 곳이 있다고 합니다. 직접 빌드하거나 벤더 패키지를 쓰는 게 아니면 사실상 버전을 올리는 수밖에 없어 보입니다. 공지에도 설정을 바꾸는 건 부분적인 완화일 뿐이고 업데이트만이 완전한 해결이라고 적혀 있습니다.

실제 공격이 있었는지는 보도마다 말이 조금 다릅니다. 릴리스 당일 Cloudflare가 초기 공격을 보고 WAF 긴급 룰을 켰다, watchTowr 허니팟에 공격 시도가 들어왔다는 보도가 있고, 공개 시점에는 확인된 악용이 없었다는 이야기도 있습니다. 저는 시도가 관측됐다는 쪽만으로도 충분히 신경 쓰이는 상황이라고 보고 있습니다.

Node2Shell은 CVE-2026-111497입니다. Node.js가 내장한 Brotli 디코더에서 경계 밖 쓰기가 일어나는 문제입니다. 깨진 Brotli 입력을 받으면 디코더가 형식 오류로 멈추지 않고 입력이 더 필요한 걸로 착각해서, 8바이트짜리 내부 버퍼에 계속 바이트를 붙입니다. node:zlib로 믿을 수 없는 데이터를 풀고 있다면 크래시, 메모리 노출, 최악이면 원격 코드 실행까지 갈 수 있다고 합니다.

고쳐진 버전은 22.23.4, 24.22.1, 26.12.1입니다. 하나 주의할 게 있는데, 여기저기서 보이는 9.2점은 Brotli 라이브러리 쪽 CNA인 Google이 매긴 점수이고 Node.js 공식 공지는 이 건을 High로 분류했습니다. 그리고 Node 20 이하는 이미 지원이 끝난 버전이라 패치가 나오지 않습니다. 공지에는 지원 종료 버전은 보안 릴리스가 있을 때마다 항상 영향받는다고만 적혀 있습니다. 10월 24일 무렵까지는 CISA의 악용 목록에도 없었고 공개된 침해 지표도 없는 것으로 보입니다.

재밌는 건 이 둘이 완전히 남남은 아니라는 점입니다. 10월 26일에 PHP Windows 빌드가 한 번 더 나왔는데, 이유가 같은 Brotli 취약점이었습니다. cURL로 HTTP 응답을 받을 때 자동 압축 해제를 켜 둔 경우에만 해당되고 기본값은 꺼져 있다고 합니다. PHP 버전 번호는 그대로이고 빌드만 다시 나왔습니다. 같은 날 터진 두 언어의 취약점이 라이브러리 하나를 통해 다시 만난 셈입니다.

이걸 보면서 언어를 기준으로 "나는 PHP 안 쓰니까 상관없다", "나는 Node 안 쓰니까 상관없다"고 나누는 게 생각보다 거친 구분이라는 생각이 좀 들었습니다. 결국 서버 안에 실제로 무엇이 들어 있느냐를 봐야 합니다.

둘을 나란히 놓고 보면 저는 PHP 쪽이 훨씬 무서웠습니다. Node 쪽은 공격자가 보낸 Brotli 데이터를 내 서버가 실제로 풀어야 닿습니다. 그런 코드가 있는지 없는지를 찾으면 됩니다. PHP 쪽은 서버에 PHP가 떠 있고 요청을 받기만 하면 앱 코드가 돌기도 전에 닿는 구조로 읽혔습니다. 내가 짠 코드가 깔끔한지 아닌지와 상관이 없다는 얘기입니다. Apache에 mod_php로 붙어 있는 구성이 가장 위험하다는 분석도 있었는데, 2010년대에 PHP로 뭔가 올려 본 사람이라면 그 구성이 얼마나 흔했는지 아실 겁니다.

하나 더 있습니다. 패치가 나오고 며칠 뒤에 8.5.12에서도 여전히 원격 실행이 된다고 주장하는 페이지가 올라왔다고 합니다. 이걸 분석한 쪽에서는 신뢰도가 낮은 주장이고 패치를 미룰 이유는 아니라고 봤습니다. 저도 그쪽 판단이 맞다고 생각합니다. 다만 이 소식을 보면서 든 생각은 따로 있었습니다. 패치를 올리면 다들 거기서 끝났다고 보고하고 싶어 합니다. 그런데 패치를 올렸다는 것과 이제 안전하다는 건 다른 말입니다. 올린 버전이 정말 그 서버에서 돌고 있는지, 프로세스를 재시작했는지, 같은 서버에 다른 PHP가 하나 더 깔려 있지는 않은지까지 봐야 끝난 겁니다. 저는 "다 됐습니다"라는 말을 들으면 일단 의심부터 하는 편인데, 이번 같은 일에서는 그 의심이 꽤 쓸모 있는 것 같습니다.

점검하려고 열었더니 php 명령이 없었습니다

그래서 PHP부터 보려고 터미널을 열었습니다. php -v를 쳤는데 버전이 안 나오고 명령을 찾을 수 없다는 말만 나왔습니다.

생각해 보면 당연한 결과였습니다. 지금 쓰는 맥에는 PHP를 깐 적이 한 번도 없거든요. 그리고 PHP를 한창 쓰던 시절에도 저는 PHP를 "설치"해서 쓰지 않았습니다.

그때는 윈도우에서 작업했습니다. PHP는 설치형이 아니라 zip을 받아서 압축만 푸는 포터블 방식으로 썼고, 프로젝트마다 그 폴더를 따로 두고 설정도 따로 관리했습니다. 프로젝트가 끝나면 통째로 묶어서 연도별 폴더에 넣었습니다. 2009년 폴더, 2010년 폴더, 이런 식으로요. 그 안에 프로젝트이름.zip이 하나씩 들어 있었습니다.

이 방식 덕에 시스템 어디에도 PHP가 뿌리를 내린 적이 없습니다. PATH에 걸린 공용 PHP도, 서비스로 등록된 PHP도 없었습니다. 프로젝트를 닫으면 그 프로젝트의 PHP도 같이 zip 안으로 들어갔습니다. 그러니 기술을 옮기고 나서 따로 지울 것도 없었고, 지금 이 맥에서 php가 안 나오는 것도 이상한 일이 아니었던 겁니다.

2019년, PHP를 내려놓기 시작한 이유

솔직하게 쓰면, 보안 때문에 옮긴 게 아닙니다.

PHP는 1999년부터 썼습니다. 그 전에는 Perl로 웹을 만들었는데, PHP가 문법도 비슷하고 훨씬 쉽고 빨라서 갈아탔습니다. 비슷한 게 당연했던 게, PHP는 처음에 Perl로 짠 스크립트 묶음으로 시작한 언어였습니다. 그때는 파일 확장자도 .php3였습니다. PHP 3 시절이었으니까요. 그 뒤로 ASP도 하고 이것저것 다 해 봤습니다. Spring도 할 줄 알았습니다. 그래도 빨리 만들어야 하는 일이 들어오면 손이 가는 건 PHP였습니다. Spring보다 간단했으니까요. 알바처럼 기한 짧고 규모 작은 일에는 그 간단함이 거의 전부였습니다.

그렇게 20년 가까이 쓰던 걸 2019년부터 내려놓기 시작했습니다. 한 번에 다 바꾼 건 아닙니다. 처음엔 화면만 Vue와 React로 바꾸고, API는 그대로 PHP였습니다. 기존에 만들어 둔 PHP를 버릴 수가 없었으니까요. 백엔드까지 Spring으로 넘어간 건 2020년부터였는데, 이건 제 선택이라기보다 일 요구사항에 Spring이 적혀 오기 시작해서였습니다.

화면 쪽을 바꾼 이유는 그냥 최신 기술이 궁금해서였습니다. 저는 새로운 걸 배우고 이것저것 만져 보는 걸 좋아하는 편이고, 그때는 그쪽이 재밌어 보였습니다.

문제는 2019년, 2020년만 해도 Vue나 React가 사람들한테 꽤 생소했다는 겁니다. 일을 맡기는 쪽에서는 왜 멀쩡한 PHP 화면을 두고 처음 듣는 걸 쓰냐고 물을 수밖에 없었습니다. 그래서 프로젝트마다 장단점을 정리해서 설명하고 대표를 설득했습니다. 돌아보면 웃긴 게, 그때 설득하면서 꺼낸 장점 목록에 보안은 없었습니다. 저도 생각을 안 했으니까요.

보안을 따져 보고 내린 결정은 전혀 아니었습니다. PHP가 위험해서 떠난 것도 아니고, 언젠가 이런 취약점이 나올 걸 내다본 것도 아닙니다. 그때 누가 "PHP 버리면 7년 뒤에 큰 취약점 하나를 피한다"고 했으면 무슨 소리냐고 했을 겁니다.

그래서 이번 주의 결과를 선견지명이라고 부르면 사실과 다릅니다. 계획하지 않은 덕을 본 것에 가깝습니다. 그런데 그게 좀 묘합니다. 계획한 게 아니었는데 결과는 꽤 구조적이었기 때문입니다.

그때는 몰랐던 것

옮기면서 바뀐 건 언어만이 아니었습니다. 서버에 무엇이 떠 있는지가 바뀌었습니다.

PHP로 만들던 시절에는 화면을 그리는 것도 서버, 데이터를 처리하는 것도 서버였습니다. 요청이 들어오면 PHP가 받아서 HTML을 만들어 내보냈습니다. 그러니까 외부에서 들어오는 모든 요청이 PHP 인터프리터를 한 번씩 거쳤습니다. 이번 취약점이 정확히 그 경로를 노리는 거고요.

Vue나 React로 옮긴 뒤에는 화면을 빌드해서 정적 파일로 올렸습니다. 운영 서버에는 HTML, JS, CSS 파일만 있고 그걸 그려 주는 런타임은 사용자 브라우저에 있습니다. 같은 서비스를 만들어도 외부 요청이 무엇을 거치는지가 완전히 다릅니다.

다만 2019년에는 이게 반쪽이었습니다. 화면은 정적 파일이 됐지만 API는 여전히 PHP였으니, 데이터를 주고받는 요청은 계속 PHP 인터프리터를 거쳤습니다. PHP가 요청 경로에서 완전히 빠진 건 백엔드가 Spring으로 넘어간 2020년 이후 프로젝트부터입니다. 그러니 정확히 말하면 저를 구한 건 2019년의 결정 하나가 아니라 2019년에 시작해서 2020년에 마저 끝난 두 단계였습니다.

그때 저는 이걸 노출 면이라는 말로 생각해 본 적이 없습니다. 그냥 요즘 다들 이렇게 만드니까 이렇게 만든 거였습니다. 그런데 7년이 지나고 보니 그 결정이 이번 주에 제가 무엇을 걱정해야 하는지를 정해 놓고 있었습니다. 패치를 얼마나 빨리 했느냐보다, 패치할 대상이 애초에 있었느냐가 먼저였던 셈입니다.

이렇게 써 놓으니 뭔가 대단한 판단을 한 것처럼 보이는데, 다시 말하지만 그냥 새 거 해 보고 싶어서 옮긴 겁니다. 이유가 사소해도 구조는 남는 것 같습니다.

그렇다고 Spring으로 옮긴 백엔드가 안전하다는 얘기는 아닙니다. 이름 뒤에 Shell이 붙은 취약점 중에 제일 시끄러웠던 건 2021년 말의 Log4Shell이었고, 그건 Java 쪽 이야기였습니다. 저도 그때 그걸 정면으로 맞았습니다. 정확히 말하면 API만 맞았습니다. Spring으로 짠 API 서버들은 하나하나 찾아서 패치해야 했는데, Vue와 React로 만든 화면은 건드릴 게 없었습니다. 정적 파일만 올라가 있었으니 Log4j가 들어갈 자리가 처음부터 없었던 겁니다. 이번 주와 똑같은 그림이 5년 전에 이미 한 번 있었던 셈인데, 그때는 그냥 "화면은 다행이다" 하고 넘어갔습니다.

언어를 바꾸면 피하는 취약점이 생기는 대신 새로 떠안는 취약점도 생깁니다. 이번에 PHP2Shell을 피한 건 맞지만, 그 대가로 다른 무언가에 노출돼 있을 수 있습니다. 그래서 이번 일을 "PHP는 위험하고 Spring은 안전하다"로 읽고 싶지는 않습니다. 제가 이번에 본 건 언어의 안전성이 아니라, 화면을 그리는 일과 데이터를 처리하는 일을 나눠 놓으니 외부 요청이 지나가는 길이 좁아졌다는 것 정도입니다. 그 정도의 차이가 이번 주에는 컸습니다.

Node 쪽 점검, audit 0건이 알려주지 않는 것

PHP가 없으니 이번엔 Node 쪽을 봤습니다. 개인 프로젝트랑 알바 프로젝트 다섯 개를 열어서 npm audit을 돌렸고, 결과는 전부 0건이었습니다. 로컬 Node는 24.22.1이라 이번 패치 버전이었고요.

여기서 끝내도 될 것 같았는데 audit 0건이라는 숫자가 좀 걸렸습니다. 생각해 보면 npm audit은 package.json에 걸린 패키지를 보는 도구입니다. Node 런타임 자체가 몇 버전인지는 보지 않습니다. Node2Shell은 패키지가 아니라 Node가 내장한 Brotli 디코더 문제이니, audit이 0건이어도 그 프로젝트가 돌아가는 Node가 20이면 그대로 해당됩니다. 0건은 "패키지 쪽은 괜찮다"는 뜻이지 "이 프로젝트는 안전하다"는 뜻이 아닙니다.

그다음으로 든 생각은 이 프로젝트들 중 상당수는 운영 서버에 Node가 아예 없다는 거였습니다. 프론트를 정적으로 빌드해서 올리는 프로젝트라면 Node는 빌드할 때만 씁니다. 빌드가 끝나면 결과물은 그냥 파일입니다. 운영 서버가 외부에서 Brotli 데이터를 받아서 node:zlib로 풀 일이 없습니다. Node2Shell이 닿을 경로가 없는 겁니다. 이것도 PHP 때와 같은 이야기입니다. 서버에 무엇이 떠 있는지가 먼저입니다.

물론 같은 React라도 서버에서 렌더링하는 구성이면 이야기가 다릅니다. Next.js 같은 걸로 SSR을 돌리고 있다면 운영 서버에 Node가 떠 있고 외부 요청을 직접 받습니다. 그 서버가 압축된 요청 본문을 풀거나, 외부 API 응답을 Brotli로 받아서 푸는 코드가 있다면 Node2Shell 점검 대상이 됩니다. 프레임워크 이름만 보고 판단하면 안 되고, 결국 배포된 결과물이 어떤 모양인지를 봐야 합니다. 제 프로젝트들은 운 좋게 대부분 정적 빌드였는데, 이것도 보안을 생각해서 고른 게 아니라 그게 제일 간단해서 고른 겁니다. 이번 글에서 계속 같은 말을 하게 되는데, 저는 이번 주 내내 계획하지 않은 선택들의 덕을 보고 있었습니다.

그런데 하나가 거슬렸습니다. Cloudflare Workers로 올린 프로젝트 하나에 .node-version 파일과 GitHub Actions 설정이 Node 20으로 남아 있었습니다. 처음 만들 때 그 버전으로 잡아 두고 한 번도 안 건드린 겁니다.

이건 빌드 환경 이야기라서 운영 노출과는 다른 문제입니다. Workers는 Node 서버를 띄우는 방식이 아니니까 PHP2Shell 같은 의미로 위험하다고 보기는 어렵습니다. 그래도 거슬렸습니다. 지원이 끝난 런타임이 내 프로젝트 어딘가에 아무도 모르게 남아 있었다는 것 자체가요. audit으로는 절대 안 보였을 거고, 이번 일이 없었으면 계속 몰랐을 겁니다. 런타임 버전은 결국 .node-version이나 CI 설정 파일을 직접 열어 봐야 보이는 것 같습니다.

정말 위험한 건 옮기고 남겨 둔 서버였습니다

여기까지 보고 나서 저는 좀 편해져 있었습니다. PHP는 없고, Node는 최신이고, 남은 건 빌드 설정 하나 정도였으니까요. 7년 전에 옮겨서 다행이다, 하고 끝내려고 했습니다.

그러다 회사 지시 문구가 다시 생각났습니다. 프로젝트 점검만이 아니라 외부 IP 전수조사였습니다. 처음 읽었을 때는 그냥 꼼꼼하게 하라는 말로 들었는데, 이번에는 다르게 읽혔습니다.

제 노트북에 PHP가 없다는 건 제가 지금 PHP로 개발하지 않는다는 뜻일 뿐입니다. 7년 전에 PHP로 만들어서 어딘가에 올린 서버가 지금 꺼져 있다는 뜻은 아닙니다. 스택을 바꾼 건 제 쪽 이야기고, 옛날에 올린 서버는 제가 뭘 배우든 상관없이 그때 그 PHP 버전으로 계속 돌고 있을 수 있습니다. 외부 IP를 단 채로요. 그런 서버라면 아마 8.1보다 한참 낮은 버전일 거고, 그러면 정규 패치도 없습니다.

제일 마음에 걸리는 건 2019년에 만든 것들입니다. 화면은 Vue나 React라서 겉으로 보면 요즘 서비스 같은데, 그 뒤에서 데이터를 내주는 건 PHP API입니다. 화면만 보고 "이건 PHP 아니야"라고 넘기기 딱 좋은 구조입니다. 그때 대표를 설득하면서 새 기술로 만든다고 설명했으니, 맡긴 쪽에서도 PHP가 남아 있다는 걸 기억하고 있을 가능성은 낮습니다.

제 쪽에도 비슷한 게 하나 있긴 합니다. 연도별 폴더에 쌓아 둔 프로젝트이름.zip들입니다. 포터블로 썼으니 그 안에는 소스만 있는 게 아니라 그때 쓰던 PHP 실행 파일까지 통째로 들어 있습니다. 압축이 풀리지 않는 한 아무 일도 안 일어납니다. 그런데 누가 옛날 프로젝트 좀 다시 띄워 보자고 압축을 푸는 순간, 패치 하나 안 된 그 시절 PHP가 그대로 살아납니다. 그 zip들은 금고라기보다 묘지에 가깝습니다. 들여다보는 건 괜찮은데 다시 살려서 밖에 내놓으면 안 되는 것들입니다.

PHP2Shell이 노리는 게 딱 그런 서버입니다. 지금 개발하고 있는 서비스는 누군가 관리하고 있으니 패치가 나오면 올립니다. 관리하는 사람이 없는 서버는 패치가 나와도 아무도 안 올립니다. 그리고 그 서버가 아직 켜져 있다는 걸 아는 사람도 없습니다.

회사가 프로젝트 목록이 아니라 외부 IP 목록부터 보라고 한 이유를 그제야 알 것 같았습니다. 프로젝트 목록은 지금 살아 있는 것들의 목록이고, 외부 IP 목록은 실제로 인터넷에 열려 있는 것들의 목록입니다. 둘이 같다고 생각하기 쉬운데 실제로는 그 차이에 이번 같은 일이 숨어 있습니다. IP는 있는데 목록에 짝이 없는 서버, 그게 제일 무섭습니다.

이런 지시를 받으면 처음엔 좀 과하다고 느끼기 쉽습니다. 다들 자기 프로젝트는 잘 알고 있다고 생각하니까요. 그런데 자기 프로젝트를 잘 아는 것과 회사 이름으로 인터넷에 열려 있는 걸 다 아는 건 다른 문제입니다. 퇴사한 사람이 띄워 둔 테스트 서버, 행사 끝나고 내리는 걸 잊은 이벤트 페이지 같은 건 어느 프로젝트 목록에도 안 올라가 있을 겁니다. 그런 건 프로젝트에서 출발하면 못 찾고 IP에서 출발해야 찾습니다.

전환은 새 걸 만드는 일입니다. 옛 걸 끄는 건 별개의 일이고, 그건 누가 따로 해야 합니다. 저는 7년 전에 새 걸 만드는 데만 신경 썼고 옛 걸 끄는 건 생각해 본 적이 없었습니다. 새로운 걸 좋아하는 성격 탓도 있습니다. 새 걸 배우는 건 재밌는데 옛날 서버를 찾아서 정리하는 건 하나도 재미가 없으니까요.

이 부분이 좀 걸립니다. 이번에 저를 구해 준 게 새 걸 좋아하는 성격인데, 옛 서버를 남겨 두게 만든 것도 같은 성격입니다. 같은 성향이 한쪽에서는 노출을 줄였고 다른 쪽에서는 관리 안 되는 서버를 남겼습니다. 그러니 이번 일을 "새 기술을 빨리 따라가면 안전하다"로 읽으면 반만 읽은 겁니다. 새 기술로 간 사람은 새 쪽에서는 안전해지는데, 떠난 쪽에는 아무도 안 남습니다. PHP를 계속 쓰던 사람은 오히려 자기 서버 버전을 알고 있을 가능성이 높습니다. 매일 들여다보니까요. 떠난 사람이 더 모릅니다.

그러니까 제가 이번에 덕을 본 건 반만 맞는 이야기입니다. 제 노트북과 지금 프로젝트는 PHP2Shell과 관계가 없습니다. 그건 7년 전에 계획 없이 옮긴 덕입니다. 그런데 옮기기 전에 올려 둔 것들이 지금 어떤 상태인지는 이 점검으로는 알 수가 없습니다.

지금은 이 순서로 보는 게 맞는 것 같다고 생각하고 있습니다. 먼저 외부 IP 목록이랑 지금 서비스 목록을 나란히 놓고 짝이 없는 IP를 찾습니다. 그 IP에서 PHP 버전을 확인하고, 8.1 이하라면 패치보다는 끄거나 옮기는 쪽을 먼저 생각합니다. 정규 패치가 안 나오는 버전을 붙들고 있을 이유가 별로 없어 보입니다. 8.2도 올해 12월 31일이면 지원이 끝나니까, 8.2에 있는 서버라면 이번에 패치하고 나서 두 달 뒤에 같은 고민을 다시 하게 됩니다. 런타임 버전은 audit 말고 서버나 설정 파일에서 직접 봅니다.

짝이 없는 IP를 찾는 일은 말로 하면 간단한데 실제로 해 보면 귀찮은 일일 것 같습니다. 클라우드 콘솔에 떠 있는 인스턴스, 도메인 DNS에 걸린 A 레코드, 예전에 쓰던 호스팅 업체 계정을 다 따로 봐야 하니까요. 클라우드 쪽은 목록이라도 나오는데, 2010년대 웹호스팅에 올린 건 그 업체에 로그인할 수 있는지부터가 문제입니다. 도메인 쪽에서 거꾸로 찾아가는 게 그나마 빠를 수도 있겠다는 생각을 하고 있습니다. 내 이름으로 남은 도메인이 지금 어느 IP를 가리키는지 보고, 그 IP에서 무엇이 응답하는지 보는 겁니다. 응답 헤더에 PHP 버전이 그대로 찍혀 나오는 서버도 아직 꽤 있을 거라고 봅니다. 그 시절엔 그걸 숨기는 설정을 신경 쓰는 사람이 많지 않았으니까요.

손을 떠난 알바 프로젝트는 어디까지 내 일인가

이 부분은 아직 생각이 정리되지 않았습니다.

예전에 알바로 만들어 준 것들 중에는 이미 제 손을 떠난 게 있습니다. 유지보수 계약이 끝났거나, 처음부터 없었거나, 호스팅이 만료됐는지도 모르는 것들입니다. 그 서버가 아직 켜져 있는지 저는 확인할 방법이 마땅치 않습니다. 접속 정보가 남아 있어도 이제 와서 남의 서버에 들어가는 게 맞는지도 잘 모르겠습니다.

일단 지금은 이렇게 선을 긋고 있습니다. 서버 자체는 이미 제 책임 범위 밖입니다. 그건 지금 그 서비스를 쓰는 쪽의 일입니다. 다만 제 쪽에 남아 있는 것들은 제 일입니다. 그때 쓰던 FTP 계정, DB 비밀번호, API 키, 제 이름으로 남아 있는 도메인 같은 것들입니다. 그 서버가 털리면 거기 남은 제 자격증명이 다른 곳에서도 쓰일 수 있으니까요.

자격증명이 어디서 새는지는 예전에 시크릿 스캔을 걸어 뒀는데 AWS 키가 통과한 일을 쓰면서도 한 번 생각해 본 적이 있습니다. 그때는 커밋으로 나가는 걸 막는 이야기였는데, 이번에는 이미 나가 있는 것들을 거둬들이는 이야기라 방향이 반대입니다. 막는 건 도구로 어느 정도 되는데 거둬들이는 건 기억에 의존해야 해서 훨씬 어렵습니다.

알바로 만들어 준 서비스를 받아 간 쪽은 대개 개발자가 아닙니다. 그 사람들 입장에서는 사이트가 잘 뜨고 있으면 문제가 없는 겁니다. 서버 안에 PHP 몇 버전이 돌고 있는지, 그게 이번 공지와 무슨 상관인지 알 방법이 없습니다. 기사를 봤더라도 PHP2Shell이라는 단어와 자기 가게 홈페이지를 연결하기는 어려울 것 같습니다. 만든 사람은 알고, 쓰는 사람은 모르는 상태로 몇 년이 흘러 있는 겁니다. 그때 넘겨줄 때 어떤 버전으로 무엇을 올렸는지 한 장이라도 적어서 같이 줬으면 지금 그 사람들이 스스로 판단할 수 있었을 텐데, 알바로 하는 일에서 그런 걸 챙기는 사람은 그때도 지금도 많지 않은 것 같습니다.

연락이 닿는 곳에는 이런 취약점이 나왔다고 한마디 해 주는 정도는 할 수 있습니다. 그 이상은 제 일이 아니라고 봅니다. 이 선이 맞는지는 솔직히 아직 모르겠습니다.

7년 전의 저한테

7년 전의 저는 보안 같은 건 생각도 안 하고 그냥 재밌어 보이는 쪽으로 갔습니다. 그게 이번 주에 PHP2Shell 공지를 보고 터미널 첫 줄에서 점검을 끝내게 해 줬습니다.

그런데 그때의 저한테 하나 말할 수 있다면, 새 걸 만들 때 옛 걸 어디에 두고 왔는지 적어 두라고 하고 싶습니다. 서버 주소 하나, 계정 하나 정도만이라도요. 다음에 이런 공지가 오면 저는 노트북보다 그 목록을 먼저 열 것 같습니다.

댓글

이 블로그의 인기 게시물

봉제인형 사진 한 장, ChatGPT와 Gemini의 다른 대답

AI 부업으로 3개월에 2달러 벌고 50달러를 썼다는 증언을 봤습니다. 그 위에 광고가 네 겹입니다

구글은 서울에서 모델이 아니라 에이전트를 판다