보안 취약점을 찾기 위해 CodeQL을 사용하여 CORS 프레임 워크를 모델링합니다

보안 취약점을 찾기 위해 CodeQL을 사용하여 CORS 프레임 워크를 모델링합니다

웹 애플리케이션을 위해 CORS를 설정할 때 발생할 수있는 다양한 유형의 취약점이 있으며, 수제 CORS 구현에서 CORS 프레임 워크 및 논리 오류를 불안하게 사용하면 공격자가 인증을 우회 할 수있는 심각한 보안 취약점으로 이어질 수 있습니다. 또한 공격자는 CORS의 오해를 활용하여 웹 애플리케이션에서 기존의 다른 취약점의 심각성을 확대하여 인트라넷의 서비스에 액세스 할 수 있습니다.

브라우저의 두 웹 사이트 간의 통신을 보여주는 CORS 다이어그램.

이 블로그 게시물에서는 개발자와 보안 연구원이 CodeQL을 사용하여 자신의 라이브러리를 모델링하여 GO에서 GO에서 수행 한 작업을 예로 들어 어떻게 수행 할 수 있는지 보여줍니다. 내가 사용한 기술은 다른 프레임 워크를 모델링하는 데 유용하기 때문에이 블로그 게시물은 자신의 프로젝트에서 취약점을 모델링하고 찾는 데 도움이 될 수 있습니다. CodeQL과 같은 정적 분석기는 구조, 기능 및 가져온 라이브러리에 대한 자세한 정보를 얻을 수 있으므로 GREP와 같은 간단한 도구보다 다재다능합니다. 또한 CORS 프레임 워크는 종종 특정 구조 및 함수를 통한 설정 구성을 사용하기 때문에 CodeQL을 사용하는 것이 코드베이스에서 오해를 찾는 가장 쉬운 방법입니다.

CodeQL에 Code를 추가 할 때는 휠을 재창조하지 않도록 이미 사용 가능한 관련 쿼리 및 프레임 워크를 항상 확인하는 것이 가장 좋습니다. 대부분의 언어의 경우 CodeQL에는 이미 많은 기본 케이스를 다루는 CORS 쿼리가 있습니다. CORS를 구현하는 가장 쉽고 간단한 방법은 수동으로 설정하는 것입니다. Access-Control-Allow-Origin 그리고 Access-Control-Allow-Credentials 응답 헤더. CodeQL은 언어 (예 : Django, Fastapi 및 Flask)의 프레임 워크를 모델링하여 해당 헤더가 설정된 코드의 위치를 ​​식별 할 수 있습니다. CodeQL은 특정 헤더 값을 찾아 해당 모델을 바탕으로 CORS의 간단한 예를 찾아 취약한 값과 일치하는지 확인할 수 있습니다.

다음과 같은 예에서, 서버의 무단 리소스는 임의의 웹 사이트에서 액세스 할 수 있습니다.

func saveHandler(w http.ResponseWriter, r *http.Request) { 
    w.Header().Set("Access-Control-Allow-Origin", "*") 
}

이는 공격자가 위험한 종말점에 액세스하고 악용 할 수 있기 때문에 로컬로 호스팅하려는 도구와 같이 인증이없는 웹 애플리케이션에 문제가 될 수 있습니다.

