Skip to content

Instantly share code, notes, and snippets.

@veverkap
Created July 5, 2018 19:50
Show Gist options
  • Save veverkap/676fa064bfb874ba41fbbab63a71f5b2 to your computer and use it in GitHub Desktop.
Save veverkap/676fa064bfb874ba41fbbab63a71f5b2 to your computer and use it in GitHub Desktop.
github_header.ex
defmodule VerifyGithubSignature do
@behaviour Plug.Parsers
import Plug.Conn
def init(opts) do
opts
end
def parse(conn, "application", subtype, _headers, opts) do
signature = List.keyfind(conn.req_headers, signature_header(), 0)
if json_request?(subtype) do
conn
|> read_body(opts)
|> verify_signature(signature)
|> decode()
else
{:next, conn}
end
end
defp signature_header,
do: Application.get_env(:forge, :github_signature_header, "x-hub-signature")
defp token, do: Application.get_env(:forge, :github_webhook_token)
defp json_request?(subtype), do: subtype == "json" || String.ends_with?(subtype, "+json")
defp verify_signature({:more, _, conn}, _sig) do
{:error, :too_large, conn}
end
defp verify_signature({:ok, "", conn}, _sig) do
{:ok, %{}, conn}
end
defp verify_signature({:ok, body, conn}, {_, signature}) do
if request_valid?(signature, body) do
{:ok, body, put_private(conn, :hmac_verified, true)}
else
{:error, "", halt(conn)}
end
end
defp decode({:ok, body, conn}) do
case Poison.decode!(body) do
terms when is_map(terms) ->
{:ok, terms, conn}
terms ->
{:ok, %{"_json" => terms}, conn}
end
rescue
e -> raise Plug.Parsers.ParseError, exception: e
end
defp decode(_conn), do: raise(InvalidSignatureError)
defp request_valid?(signature, body) do
computed = "sha1=#{signature_for(body)}"
Plug.Crypto.secure_compare(String.downcase(computed), String.downcase(signature))
end
# generate SHA1 HMAC signature for given string
defp signature_for(text) do
IO.inspect(token)
:crypto.hmac(:sha, token(), text) |> Base.encode16()
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment