Hello everyone, today brings you a detailed introduction of the “Tencent Open Source Go language code security Guide” collected by golangRoadmap.com Go Treasure special area

Reading: www.golangroadmap.com/books/secgu…

Gopher in the daily development process, will involve a lot of data processing and the corresponding code logic, if not careful, may lead to bugs, or even maliciously used loopholes. Such as memory overflow, out-of-bounds subscript, null pointer exception, and so on.

For an experienced developer, will be at the time of writing your own code, will deal with these problems, the subconscious mind is that senior development valuable experience, so after their code online, rarely appear problem, for the development of more NB, they even have their own a set of code toolkit, all is the summary of their coding experience for many years, with the box. A junior developer, on the other hand, may not be able to handle it, and may not even think about it, so the value of senior development is well represented.

However, you can’t have all the senior developers on a team, and the same goes for the entire company and industry, so there are frameworks, tools, and specifications that allow us to build on the shoulders of our predecessors and write great code.

Tencent recently released a code security guide, which aims to sort out the risk points at the API level and provide detailed and feasible secure coding solutions.

Based on the DevSecOps philosophy, we wanted to explain secure coding solutions in a way that developers can easily understand and guide vulnerability avoidance at source.

Github.com/Tencent/sec…

Tencent released code security guidelines for 6 languages: C/C++, Go, Java, Node, JavaScript, Python. We mainly introduce the code security guidelines for Go language. The details are as follows:

Generic class

1. Code implementation classes

1.1 Input Verification

1.1.1 [Must] Verify data by type

  • All external input parameters should be usedvalidatorWhitelist verification includes but is not limited to data length, data range, data type, and data format. If the verification fails, reject the verification
// good
import (
	"fmt"
	"github.com/go-playground/validator/v10"
)

var validate *validator.Validate
validate = validator.New()
func validateVariable(a) {
	myEmail := "[email protected]"
	errs := validate.Var(myEmail, "required,email")
	iferrs ! =nil {
		fmt.Println(errs)
		return
        // Stop execution
	}
	// If the verification is successful, continue. }Copy the code
  • If the whitelist verification fails, use this parameterhtml.EscapeString,text/templateorbluemondayrightThe <, >, &, ","And other characters for filtering or encoding
  import(
  	"text/template"
  )
  
  // TestHTMLEscapeString Escaping HTML special characters
  func main(inputValue string) string{
  	escapedResult := template.HTMLEscapeString(inputValue)
  	return escapedResult
  }
Copy the code

1.2 SQL operations

1.2.1 [Required] SQL statements use precompiled and bound variables by default

  • usedatabase/sqlPerform SQL operations using ORM such as PREPARE, Query, or GORM
  import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
  )
  
  type Product struct {
    gorm.Model
    Code string
    Price uint}...var product Product
  db.First(&product, 1)
Copy the code
  • With parameterized queries, concatenation of SQL statements is prohibited, and validation is required for passed parameters used for order BY or table names
// bad
  import (
  	"database/sql"
  	"fmt"
  	"net/http"
  )
  
  func handler(db *sql.DB, req *http.Request) {
  	q := fmt.Sprintf("SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='%s' ORDER BY PRICE",
  		req.URL.Query()["category"])
  	db.Query(q)
  }

// good
func handlerGood(db *sql.DB, req *http.Request) {
    / / use? A placeholder
  	q := "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='? ' ORDER BY PRICE"
  	db.Query(q, req.URL.Query()["category"])}Copy the code

1.3 Network Request

