Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
safetext — 안전한 YAML 및 셸 생성을 위한 Go 라이브러리로, 구문 인식 템플릿을 사용하여 신뢰할 수 있는 데이터에 대한 어노테이션을 통해 주입 공격을 탐지하고 차단합니다. | Kitploit
도구/GitHubGitHub/google/safetext
Defensive ToolsScripting & AutomationWeb SecurityDevSecOps
GitHubgoogle/safetext

safetext

안전한 YAML 및 셸 생성을 위한 Go 라이브러리로, 구문 인식 템플릿을 사용하여 신뢰할 수 있는 데이터에 대한 어노테이션을 통해 주입 공격을 탐지하고 차단합니다.

저장소 보기
150985개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

safetext

이 제품은 공식적으로 지원되는 Google 제품이 아닙니다.

YAML과 같은 형식을 생성하기 위한 safe-by-construction 라이브러리입니다. text/template이나 sprintf처럼 생성하는 형식의 구문을 인식하지 못해 주입 취약점에 노출된 라이브러리를 대체합니다.

예제 사용 사례

text/template은 자신이 생성하는 형식의 구문을 인식하지 못하므로 주입 취약점에 대한 보호 기능을 제공하지 않습니다.

YAML을 생성하기 위해 text/template을 사용하는 다음 produceConfig 함수를 살펴보겠습니다:

root@kitploit:~
package main

import (
        "bytes"
        "fmt"
        "text/template"
)

func produceConfig(params any) (error, string) {
        tmpl, _ := template.New("test").Parse("{ hello: {{ .addressee }} }")

        var buf bytes.Buffer
        err := tmpl.Execute(&buf, params)
        if err != nil {
                return err, ""
        }

        return nil, buf.String()
}

func main() {
        goodReplacements := map[string]interface{}{
                "addressee": "safe",
        }

        err, config := produceConfig(goodReplacements)

        if err == nil {
                fmt.Println(config)
        } else {
                fmt.Printf("Error: %v\n", err)
        }

        badReplacements := map[string]interface{}{
                "addressee": "world, oops: true",
        }

        err, config = produceConfig(badReplacements)

        if err == nil {
                fmt.Println(config)
        } else {
                fmt.Printf("Error: %v\n", err)
        }
}

이 프로그램은 악의적인 addressee 입력값이 템플릿 실행 결과에 새 YAML 키를 주입할 수 있음을 보여줍니다.

text/template을 사용하면 이런 경우에도 오류가 발생하지 않으며, 프로그램 출력은 다음과 같습니다:

root@kitploit:~
{ hello: safe }
{ hello: world, oops: true }

반면 text/template 대신 safetext/yamltemplate로 전환하면 주입이 방지되고, 출력은 다음과 같습니다:

root@kitploit:~
{ hello: safe }
Error: YAML Injection Detected

text/template 대체 지침

입력 데이터 필드에 접근할 때 주입 탐지가 자동으로 적용됩니다.

  • 또한 모든 함수 호출 결과에 수동으로 적용할 수도 있습니다:

    root@kitploit:~
    {{ RetrieveUntrustedData | ApplyInjectionDetection }}
    
  • StructuralData 어노테이션을 적용하면 특정 필드에 대해 주입 탐지 로직을 비활성화할 수 있습니다:

    root@kitploit:~
    {{ (StructuralData .x) }}
    
  • StructuralData 어노테이션은 입력값이 변경되지 않아야 하는 함수에 입력을 전달할 때도 필요합니다. 예를 들어 일종의 조회(lookup)를 수행하는 경우입니다:

    root@kitploit:~
    name: {{ readFile (StructuralData .pathToName) | ApplyInjectionDetection }}
    
  • 가능하면 조건식, range 루프 등 text/template의 기능을 최대한 활용해 StructuralData 어노테이션을 피하는 것이 좋습니다. 예를 들어 다음 코드 대신:

    root@kitploit:~
    properties:
        {{ (StructuralData .PropertiesYaml) }}
    

    다음과 같이 작성하세요:

    root@kitploit:~
    properties:{{ range .Properties }}
        - {{ . }}{{ end }}
    

yamltemplate

yamltemplate의 목적은 기본적으로 입력 데이터의 어떤 문자열도 결과 YAML의 구조에 영향을 미치지 못하게 하고 값에만 영향을 미치도록 하는 것입니다.

  • 예를 들어 아래 템플릿은 Name 입력으로 인한 모든 주입을 자동으로 방지하면서 yamltemplate과 그대로 호환됩니다:

    root@kitploit:~
    name: {{.Name}}
    
  • 그러나 결과 YAML 구조를 변경할 것으로 예상되는 템플릿 노드(예: 임의의 YAML 설정 삽입)는 명시적으로 StructuralData로 어노테이션해야 합니다:

    root@kitploit:~
    config: {{ (StructuralData .Config) }}
    
  • StructuralData 어노테이션이 필요한 또 다른 경우는 YAML 구조에 완전한 맵을 포함해야 할 때입니다. StructuralData만 사용하면 키를 통해 주입이 통과될 수 있으므로 여기에 추가 검증 계층이 필요합니다:

    root@kitploit:~
    labels:
    {{- range $key, $value := .Labels }}
        {{ (StructuralData $key | MapKey) }}: {{ $value }}
    {{- end }}
    

    이에 대응하는 golang 측 코드는 다음과 같을 수 있습니다:

    root@kitploit:~
    func mapKeyFunc(data any) (string, error) {
        if v, ok := data.(string); ok {
            matched, err := regexp.MatchString(`^[a-zA-Z0-9/\-.]+$`, v)
            if err != nil {
                return "", err
            }
            if !matched {
                return "", fmt.Errorf("invalid characters in the key: %v", v)
            }
            return v, nil
        }
    
        return "", errors.New("invalid input")
    
    } ...
    
    tmp:= template.New("something")
    tmp.Funcs(map[string]any{"MapKey":mapKeyFunc})
    tmpl := template.Must(tmp.Parse(yamlTemplate))
    