이것은 CodeQL이 Set 방법 보안 관련 헤더를 찾는 방법이 프레임 워크에 대한 쓰기. 헤더 쓰기는 다음에 의해 모델링됩니다 HeaderWrite 수업 HTTP.qll모든 헤더 쓰기를 찾기 위해 다른 모듈과 클래스에 의해 확장됩니다.

 /** Provides a class for modeling new HTTP header-write APIs. */
  module HeaderWrite {
    /**
     * A data-flow node that represents a write to an HTTP header.
     *
     * Extend this class to model new APIs. If you want to refine existing API models,
     * extend `HTTP::HeaderWrite` instead.
     */
    abstract class Range extends DataFlow::ExprNode {
      /** Gets the (lower-case) name of a header set by this definition. */
      string getHeaderName() { result = this.getName().getStringValue().toLowerCase() }

다음과 같은 유용한 방법 getHeaderName 그리고 getHeaderValue 또한 CORS의 오해와 같은 헤더와 관련된 보안 쿼리를 개발하는 데 도움이 될 수 있습니다. 이전 코드 예제와 달리, 아래 패턴은 그 효과가 훨씬 더 큰 영향을 미치는 CORS의 오해의 예입니다.

func saveHandler(w http.ResponseWriter, r *http.Request) { 
    w.Header().Set("Access-Control-Allow-Origin", 
    r.Header.Get("Origin"))
    w.Header().Set("Access-Control-Allow-Credentials", 
    "true") 
}

요청 원산지 헤더를 반영하고 자격 증명을 허용하면 공격 웹 사이트가 현재 로그인 한 사용자로서 요청을 만들 수 있으며 전체 웹 응용 프로그램이 손상 될 수 있습니다.

CodeQL을 사용하면 헤더를 모델링하여 CodeQL이 관련 보안 코드 구조를 식별하여 CORS 취약점을 찾을 수 있도록 특정 헤더 및 메소드를 찾을 수 있습니다.

/**
 * An `Access-Control-Allow-Credentials` header write.
 */
class AllowCredentialsHeaderWrite extends Http::HeaderWrite {
    AllowCredentialsHeaderWrite() {
        this.getHeaderName() = headerAllowCredentials()
    }
}

/**
 * predicate for CORS query.
 */
predicate allowCredentialsIsSetToTrue(DataFlow::ExprNode allowOriginHW) {
        exists(AllowCredentialsHeaderWrite allowCredentialsHW |
                allowCredentialsHW.getHeaderValue().toLowerCase() = "true"

여기, HTTP::HeaderWrite 이전에 논의 된 바와 같이 클래스는 슈퍼 클래스로 사용됩니다. AllowCredentialsHeaderWrite모든 헤더가 값에 대한 쓰기를 찾습니다 Access-Control-Allow-Credentials. 그런 다음 CORS Misconfiguration 쿼리가 자격 증명이 활성화되어 있는지 확인하면 allowcredentialsheaderwrite를 확인할 수있는 소스 중 하나로 사용합니다.

개발자가 CORS 정책을 설정하는 가장 간단한 방법은 서버에서 HTTP 응답에 대한 헤더를 설정하는 것입니다. 헤더가 설정된 모든 인스턴스를 모델링하면 CORS 쿼리에서 이러한 CORS 케이스를 확인할 수 있습니다.

CodeQL을 사용하여 웹 프레임 워크를 모델링 할 때 HTTP::HeaderWrite 모델의 영향을 필요로하는 모든 CodeQL 보안 쿼리에 사용할 수 있습니다. 웹 응용 프로그램의 헤더는 매우 중요 할 수 있으므로 프레임 워크에서 작성할 수있는 모든 방법을 모델링하는 것은 해당 웹 프레임 워크를 CodeQL에 추가하는 첫 번째 단계가 될 수 있습니다.

CodeQL의 모델링 프레임 워크

보안 코드를 보여주는 두 개의 창이 열린 컴퓨터.

CORS 헤더를 수동으로 설정하는 대신 많은 개발자가 대신 CORS 프레임 워크를 사용합니다. 일반적으로 CORS 프레임 워크는 모든 응답에 대한 헤더를 추가하기 위해 웹 프레임 워크 라우터에 미들웨어를 사용합니다. 일부 웹 프레임 워크에는 자체 Cors Middleware가 있거나 타사 패키지를 포함해야 할 수도 있습니다. CodeQL에서 CORS 프레임 워크를 모델링 할 때 일반적으로 CORS 정책을 나타내는 관련 구조 및 방법을 모델링합니다. 모델링 된 구조 또는 메소드에 올바른 값이 있으면 쿼리는 구조가 실제로 코드베이스에 사용되는지 확인해야합니다.

프레임 워크의 경우, 우리는 CORS를 크게 지원하기 때문에 선택의 언어로 Go를 살펴볼 것입니다. Go는 몇 가지 CORS 프레임 워크를 제공하지만 대부분 Gin 웹 프레임 워크를위한 CORS 미들웨어 프레임 워크 인 Gin Cors의 구조를 따릅니다. 다음은 CORS에 대한 GIN 구성의 예입니다.

package main

import (
  "time"

  "github.com/gin-contrib/cors"
  "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default()
  router.Use(cors.New(cors.Config{
    AllowOrigins:     []string{"
    AllowMethods:     []string{"PUT", "PATCH"},
    AllowHeaders:     []string{"Origin"},
    ExposeHeaders:    []string{"Content-Length"},
    AllowCredentials: true,
    AllowOriginFunc: func(origin string) bool {
      return origin == "
    }
  }))
  router.Run()
}

이제 우리는 모델을 모델링했습니다 router.Use 방법 및 cors.New – 그것을 확인하십시오 cors.Config 구조는 어느 시점에 a router.Use 실제 사용을위한 기능 – 그런 다음 모든 것을 확인해야합니다. cors.Config 적절한 헤더를위한 구조.

다음으로, 우리는 모델링하려는 적절한 헤더 필드를 찾습니다. 기본 CORS의 오해 쿼리의 경우 모델을 모델링 할 것입니다 AllowOrigins,,, AllowCredentials,,, AllowOriginFunc. CodeQL에 프레임 워크를 추가하는 데 관심이있는 모든 것을 보는 데 관심이있는 경우 CodeQL에 Gincors 및 RSCOR를 추가하라는 요청은 CodeQL에 참조로 사용할 수 있습니다. 아래에서 가장 중요한 세부 사항에 대해 논의하겠습니다.

 /**
   * A variable of type Config that holds the headers to be set.
   */
  class GinConfig extends Variable {
    SsaWithFields v;

    GinConfig() {
      this = v.getBaseVariable().getSourceVariable() and
      v.getType().hasQualifiedName(packagePath(), "Config")
    }

    /**
     * Get variable declaration of GinConfig
     */
    SsaWithFields getV() { result = v }
  }

필드가있는 단일 정적 할당 인 SsawithFields를 사용하여 구성 유형을 모델링했습니다. 사용하여 getSourceVariable()구조가 할당 된 변수를 얻을 수 있으며, 이는 구성이 사용되는 위치를 확인하는 데 도움이 될 수 있습니다. 이를 통해 코드베이스의 CORS 구성 구조가 포함 된 트랙 변수를 찾을 수 있습니다.

func main() {
...
// We can now track the corsConfig variable for further updates,such as when one of the fields is updated.
corsConfig:= cors.New(cors.Config{
...
})}

관련 구조가 포함 된 변수가 있으므로 변수가 기록 된 모든 인스턴스를 찾고자합니다. 이렇게하면 할당 된 관련 속성 값을 이해하여 CORS 구성이 잘못 구성되어 있는지 여부를 결정할 수 있습니다.

 /**
   * A write to the value of Access-Control-Allow-Origins header
   */
  class AllowOriginsWrite extends UniversalOriginWrite {
    DataFlow::Node base;
	
	// This models all writes to the AllowOrigins field of the Config type
    AllowOriginsWrite() {

      exists(Field f, Write w |
        f.hasQualifiedName(packagePath(), "Config", "AllowOrigins") and
        w.writesField(base, f, this) and

		// To ensure we are finding the correct field, we look for a write of type string (SliceLit)
        this.asExpr() instanceof SliceLit
      )

    }

    /**
     * Get config variable holding header values
     */
    override GinConfig getConfig() {
      exists(GinConfig gc |
        (
          gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
            base.asInstruction() or
          gc.getV().getAUse() = base
        ) and
        result = gc
      )
    }
  }

추가함으로써 getConfig 함수, 우리는 이전에 생성 된 것을 반환합니다 GinConfig이를 통해 관련 헤더에 대한 쓰기가 동일한 구성 구조에 영향을 미치는지 확인할 수 있습니다. 예를 들어, 개발자는 취약한 원점이있는 구성과 자격 증명을 허용하는 다른 구성을 만들 수 있습니다. 취약한 원점을 가진 구성 만 보안 문제를 생성하기 때문에 자격 증명을 허용하는 구성은 강조 표시되지 않습니다. CORS 관련 헤더가 다른 프레임 워크에서 모든 확장을하도록 허용함으로써 UniversalOriginWrite 그리고 UniversalCredentialsWrite우리는 Cors Misconfiguration 쿼리의 것들을 사용할 수 있습니다.

CODEQL에 CORS 오해 쿼리를 작성합니다

CORS 문제는 자격 증명이없는 두 가지 유형으로 분리되어 있습니다 (우리가 찾고있는 곳 또는 null)과 자격 증명이있는 Cors (Origin Reflection 또는 Null을 찾고있는 곳). CodeQL 쿼리를 간단하게 유지하려면 각 유형의 CORS 취약점에 대해 하나의 쿼리를 만들고 그에 따라 심각도를 할당 할 수 있습니다. GO 언어의 경우 CodeQL에는 모든 응용 프로그램에 적용 할 수 있으므로 “자격 증명이있는 COR”유형의 쿼리 만 있습니다.

Go Cors의 오해 쿼리 자체에서 어떻게 사용되는지 확인하기 위해 위에서 만든 모델에 묶어 봅시다.

from DataFlow::ExprNode allowOriginHW, string message
where
  allowCredentialsIsSetToTrue(allowOriginHW) and
  (
    flowsFromUntrustedToAllowOrigin(allowOriginHW, message)
    or
    allowOriginIsNull(allowOriginHW, message)
  ) and
  not flowsToGuardedByCheckOnUntrusted(allowOriginHW)
...
select allowOriginHW, message

이 쿼리는 중요한 취약점에만 관심이 있으므로 자격 증명이 허용되는지 여부와 허용 된 기원이 원격 소스에서 나오는지 또는 하드 코딩되어 있는지 확인합니다. 오 탐지를 방지하기 위해 원격 소스가 원점에 도달하기 전에 스트링 비교와 같은 특정 경비원이 있는지 확인합니다. 술어를 자세히 살펴 보겠습니다 allowCredentialsIsSetToTrue.

/**
 * Holds if the provided `allowOriginHW` HeaderWrite's parent ResponseWriter
 * also has another HeaderWrite that sets a `Access-Control-Allow-Credentials`
 * header to `true`.
 */
predicate allowCredentialsIsSetToTrue(DataFlow::ExprNode allowOriginHW) {
  exists(AllowCredentialsHeaderWrite allowCredentialsHW |
    allowCredentialsHW.getHeaderValue().toLowerCase() = "true"
  |
    allowOriginHW.(AllowOriginHeaderWrite).getResponseWriter() =
      allowCredentialsHW.getResponseWriter()
  )
  or
...

술어의 첫 번째 부분에서는 헤더를 비교하기 위해 이전에 모델링 한 헤더 중 하나를 사용할 수 있습니다. 이를 통해 자격 증명이없는 모든 헤더 쓰기를 필터링하는 데 도움이됩니다.

  exists(UniversalAllowCredentialsWrite allowCredentialsGin |
    allowCredentialsGin.getExpr().getBoolValue() = true
  |
    allowCredentialsGin.getConfig() = allowOriginHW.(UniversalOriginWrite).getConfig() and
    not exists(UniversalAllowAllOriginsWrite allowAllOrigins |
      allowAllOrigins.getExpr().getBoolValue() = true and
      allowCredentialsGin.getConfig() = allowAllOrigins.getConfig()
    )
    or
    allowCredentialsGin.getBase() = allowOriginHW.(UniversalOriginWrite).getBase() and
    not exists(UniversalAllowAllOriginsWrite allowAllOrigins |
      allowAllOrigins.getExpr().getBoolValue() = true and
      allowCredentialsGin.getBase() = allowAllOrigins.getBase()
    )
  )
}

CORS가 헤더를 통해 설정되지 않으면 CORS 프레임 워크를 사용하는지 확인합니다. UniversalAllowCredentialsWrite. 해당 원점 값이 “*”로 설정된 모든 인스턴스를 필터링하기 위해 not CodeQL 키워드 켜짐 UniversalAllowAllOriginsWrite이들은이 취약점에 적용 할 수 없기 때문에. flowsFromUntrustedToAllowOrigin 그리고 allowOriginIsNull 유사한 논리를 따라 결과 헤더 권한이 취약한 지 확인하십시오.

CODEQL 쿼리를 모델링하여 CORS와 관련된 취약점을 감지하면 한 가지 크기의 접근 방식을 사용할 수 없습니다. 대신 두 가지 이유로 각 웹 프레임 워크에 쿼리를 조정해야합니다.

  • 각 프레임 워크는 고유 한 방식으로 Cors 정책을 구현합니다
  • 취약성 패턴은 프레임 워크의 동작에 달려 있습니다

예를 들어, 우리는 Gin Cors에서 전에 AllowOriginFunc. 문서를 살펴 보거나 코드를 실험 한 후, 우리는 그것이 무시할 수 있음을 알 수 있습니다. AllowOrigins. 쿼리를 개선하기 위해 찾는 CodeQL 쿼리를 작성할 수 있습니다. AllowOriginFuncs는 항상 true를 반환하여 자격 증명과 쌍을 이루면 심각도 취약성이 높아집니다.

이것을 당신과 함께 가져 가십시오

CodeQL을 사용하여 웹 프레임 워크 및 헤더의 동작을 이해하면 코드에서 보안 문제를 찾고 취약점이 작업에 들어갈 가능성을 줄이는 것은 간단합니다. CORS의 오해 쿼리를 지원하는 CodeQL 언어의 수는 여전히 증가하고 있으며 항상 커뮤니티의 개선의 여지가 있습니다.

이 블로그가 CodeQL 쿼리 작성에 도움이된다면 CodeQL 커뮤니티 팩에서 커뮤니티와 공유하고 싶은 것을 자유롭게 열어주십시오.

마지막으로, Github Code Security는 CORS Misconfiguration과 같은 버그에 대한 수정을 감지하고 제안함으로써 프로젝트를 확보하는 데 도움이 될 수 있습니다!

더 많이 탐색하십시오 Github 보안 실험실 블로그 게시물>

작성자가 작성했습니다

케빈을 쓰러 뜨 렸습니다

출처 참조

Post Comment

당신은 놓쳤을 수도 있습니다