1.3.1 [Required] Resource Request Filtering and verification

  • Get(url), http.post (URL, contentType, body), http.head (url), http.postform (url, data), http.do (req), If the variable value is externally controllable (i.e. dynamically obtained from parameters), strict security checks should be performed on the request target.

  • If the requested resource domain name belongs to a fixed range, for example, only A.qq.com and B.qq.com are allowed, it should be whitelisted. If whitelisting is not applicable, the recommended verification logical steps are:

    • Step 1. Only HTTP or HTTPS is allowed

    • Step 2. Parse the destination URL and get its HOST

    • 3. Parse HOST and convert the IP address pointed to by HOST to Long

    • Step 4 check whether the IP address is an internal IP address. The network segment is:

      // Take the private network defined by RFC as an example. If there are private network segments defined by RFC, they should also be added to the forbidden list. 10.0.0.0 / along / 12 192.168.0.0/16 127.0.0.0/8 AugustCopy the code
    • Step 5. Request the URL

    • Step 6. If there is a redirect, perform 1 after the redirect. Otherwise, bind the verified IP address and domain name and initiate a URL request

  • External entity references are not supported by the official library Encoding/XML, which can be used to avoid xxE vulnerabilities

  import (
  	"encoding/xml"
  	"fmt"
      "os"
  )
  
  func main(a) {
  	type Person struct {
  		XMLName   xml.Name `xml:"person"`
  		Id        int      `xml:"id,attr"`
  		UserName string   `xml:"name>first"`
  		Comment string `xml:",comment"`
  	}
  
  	v := &Person{Id: 13, UserName: "John"}
  	v.Comment = " Need more details. "
  
  	enc := xml.NewEncoder(os.Stdout)
  	enc.Indent(""."")
  	iferr := enc.Encode(v); err ! =nil {
  		fmt.Printf("error: %v\n", err)
  	}
  
  }
Copy the code

1.4 Server-side rendering

1.4.1 [Required] Template rendering filter validation

  • usetext/templateorhtml/templateWhen rendering templates, do not introduce external input parameters into templates, or only whitelist characters are allowed.
   // bad
    func handler(w http.ResponseWriter, r *http.Request) {
      r.ParseForm()
      x := r.Form.Get("name")
     
      var tmpl = ` 
       
      
First name:

`

+ x + ` </p></body></html>` t := template.New("main") t, _ = t.Parse(tmpl) t.Execute(w, "Hello")}// good import ( "fmt" "github.com/go-playground/validator/v10" ) var validate *validator.Validate validate = validator.New() func validateVariable(val) { errs := validate.Var(val, "gte=1,lte=100")// Limit must be a positive integer between 1 and 100 iferrs ! =nil { fmt.Println(errs) return False } return True } func handler(w http.ResponseWriter, r *http.Request) { r.ParseForm() x := r.Form.Get("name") if validateVariable(x): var tmpl = `
First name:

`

+ x + ` </p></body></html>` t := template.New("main") t, _ = t.Parse(tmpl) t.Execute(w, "Hello") else:... }Copy the code

1.5 Web cross-domain

1.5.1 [Required] Cross-domain Resource Sharing CORS limits request sources

  • Sensitive information may be leaked due to improper CORS request protection. Therefore, access-Control-allow-Origin must be strictly set to use the same Origin policy for protection.
 // good
  c := cors.New(cors.Options{
      AllowedOrigins: []string{"http://qq.com"."https://qq.com"},
      AllowCredentials: true,
      Debug: false,})// Introduce middleware
  handler = c.Handler(handler)
Copy the code

1.6 Response Output

1.6.1 [Must] Set the correct HTTP response packet type

  • The content-type of the response header must be consistent with the actual response Content. For example, if the API response data type is JSON, the response header is usedapplication/json; If it is XML, set it totext/xml.

1.6.2 [Required] Add a Security Response Header

  • Add response headers for all interfaces and pagesX-Content-Type-Options: nosniff.
  • Add response headers for all interfaces and pagesX-Frame-Options . Set the allowable range reasonably as required, including:DENY,SAMEORIGIN,ALLOW-FROM origin. Usage Reference:MDN document

1.6.3 [Required] External input concatenated to the HTTP response header needs to be filtered

  • Do not concatenate external controllable parameters into the HTTP response header. If necessary, filter them out\r,\nWait for a newline character, or reject external input that carries a newline character.

1.6.4 [Required] External input is spliced in front of the Response page for encoding processing

  • It is recommended for directly printing HTML pages or using templates to generate HTML pagestext/templateAutomatic encoding, or usehtml.EscapeStringortext/templaterightThe <, >, &, ","And other characters for encoding.
import(
	"html/template"
)        

func outtemplate(w http.ResponseWriter,r *http.Request) {
    param1 := r.URL.Query().Get("param1")
    tmpl := template.New("hello")
    tmpl, _ = tmpl.Parse(`{{define "T"}}{{.}}{{end}}`)
    tmpl.ExecuteTemplate(w, "T", param1)
}
Copy the code

1.7 Session Management

1.7.1 [Required] Maintain session information securely

  • Sessions should be regenerated when users log in and cleared after users log out.
import (
	"net/http"
	"github.com/gorilla/mux"
	"github.com/gorilla/handlers"
)

/ / create a cookie
func setToken(res http.ResponseWriter, req *http.Request) {
    expireToken := time.Now().Add(time.Minute * 30).Unix()
    expireCookie := time.Now().Add(time.Minute * 30)... cookie := http.Cookie{ Name:"Auth",
        Value: signedToken,
        Expires: expireCookie, // It expires
        HttpOnly: true,
        Path: "/",
        Domain: "127.0.0.1",
        Secure: true
    }

    http.SetCookie(res, &cookie)
    http.Redirect(res, req, "/profile".307)}/ / delete the cookie
func logout(res http.ResponseWriter, req *http.Request) {
    deleteCookie := http.Cookie{
        Name: "Auth",
        Value: "none",
        Expires: time.Now()
    }
    http.SetCookie(res, &deleteCookie)
    return
}

Copy the code

1.7.2 [Required] CSRF Protection

  • Interfaces that involve system-sensitive operations or that can read sensitive information should be verifiedRefererOr addcsrf_token.
// good
import (
    "net/http"
    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main(a) {
    r := mux.NewRouter()
    r.HandleFunc("/signup", ShowSignupForm)
    r.HandleFunc("/signup/post", SubmitSignupForm)
    // Use csrF_token authentication
    http.ListenAndServe(": 8000",
        csrf.Protect([]byte("32-byte-long-auth-key"))(r))
}

Copy the code

1.8 Access Control

1.8.1 [Required] Default Authentication

  • Unless resources are fully accessible to the public, the system implements identity authentication by default and whitelists interfaces or pages that do not require authentication.

  • Based on the confidentiality of resources and user roles, set different levels of permissions based on the principle of minimum permissions, for example, full public, login read, login write, specific user read, specific user write

  • Read and write data related to users must verify the identity and permission of the logon user to avoid unauthorized operations

    - the pseudo code
      select id from table where id=:id and userid=session.userid
    Copy the code
  • External services without an independent account system use QQ or wechat to log in. Internal services use the unified login service to log in. For other services that use an account and password to log in, a secondary authentication such as a verification code is required

1.9 Concurrent Protection

1.9.1 Direct calls to loop variables in closures are prohibited

  • Start the coroutine in the loop. When the index value of the loop is used in the coroutine, data competition will occur when multiple coroutines use the same variable, resulting in abnormal execution results.
// bad
func main(a) {
    runtime.GOMAXPROCS(runtime.NumCPU())
    var group sync.WaitGroup

    for i := 0; i < 5; i++ {
        group.Add(1)
        go func(a) {
            defer group.Done()
            fmt.Printf("%-2d", i) // The I printed here is not what is expected
        }()
    }
    group.Wait()
}

// good
func main(a) {
    runtime.GOMAXPROCS(runtime.NumCPU())
    var group sync.WaitGroup

    for i := 0; i < 5; i++ {
        group.Add(1)
        go func(j int) {
            defer func(a) {
                if r := recover(a); r ! =nil {
                    fmt.Println("Recovered in start()")
                }
                group.Done()
            }()
        fmt.Printf("%-2d", j) // Closures use local variables inside
        }(i)  // Pass loop variables explicitly to the coroutine
    }
    group.Wait()
}
Copy the code

1.9.2 [Required] Forbid Concurrent Map writing

  • Concurrent map writing may cause program crash and exit abnormally. Lock is recommended
// bad
func main(a) {
	m := make(map[int]int)
	// Concurrent read and write
	go func(a) {
		for {
			_ = m[1]}} ()go func(a) {
		for {
			m[2] = 1
		}
	}()
	select{}}Copy the code

1.9.3 [Must] Ensure concurrency security

Sensitive operations without concurrent security restrictions may cause data read and write exceptions and bypass service logic restrictions. This can be defended by synchronous locking or atomic manipulation.

Shared memory through synchronization lock

// good
var count int
func Count(lock *sync.Mutex) {
    lock.Lock()/ / write locks
    count++
    fmt.Println(count)
    lock.Unlock()Unlock() or RLock(); Unlock() or RUnlock();
}

func main(a) {
    lock := &sync.Mutex{}
    for i := 0; i < 10; i++ {
        go Count(lock) // The pointer is passed to prevent the lock inside the function from being inconsistent with the calling lock
    }
    for {
        lock.Lock()
        c := count
        lock.Unlock()
        runtime.Gosched()// Give the time slice to the coroutine
        if c > 10 {
            break}}}Copy the code
  • usesync/atomicPerform atomic operation
// good
import (
	"sync"
	"sync/atomic"
)

func main(a) {
	type Map map[string]string
	var m atomic.Value
	m.Store(make(Map))
	var mu sync.Mutex // used only by writers
	read := func(key string) (val string) {
		m1 := m.Load().(Map)
		return m1[key]
	}
	insert := func(key, val string) {
		mu.Lock() // Synchronize with potential writes
		defer mu.Unlock()
		m1 := m.Load().(Map) // Import struct current data
		m2 := make(Map)      // Create a new value
		for k, v := range m1 {
			m2[k] = v
		}
		m2[key] = val
		m.Store(m2)   // Replace the current object with new
	}
	_, _ = read, insert
}
Copy the code

The background class

1 Code implementation class

1.1 Memory Management

1.1.1 [Required] Slice length verification

  • When operating on slice, you must determine whether the length is valid to prevent panic
// bad: The length of data is not judged, resulting in index out of range
func decode(data [] byte) bool {
    if data[0] = ='F' && data[1] = ='U' && data[2] = ='Z' && data[3] = ='Z' && data[4] = ='E' && data[5] = ='R' {
        fmt.Println("Bad")
        return true
    }
  
    return false
}

// bad: slice bounds out of range
func foo(a) {
    var slice = []int{0.1.2.3.4.5.6}
    fmt.Println(slice[:10])}// good: Check whether the length is valid before using data
func decode(data [] byte) bool {
    if len(data) == 6 {
        if data[0] = ='F' && data[1] = ='U' && data[2] = ='Z' && data[3] = ='Z' && data[4] = ='E' && data[5] = ='R' {
            fmt.Println("Good")
            return true}}return false
}
Copy the code

1.1.2 [must] Nil pointer judgment

  • When we do pointer operations, we must determine if the pointer is nil to prevent panic, especially when we do Unmarshal structures
type Packet struct {
    PackeyType        uint8
    PackeyVersion     uint8
    Data              *Data
}

type Data struct {
    Stat    uint8
    Len 	uint8
    Buf 	[8]byte
}

func (p *Packet) UnmarshalBinary(b []byte) error {
    if len(b) < 2 {
       return io.EOF
    }
  
    p.PackeyType = b[0]
    p.PackeyVersion = b[1]
  
    // If the length is equal to 2, no new Data
    if len(b) > 2 {
        p.Data = new(Data)
        // Unmarshal(b[i:], p.Data)
    }
  
    return nil
}

// bad: not checked if pointer is nil
func main(a) {
    packet := new(Packet)
    data := make([]byte.2)
    iferr := packet.UnmarshalBinary(data); err ! =nil {
        fmt.Println("Failed to unmarshal packet")
        return
    }
    
    fmt.Printf("Stat: %v\n", packet.Data.Stat)
}

// good: checks if the Data pointer is not nil
func main(a) {
    
    packet := new(Packet)
    data := make([]byte.2)
    
    iferr := packet.UnmarshalBinary(data); err ! =nil {
        fmt.Println("Failed to unmarshal packet")
        return
    }
    
    if packet.Data == nil {
        return
    }
    
    fmt.Printf("Stat: %v\n", packet.Data.Stat)
}
Copy the code

1.1.3 [Required] Integer security

  • When performing numeric operations, you need to set length limits to prevent exceptions caused by external input operations:

    • Ensures that unsigned integer operations do not invert
    • Ensure that there is no overflow during signed integer operations
    • Ensure that integer conversions do not cause truncation errors
    • Ensures that integer conversions are free from sign errors
  • The following scenarios must be strictly length restricted:

    • As an array index
    • As the length or size of the object
    • As an array boundary (eg as a loop counter)
// bad: the length is not limited, resulting in integer overflow
func overflow(numControlByUser int32) {
    var numInt int32 = 0
    numInt = numControlByUser + 1
    // Integer overflow is caused by improper length restriction
    fmt.Printf("%d\n", numInt)
    // Using numInt may cause other errors
}

func main(a) {
    overflow(2147483647)}// good: 
func overflow(numControlByUser int32) {
    var numInt int32 = 0
    numInt = numControlByUser + 1
    if numInt < 0 {
        fmt.Println("integer overflow")
        return;
    } 
    fmt.Println("integer ok")}func main(a) {
    overflow(2147483647)}Copy the code

1.1.4 [required] make Allocate length verification

  • During make memory allocation, the external controllable length should be verified to prevent panic.
// bad
func parse(lenControlByUser int, data[] byte) {
    size := lenControlByUser
    // Determine the length of the incoming size to avoid panic
    buffer := make([]byte, size)
    copy(buffer, data)
}

// good
func parse(lenControlByUser int, data[] byte) ([]byte, error){
    size := lenControlByUser
    // Limit the size range of external controllable lengths
    if size > 64*1024*1024 {
        return nil, errors.New("value too large")
    }
    buffer := make([]byte, size)
    copy(buffer, data)
    return buffer, nil
}
Copy the code

1.1.5 [Must] Forbid SetFinalizer and pointer circular reference at the same time

  • Runtime.setfinalizer () is not executed from the time an object is selected by GC until memory is removed, even if the program terminates normally or an error occurs. Although the “circular reference” formed by Pointers can be correctly processed by GC, runtime.setFinalizer () cannot be called because Finalizer depends on an uncertain sequence. As a result, the target object cannot become reachable and the memory cannot be reclaimed.
// bad
func foo(a) {
    var a, b Data
    a.o = &b
    b.o = &a

    // Pointer loop reference, SetFinalizer() cannot be called properly
    runtime.SetFinalizer(&a, func(d *Data) {
        fmt.Printf("a %p final.\n", d)
    })
    runtime.SetFinalizer(&b, func(d *Data) {
        fmt.Printf("b %p final.\n", d)
    })
}

func main(a) {
    for {
        foo()
        time.Sleep(time.Millisecond)
    }
}
Copy the code

1.1.6 [Must] Prohibit repeated channel release

  • Repeat release generally exists in abnormal process judgment. If a malicious attacker constructs abnormal conditions to make the program repeat release channel, it will trigger runtime panic, resulting in DoS attack.
// bad
func foo(c chan int) {
    defer close(c)
    err := processBusiness()
    iferr ! =nil {
        c <- 0
        close(c) // Release channel again
        return
    }
    c <- 1
}

// good
func foo(c chan int) {
    defer close(c) // Use defer to close the channel
    err := processBusiness()
    iferr ! =nil {
        c <- 0
        return
    }
    c <- 1
}
Copy the code

1.1.7 [Must] Ensure that each coroutine can exit

  • When a coroutine is started, a push operation will be performed. If the system does not exit, and no exit conditions are set for the coroutine, it is equivalent to losing control of the coroutine, and the resources it occupies cannot be reclaimed, which may lead to memory leakage.
// bad: The coroutine has no exit condition set
func doWaiter(name string, second int) {
    for {
        time.Sleep(time.Duration(second) * time.Second)
        fmt.Println(name, " is ready!")}}Copy the code

1.1.8 Not to Use the unsafe package

  • Because the Unsafe package circumvents Golang’s memory-safe principles, the library is generally unsafe to use and can cause memory corruption, so avoid using the package. If an unsafe pointer is required, security checks must be performed.
// bad: Handles the original pointer through unsafe
func unsafePointer(a) {
    b := make([]byte.1)
    foo := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0+))uintptr(0xfffffffe)))
    fmt.Print(*foo + 1)}// [signal SIGSEGV: segmentation violation code=0x1 addr=0xc100068f55 pc=0x49142b]
Copy the code

1.1.9 [Recommendation] Do not use slice as a function entry parameter

  • Slice is a reference type that uses address passing as a function entry. Changes to slice also affect the original data
  // bad
  // Slice is passed as an entry parameter to the function
  func modify(array []int) {
      array[0] = 10 // Element changes to the input slice affect the original data
  }
  
  func main(a) {
      array := []int{1.2.3.4.5}
  
      modify(array)
      fmt.Println(array) // output: [10 2 3 4 5]
  }

  // good
  // Array as a function parameter instead of slice
  func modify(array [5]int) {
    array[0] = 10
  }

  func main(a) {
      // Pass in an array. Note the difference between array and slice
      array := [5]int{1.2.3.4.5}
  
      modify(array)
      fmt.Println(array)
  }
Copy the code

1.2 File Operations

1.2.1 [Required] Path traversal check

  • If the file name is not restricted, any file may be read or written, or even code may be executed.
// bad: reads any file
func handler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Query()["path"] [0]

	// Unfiltered file paths may result in arbitrary file reads
	data, _ := ioutil.ReadFile(path)
	w.Write(data)

	// For external file name variables, also need to verify the existence of.. / Specifies the name of the file traversed by the path
	data, _ = ioutil.ReadFile(filepath.Join("/home/user/", path))
	w.Write(data)
}

// bad: write to any file
func unzip(f string) {
	r, _ := zip.OpenReader(f)
	for _, f := range r.File {
		p, _ := filepath.Abs(f.Name)
		// The compressed file name is not verified, which may cause.. / etc path traversal, arbitrary file path write
		ioutil.WriteFile(p, []byte("present"), 0640)}}// good: check whether the compressed file name contains.. Path traverses characteristic characters to prevent arbitrary writing
func unzipGood(f string) bool {
	r, err := zip.OpenReader(f)
	iferr ! =nil {
		fmt.Println("read zip file fail")
		return false
	}
	for _, f := range r.File {
		p, _ := filepath.Abs(f.Name)
		if! strings.Contains(p,"..") {
			ioutil.WriteFile(p, []byte("present"), 0640)}}return true
}
Copy the code

1.2.2 [Required] File access permission

  • Set different levels of access permissions based on the sensitivity of creating files to prevent sensitive data from being read by users with any permissions. For example, set the file permission to:-rw-r-----
ioutil.WriteFile(p, []byte("present"), 0640)
Copy the code

1.3 System Ports

1.3.1 [Mandatory] Run the Check command

  • useexec.Command,exec.CommandContext,syscall.StartProcess,os.StartProcessIf the first parameter (path) directly takes the input value, use a whitelist to limit the range of commands that can be executedbash,cmd,shSuch as command;
  • useexec.Command,exec.CommandContextAnd so onbash,cmd,shSuch as create shell, -c after the argument (arg) concatenate external input, should filter \n $&; | ‘() “` potentially malicious character;
