[2025-08-12] CSTI

πŸ¦₯ λ³Έλ¬Έ

μ •μ˜ 및 κ°œλ…

Client-Side Template Injection. λΈŒλΌμš°μ €μ—μ„œ λ™μž‘ν•˜λŠ” ν…œν”Œλ¦Ώ 엔진이 이용자의 μž…λ ₯값을 ν…œν”Œλ¦Ώ ꡬ문으둜 ν•΄μ„λ˜μ–΄ λ Œλ”λ§λ  λ•Œ, μ˜λ„ν•˜μ§€ μ•Šμ€ ν‘œν˜„μ‹ ν‰κ°€λ‚˜ 슀크립트 ꡬ문 싀행을 μΌμœΌν‚€λŠ” 취약점.

λ™μž‘ 흐름

  1. 앱이 ν΄λΌμ΄μ–ΈνŠΈ ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•΄ DOM을 λ Œλ”λ§
  2. κ°œλ°œμžκ°€ μ‚¬μš©μž μž…λ ₯을 ν…œν”Œλ¦ΏμœΌλ‘œ 해석될 수 μžˆλŠ” μœ„μΉ˜μ— λ„£λŠ”λ‹€
  3. ν…œν”Œλ¦Ώ 엔진이 ν•΄λ‹Ή λ…Έλ“œλ₯Ό 컴파일/바인딩 ν•˜λ©΄μ„œ {{..}} 같은 ν‘œν˜„μ‹μ„ 평가
  4. JS μ‹€ν–‰μœΌλ‘œ 이어져 DOM XSSκ°€ λœλ‹€.

ν…œν”Œλ¦Ώ μ’…λ₯˜ 및 취약점

  • Vue : ν”„λ‘ νŠΈμ—”λ“œ ν”„λ ˆμž„ μ›Œν¬.
    • EX)

        <script src="https://unpkg.com/vue@3"></script>
              
        <div id="app">{{ message }}</div>
              
        <script>
          Vue.createApp({
            data() {
              return {
                message: 'Hello Vue!'
              }
            }
          }).mount('#app')
        </script>
      
    • μ·¨μ•½ μ½”λ“œ 예제

        <script src="https://unpkg.com/vue@3"></script>
              
        <div id="app">
        	<?php echo htmlspecialchars($_GET['msg']); ?>
        </div>
              
        <script>
          Vue.createApp({
            data() {
              return {
                message: 'Hello Vue!'
              }
            }
          }).mount('#app')
        </script>
      
      • $_GET['msg'] : URLμ—μ„œ msg 값을 λ°›μ•„μ™€μ„œ λ³΄μ—¬μ€Œ
      • htmlspecialchars() : <, > 같은 HTML νƒœκ·Έλ₯Ό μΈμ½”λ”©ν•˜μ—¬ 일반적인 XSS 차단
        • ?msg=<script>alert(1)</script> β†’ <script>κ°€ &lt;script&gt;둜 λ°”λ€Œμ–΄μ„œ 슀크립트 νƒœκ·Έλ‘œ μ‹€ν–‰λ˜μ§€ μ•ŠμŒ.
      • ?msg={{1+1}} μ—μ„œ 2κ°€ 좜λ ₯λ˜μ—ˆλ‹€λ©΄ CSTI κ°€λŠ₯
    • {{_Vue.h.constructor}} : μƒμ„±μž. ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³  ν˜ΈμΆœν•˜κΈ° λ•Œλ¬Έμ— XSS 곡격이 κ°€λŠ₯ν•˜λ‹€

      • EX) {{_Vue.h.constructor("alert(1)")}}
  • AngularJS : ν”„λ‘ νŠΈμ—”λ“œ ν”„λ ˆμž„μ›Œν¬. νƒ€μž…μŠ€ν¬λ¦½νŠΈ 기반
    • EX)

        <!doctype html>
        <html ng-app>
          <head>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js"></script>
          </head>
          <body>
            <div>
              <label>Name:</label>
              <input type="text" ng-model="yourName" placeholder="Enter a name here">
              <hr>
              <h1>Hello {{yourName}}!</h1>
            </div>
          </body>
        </html>
      
    • μ·¨μ•½ μ½”λ“œ 예제

        <!doctype html>
        <html ng-app>
          <head>
            <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js"></script>
          </head>
          <body>
              <?php echo htmlspecialchars($_GET['msg']); ?>
          </body>
        </html>
      
      • Vue의 μ·¨μ•½ 예제 μ½”λ“œμ™€ λ§ˆμ°¬κ°€μ§€
      • htmlspecialchars() : <, > 같은 HTML νƒœκ·Έλ₯Ό μΈμ½”λ”©ν•˜μ—¬ 일반적인 XSS 차단
    • {{constructor.constructor}} : μƒμ„±μž. ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³  ν˜ΈμΆœν•˜μ—¬ 곡격

      • EX) {{constructor.constructor("alert(1)")()}}

λŒ€μ‘ 방법

  1. μ‚¬μš©μžμ˜ μž…λ ₯은 항상 λ°μ΄ν„°λ‘œλ§Œ 닀루고, ν…œν”Œλ¦ΏμœΌλ‘œ 해석될 수 μžˆλŠ” 경둜둜 λ„£μ§€ 말기
  2. ν”„λ ˆμž„μ›Œν¬λ³„ 금기/ꢌμž₯
    • Vue : v-html 에 μ™ΈλΆ€/μ‚¬μš©μž μž…λ ₯ κΈˆμ§€
    • Angular : $sce.trustAsHtml λ‚¨μš© κΈˆμ§€, ng-bind-html에 μ‚¬μš©μž μž…λ ₯ κΈˆμ§€. $compile둜 μ‚¬μš©μž μž…λ ₯을 λ‹€μ‹œ μ»΄νŒŒμΌν•˜μ§€ 말 것.
  3. CSPμ—μ„œ script-src 'self'; object-src 'none'; 을 톡해 XSS ν”Όν•΄ μΆ•μ†Œ
  4. ν…œν”Œλ¦Ώ ꡬ문 μ΄μŠ€μΌ€μ΄ν”„

Categories:

Updated:

Leave a comment