페이로드를 수신하도록 서버를 구성하고 나면, 구성한 엔드포인트에 전송된 모든 페이로드를 수신 대기합니다. 보안상의 이유로 GitHub에서 나오는 요청으로 요청을 제한하고자 할 수 있습니다. 이에 대해 이동하는 몇 가지 방법이 있지만(예: GitHub의 IP 주소에서 요청을 허용하도록 선택할 수 있음) 훨씬 더 쉬운 방법은 비밀 토큰을 설정하고 정보의 유효성을 검사하는 것입니다.
REST API를 사용하여 리포지토리, 조직 및 앱 웹후크를 관리할 수 있습니다. 웹후크에 대한 웹후크 배달을 나열하거나 외부 앱 또는 서비스에 통합할 수 있는 웹후크에 대한 개별 배달을 가져와서 다시 배달할 수 있습니다. REST API를 사용하여 웹후크의 구성을 변경할 수도 있습니다. 예를 들어 페이로드 URL, 콘텐츠 형식, SSL 확인 및 비밀을 수정할 수 있습니다. 자세한 내용은 다음을 참조하세요.
비밀 토큰 설정
GitHub 및 서버, 이렇게 두 위치에서 비밀 토큰을 설정해야 합니다.
GitHub에서 토큰을 설정하려면 다음을 수행합니다.
-
웹후크를 설정하는 리포지토리로 이동합니다.
-
리포지토리 이름 아래에서 설정을 클릭합니다. "설정" 탭이 표시되지 않으면 드롭다운 메뉴를 선택한 다음 설정을 클릭합니다.
-
왼쪽 사이드바에서 웹후크를 클릭합니다.
-
웹후크 옆에 있는 편집을 클릭합니다.
-
"비밀" 필드에 엔트로피가 높은 임의의 문자열을 입력합니다. 예를 들어 터미널에서 를 사용하여 문자열
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
을 생성할 수 있습니다. -
웹후크 업데이트를 클릭합니다.
다음으로, 이 토큰을 저장하는 서버에서 환경 변수를 설정합니다. 일반적으로 다음을 실행하는 것만큼 간단합니다.
$ export SECRET_TOKEN=YOUR-TOKEN
토큰을 앱에 하드 코딩하지 마세요!
GitHub의 페이로드 유효성 검사
비밀 토큰이 설정되면 GitHub Enterprise Server에서는 이 토큰을 사용하여 각 페이로드가 포함된 해시 서명을 만듭니다. 이 해시 서명은 각 요청의 헤더와 함께 x-hub-signature-256
으로 포함됩니다.
참고: 이전 버전과의 호환성을 위해 SHA-1 해시 함수를 사용하여 생성된 x-hub-signature
헤더도 포함됩니다. 가능하면 보안 강화를 위해 x-hub-signature-256
헤더를 사용하는 것이 좋습니다. 아래 예제에서는 헤더를 사용하는 방법을 보여 줍니다 x-hub-signature-256
.
를 사용하여 SECRET_TOKEN
해시를 계산하고 결과가 GitHub Enterprise Server의 해시와 일치하는지 확인해야 합니다. GitHub Enterprise Server은(는) HMAC 16진수 다이제스트를 사용하여 해시를 계산합니다.
참고: 웹후크 페이로드는 유니코드 문자를 포함할 수 있습니다. 언어 및 서버 구현에서 문자 인코딩을 지정하는 경우 페이로드를 UTF-8로 처리해야 합니다.
언어 및 서버 구현은 다음 예제와 다를 수 있습니다. 그러나 다음과 같이 지적해야 할 매우 중요한 사항이 여러 가지 있습니다.
-
어떤 구현을 사용하든, 해시 서명은 비밀 토큰 및 페이로드 본문의 키를 사용하여
sha256=
으로 시작합니다. -
일반
==
연산자의 사용은 권장되지 않습니다.secure_compare
등과 같은 메서드는 “일정한 시간” 문자열 비교를 수행하는데, 이는 일반적인 같음 연산자에 대한 특정 타이밍 공격을 완화하는 데 도움이 됩니다.
Ruby 예제
예를 들어 다음 verify_signature
함수를 정의할 수 있습니다.
def verify_signature(payload_body)
signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)
return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256'])
end
그런 다음, 웹후크 페이로드를 받을 때 호출할 수 있습니다.
post '/payload' do
request.body.rewind
payload_body = request.body.read
verify_signature(payload_body)
push = JSON.parse(payload_body)
"I got some JSON: #{push.inspect}"
end
Python 예제
예를 들어 다음 verify_signature
함수를 정의하고 웹후크 페이로드를 받으면 호출할 수 있습니다.
import hashlib
import hmac
def verify_signature(payload_body, secret_token, signature_header):
"""Verify that the payload was sent from GitHub by validating SHA256.
Raise and return 403 if not authorized.
Args:
payload_body: original request body to verify (request.body())
secret_token: GitHub app webhook token (WEBHOOK_SECRET)
signature_header: header received from GitHub (x-hub-signature-256)
"""
if not signature_header:
raise HTTPException(status_code=403, detail="x-hub-signature-256 header is missing!")
hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
expected_signature = "sha256=" + hash_object.hexdigest()
if not hmac.compare_digest(expected_signature, signature_header):
raise HTTPException(status_code=403, detail="Request signatures didn't match!")
Typescript 예제
예를 들어 다음 verify_signature
함수를 정의하고 웹후크 페이로드를 받으면 호출할 수 있습니다.
import * as crypto from "crypto";
const WEBHOOK_SECRET: string = process.env.WEBHOOK_SECRET;
const verify_signature = (req: Request) => {
const signature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest("hex");
return `sha256=${signature}` === req.headers.get("x-hub-signature-256");
};
const handleWebhook = (req: Request, res: Response) => {
if (!verify_signature(req)) {
res.status(401).send("Unauthorized");
return;
}
// The rest of your logic here
};