// bad
func foo(a) {
	userInputedVal := "&& echo 'hello'" // Assume that the variable value is passed in externally
	cmdName := "ping " + userInputedVal

	// The command injection character is not detected in the external input
	cmd := exec.Command("sh"."-c", cmdName)
	output, _ := cmd.CombinedOutput()
	fmt.Println(string(output))

	cmdName := "ls"
	// The external input is not expected
	cmd := exec.Command(cmdName)
	output, _ := cmd.CombinedOutput()
	fmt.Println(string(output))
}

// good
func checkIllegal(cmdName string) bool {
	if strings.Contains(cmdName, "&") || strings.Contains(cmdName, "|") || strings.Contains(cmdName, ";") ||
		strings.Contains(cmdName, "$") || strings.Contains(cmdName, "'") || strings.Contains(cmdName, "`") ||
		strings.Contains(cmdName, "(") || strings.Contains(cmdName, ")") || strings.Contains(cmdName, "\" ") {
		return true
	}
	return false
}

func main(a) {
	userInputedVal := "&& echo 'hello'"
	cmdName := "ping " + userInputedVal

	if checkIllegal(cmdName) { // Check whether the command passed to sh has special characters
		return // Direct return with special characters
	}

	cmd := exec.Command("sh"."-c", cmdName)
	output, _ := cmd.CombinedOutput()
	fmt.Println(string(output))
}
Copy the code

