Browse Source

Added auto sniffer for self signed certificate

Toby Chui 3 months ago
parent
commit
b18b9bd8a5
5 changed files with 117 additions and 53 deletions
  1. 2 1
      api.go
  2. 0 2
      mod/auth/auth.go
  3. 100 10
      mod/dynamicproxy/domainsniff/domainsniff.go
  4. 15 5
      web/components/rules.html
  5. 0 35
      wrappers.go

+ 2 - 1
api.go

@@ -8,6 +8,7 @@ import (
 	"imuslab.com/zoraxy/mod/acme/acmedns"
 	"imuslab.com/zoraxy/mod/acme/acmewizard"
 	"imuslab.com/zoraxy/mod/auth"
+	"imuslab.com/zoraxy/mod/dynamicproxy/domainsniff"
 	"imuslab.com/zoraxy/mod/ipscan"
 	"imuslab.com/zoraxy/mod/netstat"
 	"imuslab.com/zoraxy/mod/netutils"
@@ -33,7 +34,7 @@ func RegisterHTTPProxyAPIs(authRouter *auth.RouterDef) {
 	authRouter.HandleFunc("/api/proxy/setAlias", ReverseProxyHandleAlias)
 	authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
 	authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
-	authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
+	authRouter.HandleFunc("/api/proxy/tlscheck", domainsniff.HandleCheckSiteSupportTLS)
 	authRouter.HandleFunc("/api/proxy/setIncoming", HandleIncomingPortSet)
 	authRouter.HandleFunc("/api/proxy/useHttpsRedirect", HandleUpdateHttpsRedirect)
 	authRouter.HandleFunc("/api/proxy/listenPort80", HandleUpdatePort80Listener)

+ 0 - 2
mod/auth/auth.go

@@ -9,7 +9,6 @@ import (
 	"crypto/rand"
 	"crypto/sha512"
 	"errors"
-	"fmt"
 	"net/http"
 	"net/mail"
 	"strings"
@@ -341,7 +340,6 @@ func (a *AuthAgent) CheckAuth(r *http.Request) bool {
 		return false
 	}
 
-	fmt.Println(r.RequestURI, session.Values)
 	// Check if user is authenticated
 	if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
 		return false

+ 100 - 10
mod/dynamicproxy/domainsniff/domainsniff.go

@@ -10,8 +10,14 @@ package domainsniff
 */
 import (
 	"crypto/tls"
+	"encoding/json"
+	"fmt"
 	"net"
+	"net/http"
+	"strings"
 	"time"
+
+	"imuslab.com/zoraxy/mod/utils"
 )
 
 // Check if the domain is reachable and return err if not reachable
@@ -27,30 +33,114 @@ func DomainReachableWithError(domain string) error {
 }
 
 // Check if a domain have TLS but it is self-signed or expired
-func DomainIsSelfSigned(domain string) (bool, error) {
+// Return false if sniff error
+func DomainIsSelfSigned(domain string) bool {
+	//Extract the domain from URl in case the user input the full URL
+	host, port, err := net.SplitHostPort(domain)
+	if err != nil {
+		host = domain
+	} else {
+		domain = host + ":" + port
+	}
+	if !strings.Contains(domain, ":") {
+		domain = domain + ":443"
+	}
+
 	//Get the certificate
 	conn, err := net.Dial("tcp", domain)
 	if err != nil {
-		return false, err
+		return false
 	}
 	defer conn.Close()
 
+	//Connect with TLS using secure verify
+	tlsConn := tls.Client(conn, nil)
+	err = tlsConn.Handshake()
+	if err == nil {
+		//This is a valid certificate
+		fmt.Println()
+		return false
+	}
+
 	//Connect with TLS using insecure skip verify
 	config := &tls.Config{
 		InsecureSkipVerify: true,
 	}
-	tlsConn := tls.Client(conn, config)
+	tlsConn = tls.Client(conn, config)
 	err = tlsConn.Handshake()
-	if err != nil {
-		return false, err
-	}
-
-	//Check if the certificate is self-signed
-	cert := tlsConn.ConnectionState().PeerCertificates[0]
-	return cert.Issuer.CommonName == cert.Subject.CommonName, nil
+	//If the handshake is successful, this is a self-signed certificate
+	return err == nil
 }
 
 // Check if domain reachable
 func DomainReachable(domain string) bool {
 	return DomainReachableWithError(domain) == nil
 }
+
+// Check if domain is served by a web server using HTTPS
+func DomainUsesTLS(targetURL string) bool {
+	//Check if the site support https
+	httpsUrl := fmt.Sprintf("https://%s", targetURL)
+	httpUrl := fmt.Sprintf("http://%s", targetURL)
+
+	client := http.Client{Timeout: 5 * time.Second}
+
+	resp, err := client.Head(httpsUrl)
+	if err == nil && resp.StatusCode == http.StatusOK {
+		return true
+	}
+
+	resp, err = client.Head(httpUrl)
+	if err == nil && resp.StatusCode == http.StatusOK {
+		return false
+	}
+
+	//If the site is not reachable, return false
+	return false
+}
+
+/*
+	Request Handlers
+*/
+//Check if site support TLS
+//Pass in ?selfsignchk=true to also check for self-signed certificate
+func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
+	targetURL, err := utils.PostPara(r, "url")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid url given")
+		return
+	}
+
+	//If the selfsign flag is set, also chec for self-signed certificate
+	_, err = utils.PostBool(r, "selfsignchk")
+	if err == nil {
+		//Return the https and selfsign status
+		type result struct {
+			Protocol string `json:"protocol"`
+			SelfSign bool   `json:"selfsign"`
+		}
+
+		scanResult := result{Protocol: "http", SelfSign: false}
+
+		if DomainUsesTLS(targetURL) {
+			scanResult.Protocol = "https"
+			if DomainIsSelfSigned(targetURL) {
+				scanResult.SelfSign = true
+			}
+		}
+
+		js, _ := json.Marshal(scanResult)
+		utils.SendJSONResponse(w, string(js))
+		return
+	}
+
+	if DomainUsesTLS(targetURL) {
+		js, _ := json.Marshal("https")
+		utils.SendJSONResponse(w, string(js))
+		return
+	} else {
+		js, _ := json.Marshal("http")
+		utils.SendJSONResponse(w, string(js))
+		return
+	}
+}

+ 15 - 5
web/components/rules.html

@@ -295,15 +295,25 @@
     //Automatic check if the site require TLS and check the checkbox if needed
     function autoCheckTls(targetDomain){
        $.cjax({
-            url: "/api/proxy/tlscheck",
+            url: "/api/proxy/tlscheck?selfsignchk=true",
             data: {url: targetDomain},
             success: function(data){
                 if (data.error != undefined){
                     msgbox(data.error, false);
-                }else if (data == "https"){
-                    $("#reqTls").parent().checkbox("set checked");
-                }else if (data == "http"){
-                    $("#reqTls").parent().checkbox("set unchecked");
+                }else{
+                    //Check if the site require TLS
+                    if (data.protocol == "https"){
+                        $("#reqTls").parent().checkbox("set checked");
+                    }else if (data.protocol == "http"){
+                        $("#reqTls").parent().checkbox("set unchecked");
+                    }
+                    //Check if the site is using self-signed cert
+                    if (data.selfsign){
+                        $("#skipTLSValidation").parent().checkbox("set checked");
+                    }else{
+                        $("#skipTLSValidation").parent().checkbox("set unchecked");
+                    }
+                    
                 }
             }
        })

+ 0 - 35
wrappers.go

@@ -18,11 +18,9 @@ package main
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"strconv"
 	"strings"
-	"time"
 
 	"imuslab.com/zoraxy/mod/dynamicproxy"
 	"imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
@@ -32,39 +30,6 @@ import (
 	"imuslab.com/zoraxy/mod/wakeonlan"
 )
 
-/*
-	Proxy Utils
-*/
-//Check if site support TLS
-func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
-	targetURL, err := utils.PostPara(r, "url")
-	if err != nil {
-		utils.SendErrorResponse(w, "invalid url given")
-		return
-	}
-
-	httpsUrl := fmt.Sprintf("https://%s", targetURL)
-	httpUrl := fmt.Sprintf("http://%s", targetURL)
-
-	client := http.Client{Timeout: 5 * time.Second}
-
-	resp, err := client.Head(httpsUrl)
-	if err == nil && resp.StatusCode == http.StatusOK {
-		js, _ := json.Marshal("https")
-		utils.SendJSONResponse(w, string(js))
-		return
-	}
-
-	resp, err = client.Head(httpUrl)
-	if err == nil && resp.StatusCode == http.StatusOK {
-		js, _ := json.Marshal("http")
-		utils.SendJSONResponse(w, string(js))
-		return
-	}
-
-	utils.SendErrorResponse(w, "invalid url given")
-}
-
 /*
 	Statistic Summary
 */