Browse Source

Fixed csrf bug in cert upload

Toby Chui 7 months ago
parent
commit
8b82223ebe
6 changed files with 38 additions and 46 deletions
  1. 1 1
      main.go
  2. 13 6
      mod/acme/autorenew.go
  3. 8 31
      mod/tlscert/tlscert.go
  4. 1 1
      start.go
  5. 15 7
      web/components/cert.html
  6. BIN
      www/html/photo_2024-05-19_20-46-56 (3).jpg

+ 1 - 1
main.go

@@ -61,7 +61,7 @@ var (
 	name        = "Zoraxy"
 	version     = "3.1.0"
 	nodeUUID    = "generic" //System uuid, in uuidv4 format
-	development = false     //Set this to false to use embedded web fs
+	development = true      //Set this to false to use embedded web fs
 	bootTime    = time.Now().Unix()
 
 	/*

+ 13 - 6
mod/acme/autorenew.go

@@ -248,15 +248,19 @@ func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Reque
 }
 
 func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
-
-	email, err := utils.PostPara(r, "set")
-	if err != nil {
+	if r.Method == http.MethodGet {
 		//Return the current email to user
 		js, _ := json.Marshal(a.RenewerConfig.Email)
 		utils.SendJSONResponse(w, string(js))
-	} else {
+	} else if r.Method == http.MethodPost {
+		email, err := utils.PostPara(r, "set")
+		if err != nil {
+			utils.SendErrorResponse(w, "invalid or empty email given")
+			return
+		}
+
 		//Check if the email is valid
-		_, err := mail.ParseAddress(email)
+		_, err = mail.ParseAddress(email)
 		if err != nil {
 			utils.SendErrorResponse(w, err.Error())
 			return
@@ -265,8 +269,11 @@ func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
 		//Set the new config
 		a.RenewerConfig.Email = email
 		a.saveRenewConfigToFile()
-	}
 
+		utils.SendOK(w)
+	} else {
+		http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
+	}
 }
 
 // Check and renew certificates. This check all the certificates in the

+ 8 - 31
mod/tlscert/tlscert.go

@@ -11,6 +11,7 @@ import (
 	"path/filepath"
 	"strings"
 
+	"imuslab.com/zoraxy/mod/info/logger"
 	"imuslab.com/zoraxy/mod/utils"
 )
 
@@ -21,15 +22,16 @@ type CertCache struct {
 }
 
 type Manager struct {
-	CertStore   string       //Path where all the certs are stored
-	LoadedCerts []*CertCache //A list of loaded certs
+	CertStore   string         //Path where all the certs are stored
+	LoadedCerts []*CertCache   //A list of loaded certs
+	Logger      *logger.Logger //System wide logger for debug mesage
 	verbal      bool
 }
 
 //go:embed localhost.pem localhost.key
 var buildinCertStore embed.FS
 
-func NewManager(certStore string, verbal bool) (*Manager, error) {
+func NewManager(certStore string, verbal bool, logger *logger.Logger) (*Manager, error) {
 	if !utils.FileExists(certStore) {
 		os.MkdirAll(certStore, 0775)
 	}
@@ -52,6 +54,7 @@ func NewManager(certStore string, verbal bool) (*Manager, error) {
 		CertStore:   certStore,
 		LoadedCerts: []*CertCache{},
 		verbal:      verbal,
+		Logger:      logger,
 	}
 
 	err := thisManager.UpdateLoadedCertList()
@@ -78,7 +81,7 @@ func (m *Manager) UpdateLoadedCertList() error {
 		priKey := filepath.Join(m.CertStore, certname+".key")
 		certificate, err := tls.LoadX509KeyPair(pubKey, priKey)
 		if err != nil {
-			log.Println("Certificate loaded failed: " + certname)
+			m.Logger.PrintAndLog("tls-router", "Certificate load failed: "+certname, err)
 			continue
 		}
 
@@ -86,6 +89,7 @@ func (m *Manager) UpdateLoadedCertList() error {
 			loadedCert, err := x509.ParseCertificate(thisCert)
 			if err != nil {
 				//Error pasring cert, skip this byte segment
+				m.Logger.PrintAndLog("tls-router", "Certificate parse failed: "+certname, err)
 				continue
 			}
 
@@ -171,37 +175,10 @@ func (m *Manager) GetCert(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, err
 		pubKey, priKey = m.GetCertByX509CNHostname(helloInfo.ServerName)
 	} else {
 		//Fallback to legacy method of matching certificates
-		/*
-			domainCerts, _ := m.ListCertDomains()
-			cloestDomainCert := matchClosestDomainCertificate(helloInfo.ServerName, domainCerts)
-			if cloestDomainCert != "" {
-				//There is a matching parent domain for this subdomain. Use this instead.
-				pubKey = filepath.Join(m.CertStore, cloestDomainCert+".pem")
-				priKey = filepath.Join(m.CertStore, cloestDomainCert+".key")
-			} else if m.DefaultCertExists() {
-				//Use default.pem and default.key
-				pubKey = filepath.Join(m.CertStore, "default.pem")
-				priKey = filepath.Join(m.CertStore, "default.key")
-				if m.verbal {
-					log.Println("No matching certificate found. Serving with default")
-				}
-			} else {
-				if m.verbal {
-					log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
-				}
-			}*/
-
 		if m.DefaultCertExists() {
 			//Use default.pem and default.key
 			pubKey = filepath.Join(m.CertStore, "default.pem")
 			priKey = filepath.Join(m.CertStore, "default.key")
-			//if m.verbal {
-			//	log.Println("No matching certificate found. Serving with default")
-			//}
-		} else {
-			//if m.verbal {
-			//	log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
-			//}
 		}
 	}
 