1.4 Communication Security

1.4.1 [Required] TLS is used for network communication

  • The plaintext communication protocol has been verified and has high security risks. Hijacking by middlemen may lead to many security risks. Therefore, you must use at least TLS to ensure communication security, for example, gRPC and Websocket use TLS1.3.
// good
func main(a) {
  http.HandleFunc("/".func (w http.ResponseWriter, req *http.Request) {
    w.Header().Add("Strict-Transport-Security"."max-age=63072000; includeSubDomains")
    w.Write([]byte("This is an example server.\n"))})// The server configures the certificate and private key
  log.Fatal(http.ListenAndServeTLS(": 443"."yourCert.pem"."yourKey.pem".nil))}Copy the code

1.4.2 [Recommended] Enable certificate verification for TLS

  • The TLS certificate must be valid and valid, and the domain name must be correct. Certificate verification must be enabled on the server in the production environment.
// bad
import (
	"crypto/tls"
	"net/http"
)

func doAuthReq(authReq *http.Request) *http.Response {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	res, _ := client.Do(authReq)
	return res
}

// good
import (
	"crypto/tls"
	"net/http"
)

func doAuthReq(authReq *http.Request) *http.Response {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
	}
	client := &http.Client{Transport: tr}
	res, _ := client.Do(authReq)
	return res
}
Copy the code

