Insecure File Management
Why is this important?
Any functionality related to file management requires careful usage. If attackers are able to influence the input to file access related APIs, then it can have a serious impact. For example, attackers could read all files on your application server.
In other cases this can allow attackers to include their own code, or files, that are then executed by the application at runtime.
Check out this video for a high-level explanation on local file inclusion issues:
Fixing Insecure File Management
Option A: Use FilePath.Base() and Verify Extensions
- Go through the issues that GuardRails identified in the PR.
- A vulnerable example is shown below:
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
// The insecure user input is coming from the title parameter
title := r.URL.Query().Get("title")
// and used to directly open a file.
// By using ../../../ attackers can access any file in any folder
// such as the /etc/passwd file, or files containing sensitive data
f, err := os.Open(title)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
body := make([]byte, 5)
if _, err = f.Read(body); err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Fprintf(w, "%s", body)
})
log.Fatal(http.ListenAndServe(":3000", nil))
}
- Replace it with the following:
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
// Import https://golang.org/pkg/path/filepath/#Base
"path/filepath"
)
// Create function to check for valid extensions
func isValidExtension(ext string) bool {
switch ext {
case
".jpg",
".jpeg",
".png":
return true
}
return false
}
func main() {
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
// We are using filepath.Base to ensure the last element of the
// path is returned.
// https://golang.org/pkg/path/filepath/#Base
title := filepath.Base(r.URL.Query().Get("title"))
// You should also check for the correct file extension
// This is done with https://golang.org/pkg/path/filepath/#Ext
extension := strings.ToLower(filepath.Ext(title))
// Call the function to check for allowed extensions
// and only perform the read action the extensions is as expected
if isValidExtension(extension) {
f, err := os.Open(title)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
body := make([]byte, 5)
if _, err = f.Read(body); err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Fprintf(w, "%s", body)
}else{
log.Print("Not permitted file extension was encountered.")
}
})
log.Fatal(http.ListenAndServe(":3000", nil))
}
- Test it
- Ship it 🚢 and relax 🌴
Option B: Don't Store Files Locally
Another option is to store files in the cloud. This way local file inclusion attacks can be prevented. Some of the options are:
More information:
- Common Weakness Enumeration (CWE-73)
- Checkmarx - Go - Web Application Secure Coding Practices: File Management