크로스 사이트 스크립팅 (XSS)

Cross-Site Scripting

설명

template.HTMLAttr는 문자열을 신뢰할 수 있는 HTML 속성으로 취급하며 이스케이프하지 않습니다. 사용자 입력으로 속성 전체를 조합한 뒤 이 타입으로 변환하면, 공격자가 따옴표를 이용해 속성을 벗어나 이벤트 핸들러 등을 삽입할 수 있습니다.

잠재적 영향

  • 공격자가 피해자의 페이지에서 JavaScript를 실행할 수 있습니다.
  • 페이지에 접근 가능한 정보의 유출, 화면 변조, 사용자 권한을 이용한 요청으로 이어질 수 있습니다.

해결 방법

  • 속성 이름과 구조는 템플릿에 고정하고, 값만 일반 문자열로 전달하세요.
  • 사용자 입력을 template.HTMLAttr로 변환하지 말고 html/template의 맥락별 이스케이프를 사용하세요.
  • 값의 용도에 맞게 허용 문자와 길이를 제한하세요.

예시

변경 전

go
package main

import (
    "html/template"
    "net/http"
)

func unsafeHandler(w http.ResponseWriter, r *http.Request) {
    // 쿼리 파라미터로부터 사용자 입력 수신
    color := r.URL.Query().Get("color") // 예: "red"를 기대

    // 비준수: 사용자 입력을 직접 HTML attribute로 조합
    // 공격 예: color="red" onclick="alert(1)"
    attr := "style=\"color:" + color + "\""

    // template.HTMLAttr는 이스케이프를 하지 않음
    safeAttr := template.HTMLAttr(attr)

    // 이 attr를 템플릿에 그대로 사용
    t := template.Must(template.New("page").Parse(`
        <html><body>
            <p {{.}}>Hello</p>
        </body></html>`))

    // 악성 입력이 그대로 attribute로 들어가 브라우저에서 실행될 수 있음
    _ = t.Execute(w, safeAttr)
}

변경 후

go
package main

import (
    "html/template"
    "net/http"
    "regexp"
)

var pageTmpl = template.Must(template.New("page").Parse(`
<html><body>
    <p style="color:{{.Color}}">Hello</p>
</body></html>`))

// 문자 종류와 길이를 제한하는 정규식 (유효한 CSS 색상인지까지 검증하지는 않음)
var colorRe = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,20}$`)

func safeHandler(w http.ResponseWriter, r *http.Request) {
    color := r.URL.Query().Get("color")
    if !colorRe.MatchString(color) {
        // 유효하지 않은 값이면 기본값 사용
        color = "black"
    }

    // html/template에 일반 문자열로 전달하면
    // 템플릿 엔진이 context에 맞게 자동 이스케이프
    data := struct {
        Color string
    }{Color: color}

    if err := pageTmpl.Execute(w, data); err != nil {
        http.Error(w, "internal error", http.StatusInternalServerError)
        return
    }
}

설명: 변경 전에는 red" onclick="alert(1) 같은 입력이 새로운 이벤트 속성을 만들 수 있습니다. 변경 후에는 Color를 일반 문자열로 전달해 CSS 값에 맞는 필터링과 이스케이프를 적용합니다. 정규식은 문자 종류와 길이를 제한하며, 모든 허용 값이 유효한 CSS 색상임을 보장하지는 않습니다.

참조