{"meta":{"title":"웹후크 제공 유효성 검사하기","intro":"웹훅 시크릿을 사용하여 웹훅 전달이 GitHub에서 전송된 것인지 확인할 수 있습니다.","product":"웹후크","breadcrumbs":[{"href":"/ko/webhooks","title":"웹후크"},{"href":"/ko/webhooks/using-webhooks","title":"웹후크 사용"},{"href":"/ko/webhooks/using-webhooks/validating-webhook-deliveries","title":"제공 유효성 검사"}],"documentType":"article"},"body":"# 웹후크 제공 유효성 검사하기\n\n웹훅 시크릿을 사용하여 웹훅 전달이 GitHub에서 전송된 것인지 확인할 수 있습니다.\n\n## 웹후크 제공 유효성 검사 알아보기\n\n페이로드를 수신하도록 서버를 구성하고 나면, 구성한 엔드포인트에 제공된 모든 페이로드를 수신 대기합니다. 서버에서 보낸 GitHub 웹후크 배달만 처리하고 배달이 변조되지 않았는지 확인하려면 배달을 더 처리하기 전에 웹후크 서명의 유효성을 검사해야 합니다. 이렇게 하면 GitHub에서 온 것이 아닌 전달 요청을 처리하느라 서버 시간이 낭비되는 것을 방지하고 중간자 공격도 예방하는 데 도움이 됩니다.\n\n이렇게 하려면 다음을 수행해야 합니다.\n\n1. 웹후크에 대한 비밀 토큰을 만듭니다.\n2. 서버에 토큰을 안전하게 저장합니다.\n3. 들어오는 웹훅 페이로드를 토큰과 대조하여 검증하고, 해당 페이로드가 GitHub에서 온 것이며 변조되지 않았는지 확인합니다.\n\n## 비밀 토큰 만들기\n\n비밀 토큰을 사용하여 새 웹후크를 만들거나 기존 웹후크에 비밀 토큰을 추가할 수 있습니다. 비밀 토큰을 만들 때는 엔트로피가 높은 임의의 텍스트 문자열을 선택해야 합니다.\n\n* *비밀 토큰을 사용하여 새 웹후크를 만들려면*[웹후크 만들기](/ko/webhooks/using-webhooks/creating-webhooks)을(를) 참조하세요.\n* *기존 웹후크에 비밀 토큰을 추가하려면* 웹후크의 설정을 편집합니다. “비밀”로 이동해 `secret` 키로 사용할 문자열을 입력합니다. 자세한 내용은 [웹후크 편집하기](/ko/webhooks/using-webhooks/editing-webhooks)을(를) 참조하세요.\n\n## 비밀 토큰 안전하게 저장하기\n\n비밀 토큰을 만든 후에는 서버에서 액세스할 수 있는 안전한 위치에 저장해야 합니다. 토큰을 애플리케이션에 하드 코딩하거나 리포지토리에 푸시하지 마세요. 코드에서 인증 자격 증명을 안전하게 사용하는 방법에 대한 자세한 내용은 [해당 API 자격 증명 보안 유지](/ko/rest/authentication/keeping-your-api-credentials-secure#use-authentication-credentials-securely-in-your-code)을(를) 참조하세요.\n\n## 웹후크 제공 유효성 검사하기\n\nGitHub 는 비밀 토큰을 사용하여 각 페이로드와 함께 사용자에게 전송되는 해시 서명을 만듭니다. 해시 서명은 각 제공에서 `X-Hub-Signature-256` 헤더 값으로 표시됩니다. 자세한 내용은 [웹후크 이벤트 및 페이로드](/ko/webhooks/webhook-events-and-payloads#delivery-headers)을(를) 참조하세요.\n\n웹후크 제공을 처리하는 코드에서 비밀 토큰을 사용하여 해시를 계산해야 합니다. 그런 다음, 보낸 해시 GitHub 를 계산한 예상 해시와 비교하고 일치하는지 확인합니다. 다양한 프로그래밍 언어로 해시의 유효성을 검사하는 방법의 예시는 [Examples예시](#examples)를 참조하세요.\n\n웹후크 페이로드의 유효성을 검사할 때는 다음과 같은 사항에 유의해야 합니다.\n\n* GitHub 는 HMAC 16진수 다이제스트를 사용하여 해시를 계산합니다.\n* 해시 서명은 언제나 텍스트 `sha256=`(으)로 시작합니다.\n* 해시 서명은 웹후크의 비밀 토큰 및 페이로드 콘텐츠를 사용하여 생성됩니다.\n* 언어 및 서버 구현에서 문자 인코딩을 지정하는 경우 페이로드를 UTF-8로 처리해야 합니다. 웹후크 페이로드는 유니코드 문자를 포함할 수 있습니다.\n* 일반 `==` 연산자를 사용하지 마세요. 대신, 정규 동격 연산자에 대한 특정 타이밍 공격을 완화하는 데 도움이 되는 \"일정한 시간\" 문자열 비교를 수행하는 [`secure_compare`](https://www.rubydoc.info/gems/rack/Rack%2FUtils:secure_compare) 또는 [`crypto.timingSafeEqual`](https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b) 같은 메서드나 JIT 최적화 언어의 정규 루프를 사용하는 것이 좋습니다.\n\n### 웹후크 페이로드 유효성 검사 테스트하기\n\n다음 `secret` 및 `payload` 값을 사용하여 올바르게 구현되었는지 확인할 수 있습니다.\n\n* `secret`: `It's a Secret to Everybody`\n* `payload`: `Hello, World!`\n\n올바르게 구현되었다면 생성하는 서명이 다음 서명 값과 일치해야 합니다.\n\n* 서명: `757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17` <!-- markdownlint-disable-line GHD034 -->\n* X-Hub-Signature-256: `sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17`\n\n### 예시\n\n선택한 프로그래밍 언어를 사용하여 코드에서 HMAC 검증을 구현할 수 있습니다. 다음은 구현이 다양한 프로그래밍 언어로 표시되는 방식을 보여 주는 몇 가지 예입니다.\n\n#### Ruby 예제\n\n예를 들어 다음의 `verify_signature` 함수를 정의할 수 있습니다.\n\n```ruby\ndef verify_signature(payload_body)\n  signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)\n  return halt 500, \"Signatures didn't match!\" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256'])\nend\n```\n\n그런 다음 웹후크 페이로드를 받을 때 호출할 수 있습니다.\n\n```ruby\npost '/payload' do\n  request.body.rewind\n  payload_body = request.body.read\n  verify_signature(payload_body)\n  push = JSON.parse(payload_body)\n  \"I got some JSON: #{push.inspect}\"\nend\n```\n\n#### Python 예제\n\n예를 들어 다음의 `verify_signature` 함수를 정의하고 웹후크 페이로드를 받을 때 호출할 수 있습니다.\n\n```python\nimport hashlib\nimport hmac\ndef verify_signature(payload_body, secret_token, signature_header):\n    \"\"\"Verify that the payload was sent from GitHub by validating SHA256.\n\n    Raise and return 403 if not authorized.\n\n    Args:\n        payload_body: original request body to verify (request.body())\n        secret_token: GitHub app webhook token (WEBHOOK_SECRET)\n        signature_header: header received from GitHub (x-hub-signature-256)\n    \"\"\"\n    if not signature_header:\n        raise HTTPException(status_code=403, detail=\"x-hub-signature-256 header is missing!\")\n    hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)\n    expected_signature = \"sha256=\" + hash_object.hexdigest()\n    if not hmac.compare_digest(expected_signature, signature_header):\n        raise HTTPException(status_code=403, detail=\"Request signatures didn't match!\")\n```\n\n#### JavaScript 예제\n\n예를 들어 다음의 `verifySignature` 함수를 정의하고 웹후크 페이로드를 받을 때 모든 JavaScript 환경에서 호출할 수 있습니다.\n\n```javascript\nlet encoder = new TextEncoder();\n\nasync function verifySignature(secret, header, payload) {\n    let parts = header.split(\"=\");\n    let sigHex = parts[1];\n\n    let algorithm = { name: \"HMAC\", hash: { name: 'SHA-256' } };\n\n    let keyBytes = encoder.encode(secret);\n    let extractable = false;\n    let key = await crypto.subtle.importKey(\n        \"raw\",\n        keyBytes,\n        algorithm,\n        extractable,\n        [ \"sign\", \"verify\" ],\n    );\n\n    let sigBytes = hexToBytes(sigHex);\n    let dataBytes = encoder.encode(payload);\n    let equal = await crypto.subtle.verify(\n        algorithm.name,\n        key,\n        sigBytes,\n        dataBytes,\n    );\n\n    return equal;\n}\n\nfunction hexToBytes(hex) {\n    let len = hex.length / 2;\n    let bytes = new Uint8Array(len);\n\n    let index = 0;\n    for (let i = 0; i < hex.length; i += 2) {\n        let c = hex.slice(i, i + 2);\n        let b = parseInt(c, 16);\n        bytes[index] = b;\n        index += 1;\n    }\n\n    return bytes;\n}\n```\n\n#### TypeScript 예제\n\n예를 들어 다음의 `verify_signature` 함수를 정의하고 웹후크 페이로드를 받을 때 호출할 수 있습니다.\n\n```javascript copy\nimport { Webhooks } from \"@octokit/webhooks\";\n\nconst webhooks = new Webhooks({\n  secret: process.env.WEBHOOK_SECRET,\n});\n\nconst handleWebhook = async (req, res) => {\n  const signature = req.headers[\"x-hub-signature-256\"];\n  const body = await req.text();\n\n  if (!(await webhooks.verify(body, signature))) {\n    res.status(401).send(\"Unauthorized\");\n    return;\n  }\n\n  // The rest of your logic here\n};\n```\n\n## 문제 해결\n\n페이로드가 GitHub에서 온 것으로 확신하지만 서명 검증이 실패하는 경우:\n\n* 웹후크에 대한 비밀을 구성했는지 확인하세요. 웹후크에 대한 비밀을 구성하지 않은 경우 `X-Hub-Signature-256` 머리글이 표시되지 않습니다. 웹후크 비밀에 관한 자세한 내용은 [웹후크 편집하기](/ko/webhooks/using-webhooks/editing-webhooks)을(를) 참조하세요.\n* 올바른 머리글을 사용하고 있는지 확인하세요. GitHub에서는 HMAC-SHA256 알고리즘을 사용하는 `X-Hub-Signature-256` 머리글글을 사용하는 것이 좋습니다. `X-Hub-Signature` 머리글은 HMAC-SHA1 알고리즘을 사용하며 레거시 용도로만 포함됩니다.\n* 올바른 알고리즘을 사용하고 있는지 확인하세요. `X-Hub-Signature-256` 머리글을 사용하는 경우, HMAC-SHA256 알고리즘을 사용해야 합니다.\n* 올바른 웹후크 비밀을 사용하고 있는지 확인하세요. 웹후크 비밀 값을 모르는 경우, 웹후크 비밀을 업데이트할 수 있습니다. 자세한 내용은 [웹후크 편집하기](/ko/webhooks/using-webhooks/editing-webhooks)을(를) 참조하세요.\n* 검증 전에 페이로드 및 머리글이 수정되지 않았는지 확인하세요. 예를 들어 프록시 또는 부하 분산 장치를 사용하는 경우, 프록시 또는 부하 분산 장치가 페이로드 또는 머리글을 수정하지 않는지 확인합니다.\n* 언어 및 서버 구현에서 문자 인코딩을 지정하는 경우 페이로드를 UTF-8로 처리해야 합니다. 웹후크 페이로드는 유니코드 문자를 포함할 수 있습니다.\n\n## 추가 참고 자료\n\n* [웹후크 배달 처리](/ko/webhooks/using-webhooks/handling-webhook-deliveries)\n* [웹후크 사용에 대한 모범 사례](/ko/webhooks/using-webhooks/best-practices-for-using-webhooks)"}