1.5 Sensitive data protection

1.5.1 [Required] Access to sensitive Information

  • Prohibit hard-coding sensitive information into programs, which may expose sensitive information to attackers and make code management and maintenance more difficult
  • Use the configuration central system to centrally host sensitive information such as keys

1.5.2 [Required] Sensitive data output

  • Output only the minimum data set necessary to avoid sensitive information leakage caused by exposing redundant fields
  • Passwords (including plaintext passwords and ciphertext passwords), keys, and other sensitive information cannot be saved in logs
  • For sensitive information that must be output, reasonable desensitization display must be carried out
// bad
func serve(a) {
	http.HandleFunc("/register".func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		user := r.Form.Get("user")
		pw := r.Form.Get("password")

		log.Printf("Registering new user %s with password %s.\n", user, pw)
	})
	http.ListenAndServe(": 80".nil)}// good
func serve1(a) {
	http.HandleFunc("/register".func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		user := r.Form.Get("user")
		pw := r.Form.Get("password")

		log.Printf("Registering new user %s.\n", user)

		// ...
		use(pw)
	})
	http.ListenAndServe(": 80".nil)}Copy the code
  • Avoid giving away sensitive information through GET methods, code comments, auto-fill, caching, and so on

1.5.3 [Required] Sensitive Data Storage

  • Sensitive data should be encrypted and stored using algorithms such as SHA2 and RSA
  • Sensitive data should be stored in a separate layer with access control enabled on the access layer
  • Temporary files or caches containing sensitive information should be deleted as soon as they are no longer needed

