웹/클라우드/인프라로 돌아가기
웹/클라우드/인프라신윤섭·2026년 7월 4일

SSRF가 뭔데, AI 에이전트가 링크 읽을 때 특히 위험한 이유

공유

"링크 넣으면 미리보기 만들어주는 기능", 별거 아닌 줄 알았다

Claude Code에게 "사용자가 URL을 붙여넣으면 그 페이지 제목이랑 썸네일을 보여주는 기능 만들어줘"라고 시켰다. 몇 분 만에 코드가 나왔다.

app.post('/api/preview', async (req, res) => {
  const { url } = req.body
  const response = await fetch(url)
  const html = await response.text()
  // og:title, og:image 파싱해서 반환
  res.json(parseOpenGraph(html))
})

동작도 잘한다. 트위터 링크를 붙여넣으면 카드가 뜨고, 블로그 글 링크를 붙여넣으면 대표 이미지가 뜬다. 배포하고 다음 작업으로 넘어갔다.

문제는 이 코드 한 줄에 있다. fetch(url). 사용자가 준 주소로 "서버가" 요청을 보낸다는 뜻이다. 사용자가 https://twitter.com/...을 넣으면 문제없다. 그런데 http://localhost:6379http://169.254.169.254/latest/meta-data/를 넣으면 어떻게 될까. 서버는 아무 의심 없이 그 주소로도 요청을 보낸다. 이게 SSRF(Server-Side Request Forgery, 서버 측 요청 위조)다.

AI 에이전트가 웹 페이지를 읽고 요약해주는 기능도 구조는 같다. 사용자가 붙여넣은 링크의 내용을 챗봇이 가져오는 기능도, 웹훅을 받아서 콜백 주소로 응답을 보내는 기능도 마찬가지다. "서버가 사용자 대신 어딘가로 요청을 보낸다"는 패턴이 있으면 SSRF부터 생각해야 한다.

안내데스크 직원한테 심부름 시키는 것과 같다

회사 건물에 안내데스크가 있다. 방문객은 출입증이 없어서 건물 안을 마음대로 돌아다니지 못한다. 대신 안내데스크 직원에게 부탁할 수 있다. "3층 탕비실에서 커피 좀 갖다주세요." 직원은 그 건물에서 일하는 사람이라 3층이든 다른 층이든 자유롭게 오간다. 방문객 대신 다녀와서 갖다준다.

문제는 그 부탁이 "탕비실 커피"가 아니라 "금고실 서류"일 때 생긴다. 안내데스크 직원이 부탁을 의심 없이 들어주는 사람이라면, 방문객은 자기가 못 들어가는 곳의 물건을 직원을 시켜서 빼올 수 있다.

서버가 정확히 이 안내데스크 직원 역할을 한다. 서버가 관리자 페이지, 내부 DB 대시보드, 클라우드 인증 정보 같은 곳까지 오갈 수 있는 이유는 특별한 권한을 받아서가 아니라, 애초에 그 내부망 안에 놓여 있기 때문이다. 외부 사용자는 그 네트워크 바깥에 있어서 원래 이런 곳에 직접 접근하지 못한다. 그런데 서버한테 "이 URL 좀 대신 갔다 와줘"라고 시키는 기능이 있으면, 공격자는 그 URL을 내부 주소로 바꿔치기해서 서버를 부릴 수 있다. 서버는 자기가 이용당하는 줄도 모르고 다녀와서 결과를 그대로 공격자에게 돌려준다.

실제로 뭘 훔쳐갈 수 있나

