Skip to content

Commit

Permalink
Do masking in show and not in writeheaders; Include other sensitive h…
Browse files Browse the repository at this point in the history
…eaders like Cookie and Set-Cookie
  • Loading branch information
nkottary committed Nov 24, 2023
1 parent 311dc12 commit 1dd214b
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 19 deletions.
37 changes: 21 additions & 16 deletions src/Messages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -485,30 +485,19 @@ function writestartline(io::IO, r::Response)
end

"""
writeheaders(::IO, ::Message; mask_authorization_header::Bool=false)
writeheaders(::IO, ::Message)
Write `Message` start line and
a line for each "name: value" pair and a trailing blank line.
When `mask_authorization_header` is set to `true`, the value of
Authorization header will not be written.
"""
writeheaders(io::IO, m::Message; mask_authorization_header::Bool=false) =
writeheaders(io, m, IOBuffer(); mask_authorization_header=mask_authorization_header)
writeheaders(io::IO, m::Message) = writeheaders(io, m, IOBuffer())
writeheaders(io::Connection, m::Message) = writeheaders(io, m, io.writebuffer)

function writeheaders(io::IO, m::Message, buf::IOBuffer; mask_authorization_header::Bool=false)
function writeheaders(io::IO, m::Message, buf::IOBuffer)
writestartline(buf, m)
for (name, value) in m.headers
# match curl convention of not writing empty headers
if !isempty(value)
if mask_authorization_header && name == "Authorization"
showvalue = "XXXXXXXXXX"
else
showvalue = value
end
write(buf, name, ": ", showvalue, "\r\n")
end
!isempty(value) && write(buf, name, ": ", value, "\r\n")
end
write(buf, "\r\n")
nwritten = write(io, take!(buf))
Expand Down Expand Up @@ -620,7 +609,23 @@ function Base.show(io::IO, m::Message)
end
println(io, typeof(m), ":")
println(io, "\"\"\"")
writeheaders(io, m; mask_authorization_header=true)

# Mask sensitive header values
# The following headers values contain sensitive information that
# we don't want to show
# - Authorization
# - Cookie
# - Set-Cookie (in response)
# We will show "**********" instead
ioh = IOBuffer()
writeheaders(ioh, m)
header_str = String(take!(ioh))
mask_headers = ["Authorization", "Cookie", "Set-Cookie"]
for mh in mask_headers
header_str = replace(header_str, Regex("($mh: ).*\$", "m") => s"\1******")
end
write(io, header_str)

summary = bodysummary(m.body)
validsummary = isvalidstr(summary)
validsummary && write(io, summary)
Expand Down
14 changes: 11 additions & 3 deletions test/server.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,18 @@ const echostreamhandler = HTTP.streamhandler(echohandler)
# test that an Authorization header **is** forwarded to redirect in same domain
@test HTTP.hasheader(HTTP.get("https://$httpbin/redirect-to?url=https://$httpbin/response-headers?Authorization=auth"), "Authorization")

# test that Authorization header value is masked when `show`ing HTTP.Request
# test that sensitive header values are masked when `show`ing HTTP.Request
io = IOBuffer()
HTTP.show(io, HTTP.Request("GET", "https://xyz.com", ["Authorization" => "Basic abcdef12345"]))
@test !occursin("abcdef12345", String(io.data))
HTTP.show(io, HTTP.Request("GET", "https://xyz.com", ["Authorization" => "Basic secretauth", "Cookie" => "secretcookie", "User-Agent" => "computer"]))
headers = String(take!(io))
@test !occursin("secretauth", headers)
@test !occursin("secretcookie", headers)
@test occursin("computer", headers)

io = IOBuffer()
HTTP.show(io, HTTP.Response(200, ["Set-Cookie" => "setcookie"]))
headers = String(take!(io))
@test !occursin("setcookie", headers)

close(t1)

Expand Down

0 comments on commit 1dd214b

Please sign in to comment.