웹앱에서 보안과 성능을 높이기 위한 best practice
- 데이터베이스 연결 풀링 (Connection Pooling) 활용 문제점: 단일 TCP 연결로 모든 쿼리를 보내는 것은 비효율적이며, 응답 순서가 보장되지 않는 파이프라이닝 문제가 발생할 수 있습니다.
해결책: pg 라이브러리의 Pool 기능을 사용하여 미리 여러 개의 연결을 생성합니다. 쿼리 실행 시 풀에서 연결을 예약하고, 작업이 끝나면 다시 반환하는 방식을 통해 효율성을 극대화합니다.
- '신의 모드(God Mode)' 관리자 계정 사용 금지 문제점: 백엔드 앱이 데이터베이스 관리자(Superuser) 권한으로 접속하면, SQL 인젝션 공격 시 전체 데이터베이스가 삭제될 위험이 있습니다.
해결책: 최소 권한 원칙(Principle of Least Privilege)에 따라 각 라우트(기능)별로 전용 DB 사용자를 생성합니다.
dbread: SELECT 권한만 부여
dbcreate: INSERT 권한만 부여
dbdelete: DELETE 및 조회를 위한 SELECT 권한 부여
- 기능별 전용 연결 풀 구축 각 라우트의 성격에 맞춰 별도의 풀을 설정합니다.
읽기(Read): 사용량이 많으므로 많은 수의 연결을 미리 할당합니다.
쓰기/삭제(Create/Delete): 상대적으로 빈도가 낮으므로 최소한의 연결로 시작하여 필요할 때 생성하도록 설정합니다.
- 실전 트러블슈팅: 권한 설정의 세밀함 시퀀스(Sequence) 권한: SERIAL 타입을 사용하는 ID 컬럼에 데이터를 삽입할 때는 테이블 INSERT 권한뿐만 아니라, 다음 번호를 가져오기 위한 시퀀스에 대한 USAGE 및 SELECT 권한이 반드시 필요합니다.
삭제(Delete)를 위한 조회 권한: 데이터를 삭제하려면 해당 행을 식별해야 하므로 DELETE 권한과 함께 SELECT 권한도 부여해야 정상 작동합니다.
- 기타 보안 및 개발 관행 환경 변수 사용: 비밀번호를 코드에 하드코딩하지 말고 환경 변수(process.env)를 통해 관리해야 합니다.
에러 메시지 관리: 사용자에게 데이터베이스의 상세 에러(코드 라인, 내부 구조 등)를 그대로 노출하지 마세요. 이는 해커에게 힌트를 주는 행위이므로, 백엔드에서 에러를 래핑하여 간결한 메시지만 전달해야 합니다.
Unbounded Query 방지: SELECT *와 같이 제한 없는 쿼리는 성능 부하를 주므로, 항상 LIMIT이나 페이징 처리를 적용해야 합니다.