yamltemplate에서 지원되지 않는 사용 사례

  • 중복 키가 있는 YAML. 중복 키는 비표준 YAML이며 이 라이브러리에서 지원되지 않습니다. YAML 템플릿에서 중복 키를 제거하도록 리팩터링하세요. 예를 들어:

    root@kitploit:~
    - project:
       members: member-a
       members: member-b
    

    다음으로:

    root@kitploit:~
    - project:
      members: member-b
    

shtemplate

shtemplate은 명시적인 어노테이션 없이도 입력 데이터 문자열 중 어느 것도 새 명령이나 플래그를 주입할 수 없도록 보장하면서 셸 스크립트를 생성할 수 있게 설계되었습니다.

  • 예를 들어 문자열 하나만 출력하도록 설계된 템플릿 스크립트는 해당 문자열이 새 명령 `./evil`을 주입하는 경우 렌더링에 실패합니다:

    root@kitploit:~
    echo "{{ .addressee }}"
    
  • 입력 문자열이 템플릿 문자열에 없는 새 명령을 포함하도록 명시적으로 허용하려면 StructuralData 어노테이션을 사용할 수 있습니다:

    root@kitploit:~
    {{ (StructuralData .commands) }}
    
  • 플래그(-로 시작하는 인수)도 기본적으로 금지됩니다. 예를 들어 아래 템플릿은 Filename이 --interactive인 경우 렌더링에 실패합니다:

    root@kitploit:~
    git add {{ .Filename }}
    
  • 명령 인수로 전달되는 입력 문자열이 플래그가 될 수 있도록 명시적으로 허용하려면 AllowFlags 어노테이션을 사용할 수 있습니다:

    root@kitploit:~
    git add {{ (AllowFlags .FilenameOrGitAddFlag) }}
    
  • 단일 입력 문자열에서 여러 인수를 전달하는 것도 기본적으로 금지됩니다. 이러한 구문은 배열과 range 표현식을 사용해 구현해야 합니다:

    root@kitploit:~
    ls {{ range .Paths }}{{.}} {{end}}
    

text/template 대체에서 지원되지 않는 사용 사례

  • 템플릿 시스템 외부의 이스케이프 로직. 대신 이스케이프 로직을 템플릿에 어노테이션으로 추가해야 합니다(예: .UntrustedField | escape).

  • 부분 형식(partial format). 이 라이브러리들은 완전한 파일을 생성하도록 설계되었습니다. 세그먼트를 생성한 다음 서로 연결한다면, 이 로직을 템플릿 시스템 자체로 옮겨야 합니다(if나 range 같은 구문 사용).

  • 부작용이 있는 함수. 이 라이브러리들은 템플릿을 여러 번 실행하는 방식으로 작동하므로, 부작용이 있는 함수를 등록하면 예기치 않은 동작이 발생할 수 있습니다(예: id: {{ AllocateID }}).

shsprintf

shsprintf는 잠재적으로 잘못된 이스케이프와 관계없이 입력 데이터 문자열 중 어느 것도 새 명령이나 플래그를 주입할 수 없도록 보장하면서 셸 스크립트를 생성할 수 있게 설계되었습니다. 아래 예제를 보면, 명령이 주입된 스크립트 대신 shsprintf.ErrShInjection 오류가 반환됩니다:

root@kitploit:~
message := "`whoami`"
result, err := shsprintf.Sprintf("git commit -m %s", message)

shsprintf.Sprintf는 fmt.Sprintf와 비교해 오류 반환 값이 추가되지만 API는 그 외에 동일합니다. panic이 허용되는 상황에서는 shsprintf.MustSprintf를 사용할 수 있습니다.

shsprintf에는 사용을 권장하는 이스케이프 함수가 포함되어 있습니다:

root@kitploit:~
message := "`whoami`"
result := shsprintf.MustSprintf("git commit -m %s", shsprintf.EscapeDefaultContext(message))

text/template과 달리 특별한 어노테이션은 없습니다. 예를 들어 여러 인수를 전달해야 한다면 형식 문자열을 변경하여 처리해야 합니다:

root@kitploit:~
files := []any{ "file1", "file2", "file3" }
result, err := shsprintf.Sprintf("cat" + strings.Repeat(" %s", len(files)), files...)
도구 다운로드
  • yamltemplate은 shprintf와 함께 결합할 수 있습니다. 다음 cloud-init yaml 템플릿을 고려해 보세요:

    root@kitploit:~
    ---
    write_files:
    - path: /etc/nginx/refresh.sh
      owner: root:root
      permissions: 0755  # Don't forget the 0 (you are probably using octal...)
      content: |
        #!/bin/bash
        set -euo pipefail
    
        {{ shprintf `curl %s > /tmp/something` .userInput }}
    

    이 템플릿을 safetext/yamltemplate으로 평가하면 셸 명령 및 YAML 주입이 모두 방지됩니다.

    이렇게 하려면 golang 측을 다음과 같이 설정해야 합니다:

    root@kitploit:~
    tmp:= addons.WithShsprintf(template.New("something"))
    tmpl := template.Must(tmp.Parse(yamlTemplate))