공격자가 미리보기 기능의 url 파라미터에 넣어볼 만한 주소들이 있다.

  • http://localhost, http://127.0.0.1 : 서버 자기 자신이다. 외부에는 안 열려 있지만 서버 내부에서만 접근 가능한 관리자 페이지나 디버그 엔드포인트가 열려 있는 경우가 많다.
  • http://169.254.169.254 : AWS, GCP, Azure가 가상 머신 안에서 공통으로 쓰는 클라우드 메타데이터 주소다. 이 주소로 요청을 보내면 그 서버가 쓰고 있는 임시 IAM 자격 증명을 그대로 돌려준다. 2019년 Capital One이 신용카드 고객 1억 명 넘는 정보를 유출당한 사고가 바로 이 경로였다. 웹 애플리케이션 방화벽(WAF) 설정 오류로 외부에서 서버에 요청을 넣을 수 있었고, 그 서버가 메타데이터 엔드포인트에서 받아온 자격 증명으로 S3에 쌓인 데이터를 통째로 빼갔다.
  • http://192.168.x.x, http://10.x.x.x, http://172.16.x.x : 회사 내부망에서만 열려 있는 IP 대역이다. 외부에서는 아예 라우팅이 안 되지만, 서버가 그 내부망 안에 있다면 서버를 거쳐서 접근할 수 있다.

미리보기 기능이 돌려주는 화면에는 og:title, og:image가 아니라 관리자 페이지 HTML, 메타데이터 JSON, 내부 대시보드 내용이 그대로 찍힌다. 공격자는 그 사이트를 직접 뚫지 않고도, 그 사이트의 서버를 거쳐서 완전히 다른 시스템을 들여다본 셈이다.

AI 에이전트를 붙이면 왜 더 위험해지나

여기까지는 사람이 직접 url 파라미터에 이상한 주소를 넣어야 벌어지는 일이다. 그런데 AI 에이전트가 "URL을 읽고 요약해주는" 도구를 쓰기 시작하면 이 과정에 사람이 아예 끼지 않는다.

챗봇에게 "이 페이지 요약해줘"라고 링크를 보내면, 에이전트는 그 링크를 눈으로 확인하지 않고 fetch 도구에 그대로 넘긴다. 대화 기록에 남아있던 URL, 웹페이지 안에 숨겨진 지시문, 다른 도구가 돌려준 응답에 섞인 링크까지 전부 대상이 된다. 에이전트가 이 중 무엇이든 "읽어야 할 URL"로 판단하면 검증 없이 요청을 보낼 수 있다. 사람이 링크를 클릭하기 전에 한 번 훑어보는 단계가 통째로 사라지는 셈이다.

바이브 코딩으로 실제로 만들 법한 기능도 다르지 않다. 리서치 어시스턴트에 웹 검색과 페이지 요약 도구를 붙여서 자료를 자동으로 훑게 하거나, 고객 문의 봇에 첨부된 링크를 열어 요약해주는 기능을 붙였다면 구조는 똑같다. 검색 결과나 문의 내용에 내부 주소가 섞여 들어가도 에이전트는 그걸 그대로 fetch 도구에 넘길 수 있다.

2026년에 실제로 이런 사례가 여럿 보고됐다. AI 에이전트 프레임워크 pydantic-ai는 대화 기록(message history)에 포함된 URL을 별다른 검증 없이 fetch하는 문제가 있었다(GHSA-2jrp-274c-jhv3). LangChain의 HTMLHeaderTextSplitter는 최초 URL만 검사하고 그 URL이 3xx로 리다이렉트되는 주소는 검사하지 않는 문제가 있었다(CVE-2026-41481). 리다이렉트를 안 막으면 처음에 통과시킨 정상 도메인이 내부 주소로 슬쩍 튀어도 그대로 따라간다는 뜻이다. 둘 다 널리 쓰이는 프레임워크에서 나온 문제라서, 잘 만든 라이브러리를 갖다 쓴다고 알아서 막아주는 게 아니라는 걸 보여준다.

CSRF와 헷갈리기 쉬운데, 속이는 대상이 다르다

이름이 비슷해서 자주 헷갈리는 CSRF(Cross-Site Request Forgery)와는 속이는 대상이 다르다. CSRF는 사용자의 브라우저를 속여서 사용자 대신 요청을 보내게 만드는 공격이다. SSRF는 서버를 속여서 서버 대신 요청을 보내게 만드는 공격이다. 피해자가 사용자냐 서버냐가 다르니 막는 방법도 완전히 다르다.

그래서 어떻게 막나