+ 1 - 1
start.go

@@ -84,7 +84,7 @@ func startupSequence() {
 	})
 
 	//Create a TLS certificate manager
-	tlsCertManager, err = tlscert.NewManager("./conf/certs", development)
+	tlsCertManager, err = tlscert.NewManager("./conf/certs", development, SystemWideLogger)
 	if err != nil {
 		panic(err)
 	}

+ 15 - 7
web/components/cert.html

@@ -423,6 +423,8 @@
         }
         if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') {
             const publicKeyForm = new FormData();
+            const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
+
             publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey');
 
             const privateKeyForm = new FormData();
@@ -430,6 +432,7 @@
 
             const publicKeyRequest = new XMLHttpRequest();
             publicKeyRequest.open('POST', '/api/cert/upload?ktype=pub&domain=' + domain);
+            publicKeyRequest.setRequestHeader('X-CSRF-Token', csrfToken);
             publicKeyRequest.onreadystatechange = function() {
             if (publicKeyRequest.readyState === XMLHttpRequest.DONE) {
                 if (publicKeyRequest.status !== 200) {
@@ -446,6 +449,7 @@
 
             const privateKeyRequest = new XMLHttpRequest();
             privateKeyRequest.open('POST', '/api/cert/upload?ktype=pri&domain=' + domain);
+            privateKeyRequest.setRequestHeader('X-CSRF-Token', csrfToken);
             privateKeyRequest.onreadystatechange = function() {
             if (privateKeyRequest.readyState === XMLHttpRequest.DONE) {
                 if (privateKeyRequest.status !== 200) {
@@ -466,15 +470,11 @@
     //ktype = {"pub" / "pri"}
     function handleFileSelect(event, ktype="pub") {
         const file = event.target.files[0];
-        //const fileNameInput = document.getElementById('selected-file-name');
         if (ktype == "pub"){
             uploadPendingPublicKey = file;
         }else if (ktype == "pri"){
             uploadPendingPrivateKey = file;
         }
-        
-        
-        //fileNameInput.value = file.name;
     }
 
     //Check if the default keypairs exists
@@ -497,14 +497,18 @@
         input.addEventListener('change', () => {
             // create form data object
             const formData = new FormData();
-            
+            const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
+
             // add selected file to form data
             formData.append('file', input.files[0]);
 
             // send form data to server
             fetch('/api/cert/upload?ktype=pri', {
                 method: 'POST',
-                body: formData
+                body: formData,
+                headers: {
+                    'X-CSRF-Token': csrfToken
+                }
             })
             .then(response => {
                 initDefaultKeypairCheck();
@@ -531,6 +535,7 @@
     function uploadPublicKey() {
         // create file input element
         const input = document.createElement('input');
+        const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
         input.type = 'file';
         
         // add change listener to file input
@@ -544,7 +549,10 @@
             // send form data to server
             fetch('/api/cert/upload?ktype=pub', {
                 method: 'POST',
-                body: formData
+                body: formData,
+                headers: {
+                    'X-CSRF-Token': csrfToken
+                }
             })
             .then(response => {
                 if (response.ok) {

BIN
www/html/photo_2024-05-19_20-46-56 (3).jpg