앱 9개 중 1개는 Supabase 키 유출 중: 유출 경위와 해결법

BBetter Stack
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00이번 달 보고서에 따르면 약 2만 개의 인디 앱을 조사한 결과, 9개 중 1개꼴로
00:00:04프런트엔드 코드에 Supabase 인증 정보가 노출되어 있었다고 합니다.
00:00:08서버 로그나 프라이빗 저장소에 있는 게 아닙니다.
00:00:11모든 방문자가 다운로드하는 자바스크립트 안에 그대로 들어있었죠.
00:00:14문제는 이 앱들이 해킹당한 게 아니라, 실수로 비밀 키를 포함해서 배포했다는 점입니다.
00:00:18분명히 말씀드리자면, 어떤 키들은 공개용으로 설계되었습니다.
00:00:21문제는 배포 과정입니다. 똑같은 실수로 브라우저에 절대로 노출되어서는 안 될
00:00:25비밀 키까지 함께 배포될 수 있기 때문이죠.
00:00:27저희는 유용한 영상을 계속해서 업로드하고 있습니다.
00:00:28구독하는 것 잊지 마세요.
00:00:35사람들이 흔히 혼란스러워하는 간단한 진실이 하나 있습니다.
00:00:38우리 모두 알고는 있지만, 바쁘게 작업하다 보면 놓치기 쉬운 부분이죠.
00:00:41환경 변수를 'public'으로 표시하면, 빌드 도구는 이를 브라우저용으로 간주하여
00:00:46클라이언트 번들에 포함시킵니다.
00:00:50Next.js는 NEXT_PUBLIC, Vite는 VITE_, SvelteKit은
00:00:56public 키워드를 사용하죠.
00:00:57'Next'는 안전 라벨이 아니라, 말 그대로 배송 라벨인 셈입니다.
00:01:01자, 이제 Supabase 관련 내용을 살펴봅시다.
00:01:04'anon'이나 'publishable' 키처럼 공개되어도 상관없는 키가 있는 반면,
00:01:10'service_role'이나 비밀 키처럼 절대 공개되면 안 되는 키도 있습니다.
00:01:12만약 비밀 키가 브라우저에 노출된다면, 어떤 일이 벌어질지 짐작이 가시죠?
00:01:17이런 경우에는 행 레벨 보안(RLS)도 소용이 없습니다.
00:01:20Supabase 문서에도 서비스 키는 RLS를 우회할 수 있다고 명시되어 있습니다.
00:01:24따라서 그 키가 프런트엔드에 있다면, 지금까지 설정한 보안 정책은 아무 의미가 없게 됩니다.
00:01:29제 샌드박스를 통해 어떻게 이런 일이 일어나는지 직접 보여드리겠습니다.
00:01:33먼저 Next.js로 간단한 CRUD 앱을 만들고 Supabase를 연결했습니다.
00:01:38여기가 제 .env 파일인데, 여기서 실수가 발생합니다.
00:01:41키를 'PUBLIC' 접두사가 붙은 변수에 넣었습니다.
00:01:45이 키는 공개용이므로 노출되는 것이 당연합니다.
00:01:48진짜 문제는 동일한 'NEXT_PUBLIC' 경로를 통해 실수로 비밀 키까지 배포될 수 있다는 것이죠.
00:01:53빌드를 위해 'npm run build'를 실행한 후 앱을 구동했습니다.
00:01:59크롬 브라우저 화면입니다.
00:02:00저희 CRUD 앱의 데이터베이스에 정보를 빠르게 추가해 보겠습니다.
00:02:05자, 이제 컴파일된 자바스크립트 번들을 열고 키를 검색해 보겠습니다.
00:02:10여기 있네요.
00:02:12URL과 키가 사용자가 브라우저로 내려받은 파일 안에
00:02:18그대로 박혀 있습니다.
00:02:19그리고 여기서 주목해야 할 점이 있습니다.
00:02:21아무도 이 앱을 해킹하지 않았다는 겁니다.
00:02:22제가 그냥 찾아낸 것뿐이죠.
00:02:24어떤 취약점을 공격한 것도 아닙니다.
00:02:25그저 앱이 이미 인터넷에 공개한 내용을 읽었을 뿐입니다.
00:02:29제 눈에 보인다면 누구나 볼 수 있다는 뜻입니다.
00:02:32개발자 도구를 열고 자바스크립트 파일을 확인해서 검색만 하면 됩니다.
00:02:35그게 전부예요.
00:02:36“아무도 안 보겠지”라고 생각하신다면 오산입니다. 인터넷에는 이런 정보를
00:02:41전문적으로 찾아내는 수많은 사람과 봇들이 널려 있습니다.
00:02:44이제 실제로 통하는 해결책을 알려드리겠습니다.
00:02:47브라우저는 오직 여러분의 API만 호출해야 합니다.
00:02:50API는 서버 사이드에서 실행됩니다.
00:02:52비밀 키가 있어야 할 곳은 바로 거기입니다.
00:02:54프라이빗한 작업은 API 경로(route)나 서버 함수로 옮기세요.
00:02:58클라이언트가 엔드포인트를 호출하면, 서버에서 Supabase를 호출하는 구조여야 합니다. 그다음 다시 빌드해서 번들을 확인해 보세요.
00:03:03번들에서 키가 사라졌다면 제대로 해결된 겁니다.
00:03:04하지만 여기서 만족하고 멈추지 마세요. 추가로 할 수 있는 것들이 더 있습니다.
00:03:09사용자가 접근하는 테이블에 행 레벨 보안(RLS)이 활성화되어 있는지 확인하고,
00:03:11설정한 정책이 의도한 대로 작동하는지 반드시 체크하세요.
00:03:16테스트에도 시간을 투자해야 합니다.
00:03:18이 부분은 종종 간과되곤 하죠.
00:03:19이제 문제가 재발하지 않도록 하는 방법입니다. 대부분 한 번은 고치지만,
00:03:21나중에 바쁠 때 다시 실수를 저지르곤 합니다.
00:03:26따라서 안전장치를 마련하세요. CI 단계에서 시크릿 스캐닝을 추가해
00:03:28키가 부적절한 곳에 노출될 경우 빌드가 실패하도록 만드세요.
00:03:34또한 PR 규칙을 정해 'NEXT_PUBLIC'이나 'Vite'가 포함된 코드는
00:03:36기본적으로 공개되는 것으로 간주하고 엄격히 검토해야 합니다.
00:03:41마지막으로, 키를 정기적으로 교체하세요.
00:03:42키가 노출된 것 같다는 의심이 조금이라도 든다면 즉시 새로 발급받으세요.
00:03:43며칠 더 지켜보는 것보다 훨씬 안전한 방법입니다.
00:03:47지금 바로 확인해 볼 수 있는 방법이 있습니다.
00:03:50실제 배포 방식 그대로 앱을 빌드해 보세요.
00:03:52빌드 결과물에서 'supabase', 'JWT', 'service_role' 등 토큰처럼 보이는 모든 것을 검색하세요.
00:03:55만약 비밀 키를 발견했다면 이미 유출된 것으로 간주해야 합니다.
00:04:01키를 교체하고 로직을 서버 사이드로 변경하세요.
00:04:05이 영상에서 딱 한 가지만 기억해야 한다면 이것입니다.
00:04:08“번들에 포함되었다면, 그것은 공개된 것입니다.”
00:04:11그럼 다음 영상에서 뵙겠습니다.
00:04:13We'll see you in another video.