바이브 코딩으로 이런 기능을 만들었다면 막는 방법은 기능의 성격에 따라 갈린다.

미리보기나 웹훅처럼 특정 서비스만 상대하면 되는 기능이라면, 허용할 도메인 목록을 미리 정해두고 그 외에는 거부하는 게 가장 확실하다. 트위터 카드만 지원하면 되는데 아무 URL이나 다 받아줄 이유가 없다.

범용적으로 아무 URL이나 받아야 하는 기능이라면, 요청을 보내기 전에 그 주소가 가리키는 IP를 확인해서 내부 대역이면 걷어내야 한다. 걸러야 할 대역은 이렇다.

127.0.0.0/8      # localhost
10.0.0.0/8       # 사내망
172.16.0.0/12    # 사내망
192.168.0.0/16   # 사내망
169.254.0.0/16   # 클라우드 메타데이터 등 링크로컬 주소

처음 URL만 검사하고 끝내면 안 된다. LangChain 사례처럼 서버가 리다이렉트를 그냥 따라가면 우회당한다. follow_redirects를 켜뒀다면 리다이렉트가 발생할 때마다 같은 검사를 다시 거쳐야 한다.

도메인 이름은 정상인데 그 도메인의 DNS 응답이 나중에 내부 IP로 바뀌는 DNS 리바인딩이라는 수법도 있다. 처음 검사할 때는 정상 IP였다가 실제 요청을 보내는 순간에는 다른 IP로 바뀌어 있는 식이다. 공격자는 자기가 등록한 도메인의 DNS TTL을 몇 초 단위로 아주 짧게 걸어두고, 검사가 끝나자마자 응답을 내부 IP로 바꿔치기하는 방식으로 이 시간차를 직접 만들어낸다. 깊게 들어가면 복잡하지만, URL 검사와 실제 요청 사이에 시간차가 있으면 그 사이 DNS가 바뀔 수 있다는 사실 정도만 알아둬도 충분하다.

AWS를 쓴다면 IMDSv2를 강제로 켜두는 것도 방법이다. 메타데이터 엔드포인트에 접근하려면 토큰을 먼저 발급받아야 하는 방식이라, 단순 GET 요청 하나로 자격 증명을 빼가는 공격을 막아준다.

마지막으로 AI 에이전트에 fetch 도구를 붙였다면, 에이전트가 웹페이지나 메시지에서 읽은 URL을 지시가 아니라 데이터로 다루게 만들어야 한다. 에이전트가 가져온 텍스트 안에 또 다른 URL이나 명령어가 있어도 곧바로 실행하지 않고, 사람이 정해둔 도메인 허용 목록과 IP 필터를 반드시 거치게 만드는 것이다.

정리

fetch(userUrl) 한 줄은 서버가 사용자 대신 인터넷 어딘가에 다녀온다는 기능이다. 그 기능을 만드는 순간부터 그 URL이 어디를 가리킬 수 있는지 생각해야 한다. 링크 미리보기든, 웹훅 콜백이든, AI 에이전트의 URL 요약 도구든 구조는 같다. 사용자 입력이 서버의 네트워크 요청으로 그대로 이어지는 지점을 찾아서, 그 지점에 도메인 허용 목록과 내부 IP 차단을 걸어두는 게 SSRF 대응의 전부다. 특히 AI 에이전트가 사람 확인 없이 URL을 fetch하는 구조를 만들고 있다면, 에이전트가 읽어온 내용 속 링크를 명령이 아니라 그냥 데이터로 취급하도록 짜는 게 시작점이다.

YS

신윤섭

데이너스 대표 | AI 교육 & AX 컨설팅

81개 이상의 AI/AX 교육 과정을 설계하고, 50여 기업과 기관에서 강의했습니다. 강남세브란스, 삼성전자, 현대자동차 등 다양한 조직의 AI 역량 강화를 지원하고 있습니다.

AI 교육이 필요하신가요?

조직에 맞는 맞춤형 AI/AX 교육 프로그램을 설계해드립니다. 커리큘럼 상담부터 시작해보세요.

같은 주제의 다른 글