오늘은 사이드 프로젝트를 진행하다가 발견한 'API Key값 노출 문제'를 해결해 본 경험을 기록해보려고 한다.
📕 1. 문제 발견
현재 진행 중인 사이드 프로젝트에서 챗봇 기능이 필요해,
OpenAI의 API를 이용하여 간단한 '챗봇'을 만들어보았다.
챗봇 모델을 직접 구현하는 것도 아니고 이미 존재하는 API를 사용하여 설계하면 되는 거라 금방 구현에 성공했으나
이렇게 OpenAI API를 사용하여 클라이언트와 OpenAI가 직접적으로 통신하고 나면,
아래 스크린 샷처럼 개발자 도구의 네트워크 창에 Request URL과 API KEY값이 노출되는 문제를 발견했다.
[ Request URL 노출 ]
[ API KEY값 노출 ]
리퀘스트 헤더를 나타내는 이 부분에서
Authorization을 살펴보면 Bearer sk- 로 시작하는 OpenAI API KEY 값이 노출되는 것을 확인할 수 있다.
이 API KEY 값이 외부에 노출되고, 누군가가 내 키 값을 악의적으로 사용한다면
그 피해는 고스란히 내가 떠안게 된다.
특히 OpenAI나 AWS 같은 경우는 과금과 관련이 있기 때문에 까딱하면 내 쌩돈이 나갈 수도 있다..
그래서 이미 기존에 깃허브에 올릴 떄, API 키 값의 노출을 막기 위해
키 값을 .env에 담고 깃허브에 올리지 않는 방식을 사용하고 있었는데
배포를 하기 전에 살펴보던 중, .env를 사용했음에도 위처럼 내 키값이 노출되는 것을 확인하게 되었다.
📕 2. 해결 방법
결론부터 간단하게 설명하면, 클라이언트와 OpenAI 사이에 서버를 두었다.
기존에 구현했던 방식은 다음과 같다.
클라이언트에서 헤더에 API 키값을 담아 OpenAI에 요청하면,
OpenAI에서 데이터를 받아 바로 화면에 띄우는 것.
이 방식은 간편하긴 하지만, 클라이언트에서 바로 외부 API와 통신하기 때문에
위에서 언급한 키 값 노출의 위험이 존재한다.
그래서 수정한 구현은 아래와 같다.
현재 진행하고 있는 프로젝트 자체가
Next.js에서 서버와 클라이언트를 모두 구현하고 있기 때문에 이 방식을 사용했다.
클라이언트에서 서버로 API를 요청하면 (이때 위의 방식과 달리, Key값을 헤더에 담지 않는다.)
서버가 클라이언트 대신에 OpenAI와 통신을 한 뒤 결괏값을 받아오고
그 결과값을 다시 클라이언트에 리턴해주는 방식이다.
이렇게 되면, API Key와 같은 민감한 정보를 서버에 저장하기 때문에 보안을 강화할 수 있다.
또한, 클라이언트에서는 민감한 정보를 다룰 필요가 없어진다.
코드로는 아래와 같다. ( Next.js 14, app router 기준 )
[ 클라이언트 코드 ]
- 미리 구현한 chatbot API에 POST 요청한다.
- 이때 전송을 위해 필요한 정보는 body에 담아 보낸다.
[ 서버 코드 ]
- 서버에서는 클라이언트를 대신에 openai와 소통을 한다.
- 결괏값을 받아오면 그대로 return 해준다.
📕 3. 결과 및 느낀 점
- 사용자가 개발자 도구를 켜도 서버로 전송한 api 내용만 볼 수 있지, 기존에 노출되었던 key값이나 외부 API의 URL은 노출되지 않게 되었다.
- 사실 서버를 통해서 외부 API와 통신하는 방식을 고민하기는 했었지만, 똑같은 내용의 통신을 클라이언트->서버, 서버-> 외부 API, 이렇게 2번 하면 비효율적일 수도 있겠다 직접적인 통신을 선택했었다.
- 그리고 그동안 사용했었던 네이버 지도나 카카오 로그인 API들은 애초에 해당 키 값을 사용할 수 있는 도메인 주소를 미리 등록해 놓음으로써 불상사를 막을 수 있어 노출되어도 큰 문제는 아니겠다는 안일한 생각이 있었던 것 같다.(허용 주소를 localhost:3000으로 하지 않는 이상!)
- 하지만, 이번 기회에 API 키 값의 노출을 막을 수 있는 방법을 알게 되었고, 서버의 역할과 프론트엔드로서 보안에 신경을 쓸 수 있는 것에 하나 더 깨달은 기회였던 것 같다. 앞으로도 이런 것들을 찾아보며 공부해야겠다.
위 글은 Next.js 14의 app router를 기준으로 작성되었으며,
제가 작성한 글의 내용 중 틀리거나 피드백해 주실 부분이 있다면
댓글을 남겨주셔서 저의 부족한 부분을 채워주시면 감사하겠습니다!😶
'🎬Project' 카테고리의 다른 글
[Next.js] useRef를 활용하여 동적인 랜딩 페이지 구현하기 (0) | 2024.05.06 |
---|---|
[React] useRef 훅에 대해 이해하기 ( + useState와 let과의 차이점 ) (0) | 2024.04.29 |
[Next.js] AWS S3와 next/image를 사용해 이미지 최적화를 진행한 경험 (0) | 2024.04.13 |
PWA(프로그래시브 웹 앱) (0) | 2023.10.15 |
[Project][23.07.04 ~ 08.18] SSAFY 1차 공통 프로젝트 KPT 회고 (1) | 2023.08.26 |