~/VibeHandbook
$39

챕터 18 · 02

인젝션: 입력이 코드가 될 때

인젝션은 가장 오래됐으면서 여전히 가장 흔한 심각한 버그다. 사용자로부터 온 데이터가 데이터가 아니라 명령으로 취급될 때 발생한다. 대부분의 앱에서는 두 가지 종류가 중요하다.

SQL injection은 사용자 입력이 데이터베이스 쿼리 안으로 미끄러져 들어갈 때 일어난다. 전형적인 취약 패턴:

// VULNERABLE: 사용자의 입력이 쿼리에 그대로 붙어버린다
app.get("/user", (req, res) => {
  const name = req.query.name;
  db.query(`SELECT * FROM users WHERE name = '${name}'`);
});
// 누가  name = '; DROP TABLE users; --  를 넘기면
// 당신의 쿼리는 테이블을 삭제하라는 명령이 된다.

해결책은 parameterized query(prepared statement라고도 한다)다. 쿼리와 데이터를 따로 보내서 데이터베이스가 둘을 결코 혼동하지 않게 한다:

// SAFE: 값은 파라미터로 전달되고, 절대 코드로 취급되지 않는다
app.get("/user", (req, res) => {
  const name = req.query.name;
  db.query("SELECT * FROM users WHERE name = ?", [name]);
});

규칙: 문자열 연결로 쿼리를 만들지 말라. diff에서 변수 주위로 SQL을 조립하는 백틱이나 +가 보이면, 멈추고 parameterized 버전을 요청하라.

**XSS (cross-site scripting)**는 브라우저에서 일어나는 같은 발상이다. 사용자 입력을 받아 페이지에 raw HTML로 떨어뜨리면, 공격자는 당신의 다른 사용자들의 브라우저에서 실행되는 <script> 태그를 주입할 수 있다 — 예를 들어 그들의 세션을 훔쳐서. 해결책은 이스케이핑이다: 사용자 콘텐츠를 HTML이 아니라 텍스트로 렌더링하라. React 같은 현대 프레임워크는 기본적으로 이스케이프해주는데, 이건 훌륭하다 — AI가 "작동하게 만들려고" dangerouslySetInnerHTML이나 innerHTML 같은 탈출구에 손을 뻗기 전까지는. 그것들은 보호를 우회한다. diff에서 그런 호출이 보이면 의심해야 할 대상으로 취급하라.

모든 인젝션 버그 뒤에 있는 통합 원리: 데이터와 코드를 분리하라. 사용자 입력이 쿼리, 명령, 템플릿, 혹은 HTML로 넘어갈 때마다, 무언가가 그것을 이스케이프하거나 parameterize해야 한다.

오프라인으로 보고 싶으세요?

PDF + EPUB + 다운로드형 프롬프트 라이브러리 + 버전 업데이트를 받으세요.

$ PDF 받기 — $39