Key Takeaway

클라이언트 번들에 포함된 모든 정보는 공개된 것과 다름없으므로, 비밀 키는 반드시 서버 사이드 로직으로 격리하여 보호해야 합니다.

Highlights

약 2만 개의 인디 앱 중 9개 중 1개꼴로 Supabase 비밀 키가 노출됨

환경 변수 접두사(NEXT_PUBLIC

Timeline

앱 보안 실태와 Supabase 키 노출 현황

최근 보고서에 따르면 조사 대상인 2만 개의 앱 중 상당수가 프런트엔드 코드에 Supabase 인증 정보를 노출하고 있습니다. 이는 서버 로그가 아닌 사용자가 다운로드하는 자바스크립트 파일 내에 직접 포함되어 있다는 점에서 심각한 문제입니다. 해킹 공격이 아니라 개발자가 실수로 비밀 키를 포함해 배포한 결과이며, 공개용 키와 혼동하여 발생하는 경우가 많습니다. 브라우저에 절대 노출되어서는 안 될 키가 포함될 수 있다는 위험성을 강조합니다. 이 섹션은 현재 많은 앱이 직면한 보안 취약점의 실태를 경고하며 시작합니다.

환경 변수 접두사의 오해와 클라이언트 번들링

개발자들이 흔히 저지르는 실수 중 하나는 환경 변수 접두사의 역할을 오해하는 것입니다. Next.js의 NEXT_PUBLIC이나 Vite의 VITE_와 같은 키워드는 해당 변수를 클라이언트 번들에 포함하라는 지시어입니다. 이를 '안전 라벨'이 아닌 '배송 라벨'로 인식해야 한다고 화자는 비유합니다. 바쁜 작업 중에 이러한 접두사를 붙이는 행위가 곧 브라우저 노출로 이어진다는 사실을 망각하기 쉽습니다. 따라서 공개용이 아닌 데이터에 이러한 접두사를 사용하는 것은 보안상 치명적인 결과를 초래합니다.

서비스 키 노출과 보안 정책의 무력화

Supabase의 키 체계에서 'anon' 키는 공개 가능하지만 'service_role' 키는 절대 비밀로 유지되어야 합니다. 서비스 키가 노출될 경우, 데이터베이스 보안의 핵심인 행 레벨 보안(RLS) 정책이 모두 우회됩니다. Supabase 공식 문서에도 서비스 키는 보안 정책을 무시할 수 있다고 명시되어 있어 더욱 주의가 필요합니다. 만약 이 키가 프런트엔드에 있다면 지금까지 설정한 모든 보안 설정은 사실상 무의미해집니다. 이 섹션은 특정 키의 역할과 그것이 노출되었을 때 발생하는 기술적 파급력을 설명합니다.

실습을 통한 키 노출 재현 및 검색 방법

화자는 직접 샌드박스 환경에서 Next.js 앱을 빌드하여 키가 어떻게 유출되는지 시연합니다. .env 파일에서 PUBLIC 접두사를 붙여 배포한 뒤, 크롬 개발자 도구에서 컴파일된 자바스크립트 번들을 확인합니다. 검색을 통해 코드 내부에 URL과 비밀 키가 그대로 박혀 있는 것을 보여주며 보안의 취약성을 증명합니다. 이는 해킹 기술이 필요한 것이 아니라 인터넷에 공개된 정보를 단순히 읽는 수준임을 강조합니다. 수많은 봇과 악의적인 사용자들이 이러한 정보를 전문적으로 수집하고 있다는 경고도 덧붙입니다.

서버 사이드 로직 전환을 통한 근본적 해결책

유출 문제를 해결하기 위한 가장 확실한 방법은 브라우저가 직접 데이터베이스를 호출하지 않게 하는 것입니다. 모든 비밀 키는 서버 사이드에서 실행되는 API 경로나 서버 함수 내부로 옮겨야 합니다. 클라이언트가 API 엔드포인트를 호출하면 서버가 대신 Supabase와 통신한 뒤 결과만 전달하는 구조를 가져야 합니다. 수정한 뒤에는 다시 빌드하여 자바스크립트 번들에서 키가 사라졌는지 반드시 확인해야 합니다. 이 방법은 데이터 접근 권한을 개발자가 완전히 통제할 수 있게 해주는 핵심 전략입니다.

지속 가능한 보안 관리와 사고 예방 수칙

단기적인 수정에 그치지 않고 지속적인 보안을 유지하기 위한 추가 조치들을 제안합니다. RLS 활성화 확인과 정기적인 테스트는 기본이며, CI/CD 단계에서 시크릿 스캐닝을 도입해 실수를 사전에 차단해야 합니다. PR 과정에서 특정 접두사가 포함된 코드를 엄격히 검토하고, 유출이 의심될 때는 즉시 키를 교체하는 과감함이 필요합니다. 실제 배포 환경과 동일하게 빌드하여 'JWT'나 'service_role' 같은 키워드를 직접 검색해 볼 것을 권장합니다. "번들에 포함되었다면 그것은 공개된 것"이라는 마지막 메시지로 보안 의식을 일깨우며 마무리합니다.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video