1.5.4 exception Handling and log recording

  • Panic, recover, and defer should be properly used to handle system exceptions to avoid error information being output to the front end
defer func (a) {
        if r := recover(a); r ! =nil {
            fmt.Println("Recovered in start()")}} ()Copy the code
  • Do not enable the debug mode or output program running logs to the front end

Examples of errors:

dlv --listen=:2345 --headless=true --api-version=2 debug test.go
Copy the code

Correct examples:

dlv debug test.go
Copy the code

1.6 Encryption and Decryption

1.6.1 [Must] Passwords/keys shall not be hard-coded

  • When performing operations such as user login and encryption and decryption algorithms, do not hardcode keys or passwords in codes. You can set passwords or keys by changing algorithms or configuring them.
// bad
const (
	user     = "dbuser"
	password = "s3cretp4ssword"
)

func connect(a) *sql.DB {
	connStr := fmt.Sprintf("postgres://%s:%s@localhost/pqgotest", user, password)
	db, err := sql.Open("postgres", connStr)
	iferr ! =nil {
		return nil
	}
	return db
}

// bad
var (
	commonkey = []byte("0123456789abcdef"))func AesEncrypt(plaintext string) (string, error) {
	block, err := aes.NewCipher(commonkey)
	iferr ! =nil {
		return "", err
	}
}
Copy the code

1.6.2 [Required] Secure Key Storage

  • When using symmetric cryptography, you need to protect the encryption key. When the algorithm involves sensitive or service data, an asymmetric algorithm can be used to negotiate encryption keys. For other less sensitive data encryption, the key can be protected through transformation algorithms.

1.6.3 [Recommendation] Do not use weak password algorithms

  • Do not use an encryption algorithm with weak encryption strength.

Examples of errors:

Crypto/DES, crypto/ MD5, crypto/ SHA1, crypto/ RC4, etc.Copy the code

1.7 Regular Expressions

1.7.1 [Recommended] Use regexp to match regular expressions

  • Improper regular expressions can be used in DoS attacks to make services unavailable. You are advised to use the regexp package to match regular expressions. Regexp guarantees linear time performance and graceful failure: memory limits are placed on the parser, compiler, and execution engine. However, regexp does not support the following regular expression features. If services depend on these features, regexp is not suitable for use.
    • Backreferences and Lookaround
// good
matched, err := regexp.MatchString(`a.b`."aaxbb")
fmt.Println(matched) // true
fmt.Println(err)     // nil (regexp is valid)
Copy the code

For more information, visit www.golangroadmap.com