Commit 82cbd7a9 authored by Matt Holt's avatar Matt Holt Committed by GitHub

Detect HTTPS interception (#1430)

* WIP: Implement HTTPS interception detection by Durumeric, et. al.

Special thanks to @FiloSottile for guidance with the custom listener.

* Add {{.IsMITM}} context action and {mitm} placeholder

* Improve MITM detection heuristics for Firefox and Edge

* Add tests for MITM detection heuristics

* Improve Safari heuristics for interception detection

* Read ClientHello during first Read() instead of during Accept()

As far as I can tell, reading the ClientHello during Accept() prevents
new connections from being accepted during the read. Since Read() should
be called in its own goroutine, this keeps Accept() non-blocking.

* Clean up MITM detection handler; make possible to close connection

* Use standard lib cipher suite values when possible

* Improve Edge heuristics and test cases

* Refactor MITM checking logic; add some debug statements for now

* Fix bug in MITM heuristic tests and actual heuristic code

* Fix gofmt

* Remove debug statements; preparing for merge
parent cdf7cf5c
...@@ -99,7 +99,9 @@ one collaborator who did not open the pull request before merging. This will ...@@ -99,7 +99,9 @@ one collaborator who did not open the pull request before merging. This will
help ensure high code quality as new collaborators are added to the project. help ensure high code quality as new collaborators are added to the project.
Read [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments) Read [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments)
on the Go wiki for an idea of what we look for in good, clean Go code. on the Go wiki for an idea of what we look for in good, clean Go code, and
check out [what Linus suggests](https://gist.github.com/matthewhudson/1475276)
for good commit messages.
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
// 1. Set the AppName and AppVersion variables. // 1. Set the AppName and AppVersion variables.
// 2. Call LoadCaddyfile() to get the Caddyfile. // 2. Call LoadCaddyfile() to get the Caddyfile.
// Pass in the name of the server type (like "http"). // Pass in the name of the server type (like "http").
// Make sure the server type's package is imported
// (import _ "github.com/mholt/caddy/caddyhttp").
// 3. Call caddy.Start() to start Caddy. You get back // 3. Call caddy.Start() to start Caddy. You get back
// an Instance, on which you can call Restart() to // an Instance, on which you can call Restart() to
// restart it or Stop() to stop it. // restart it or Stop() to stop it.
......
...@@ -321,3 +321,14 @@ func (c Context) Files(name string) ([]string, error) { ...@@ -321,3 +321,14 @@ func (c Context) Files(name string) ([]string, error) {
return names, nil return names, nil
} }
// IsMITM returns true if it seems likely that the TLS connection
// is being intercepted.
func (c Context) IsMITM() bool {
if val, ok := c.Req.Context().Value(CtxKey("mitm")).(bool); ok {
return val
}
return false
}
type CtxKey string
This diff is collapsed.
This diff is collapsed.
...@@ -298,6 +298,15 @@ func (r *replacer) getSubstitution(key string) string { ...@@ -298,6 +298,15 @@ func (r *replacer) getSubstitution(key string) string {
} }
} }
return requestReplacer.Replace(r.requestBody.String()) return requestReplacer.Replace(r.requestBody.String())
case "{mitm}":
if val, ok := r.request.Context().Value(CtxKey("mitm")).(bool); ok {
if val {
return "likely"
} else {
return "unlikely"
}
}
return "unknown"
case "{status}": case "{status}":
if r.responseRecorder == nil { if r.responseRecorder == nil {
return r.emptyValue return r.emptyValue
......
...@@ -47,6 +47,18 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) { ...@@ -47,6 +47,18 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) {
} }
s.Server.Handler = s // this is weird, but whatever s.Server.Handler = s // this is weird, but whatever
tlsh := &tlsHandler{next: s.Server.Handler}
s.Server.ConnState = func(c net.Conn, cs http.ConnState) {
// when a connection closes or is hijacked, delete its entry
// in the map, because we are done with it.
if tlsh.listener != nil {
if cs == http.StateHijacked || cs == http.StateClosed {
tlsh.listener.helloInfosMu.Lock()
delete(tlsh.listener.helloInfos, c.RemoteAddr().String())
tlsh.listener.helloInfosMu.Unlock()
}
}
}
// Disable HTTP/2 if desired // Disable HTTP/2 if desired
if !HTTP2 { if !HTTP2 {
...@@ -75,6 +87,10 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) { ...@@ -75,6 +87,10 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) {
s.Server.TLSConfig.NextProtos = []string{"h2"} s.Server.TLSConfig.NextProtos = []string{"h2"}
} }
if s.Server.TLSConfig != nil {
s.Server.Handler = tlsh
}
// Compile custom middleware for every site (enables virtual hosting) // Compile custom middleware for every site (enables virtual hosting)
for _, site := range group { for _, site := range group {
stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles}) stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles})
...@@ -156,7 +172,10 @@ func (s *Server) Serve(ln net.Listener) error { ...@@ -156,7 +172,10 @@ func (s *Server) Serve(ln net.Listener) error {
// not implement the File() method we need for graceful restarts // not implement the File() method we need for graceful restarts
// on POSIX systems. // on POSIX systems.
// TODO: Is this ^ still relevant anymore? Maybe we can now that it's a net.Listener... // TODO: Is this ^ still relevant anymore? Maybe we can now that it's a net.Listener...
ln = tls.NewListener(ln, s.Server.TLSConfig) ln = newTLSListener(ln, s.Server.TLSConfig, s.Server.ReadTimeout)
if handler, ok := s.Server.Handler.(*tlsHandler); ok {
handler.listener = ln.(*tlsHelloListener)
}
// Rotate TLS session ticket keys // Rotate TLS session ticket keys
s.tlsGovChan = caddytls.RotateSessionTicketKeys(s.Server.TLSConfig) s.tlsGovChan = caddytls.RotateSessionTicketKeys(s.Server.TLSConfig)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment