From c46038c51d874551271409ae5fbd4db9fb0917a6 Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 10 Jun 2024 05:32:48 -0700 Subject: [PATCH] add examples about protecting internet-facing httpds * request body size limit * request timeout --- README.md | 145 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index d1fc409..78890c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # sneak/styleguide The following is the first released version of my personal code styleguide. +There are many like it, but this one is mine. Only the Go portion is "complete". The others are mostly just placeholders. @@ -115,10 +116,10 @@ Feedback and suggestions are not only welcome but explicitly encouraged. in a function called `main` and call `main()` at the end of the script using the standard invocation: - ```python - if __name__ == "__main__": - main() - ``` + ```python + if __name__ == "__main__": + main() + ``` ## Golang @@ -237,8 +238,9 @@ Feedback and suggestions are not only welcome but explicitly encouraged. repo root. 1. If you are writing a multi-module project, put all `.go` files in a - `pkg/` or `internal/` directory. This is to keep the root clean and to - make it easier to see what is a library and what is a binary. + `pkg/` or `internal/` subdirectory. `internal/` is for modules used + only by the current repo, and `pkg/` is for modules that can be consumed + externally. This is to keep the repo root as clean as possible. 1. Binaries go in `cmd/` directories. Each binary should have its own directory. This is to keep the root clean and to make it easier to see @@ -576,18 +578,73 @@ Feedback and suggestions are not only welcome but explicitly encouraged. ## Other Golang Tips and Best Practices (Optional) +1. For any internet-facing http server, set appropriate timeouts and limits + to protect against slowloris attacks or huge uploads that can consume + server resources even without authentication. + + Example to limit request body size: + + ```go + package main + + import ( + "fmt" + "net/http" + ) + + func main() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + // Limit the request body to 10MB + r.Body = http.MaxBytesReader(w, r.Body, 10<<20) + if err := r.ParseForm(); err != nil { + http.Error(w, "Request body too large", http.StatusRequestEntityTooLarge) + return + } + fmt.Fprintf(w, "Hello, World!") + }) + + http.ListenAndServe(":8080", nil) + } + ``` + + Example to set appropriate timeouts: + + ```go + package main + + import ( + "net/http" + "time" + ) + + func main() { + server := &http.Server{ + Addr: ":8080", + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + Handler: http.DefaultServeMux, + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, World!") + }) + + server.ListenAndServe() + } + ``` + 1. When passing channels to goroutines, use read-only (`<-chan`) or write-only (`chan<-`) channels to communicate the direction of data flow clearly. -2. Use `io.MultiReader` to concatenate multiple readers and +1. Use `io.MultiReader` to concatenate multiple readers and `io.MultiWriter` to duplicate writes to multiple writers. This can simplify the handling of multiple data sources or destinations. -3. For simple counters and flags, use the `sync/atomic` package to avoid +1. For simple counters and flags, use the `sync/atomic` package to avoid the overhead of mutexes. -4. When using mutexes, minimize the scope of locking to reduce contention +1. When using mutexes, minimize the scope of locking to reduce contention and potential deadlocks. Prefer to lock only the critical sections of code. Try to encapsulate the critical section in its own function or method. Acquire the lock as the first line of the function, defer @@ -596,63 +653,63 @@ Feedback and suggestions are not only welcome but explicitly encouraged. using mutexes in the middle of a function. In short, build atomic functions. -5. Design types to be immutable where possible. This can help avoid issues +1. Design types to be immutable where possible. This can help avoid issues with concurrent access and make the code easier to reason about. -6. Global state can lead to unpredictable behavior and makes the code +1. Global state can lead to unpredictable behavior and makes the code harder to test. Use dependency injection to manage state. -7. Avoid using `init` functions unless absolutely necessary as they can +1. Avoid using `init` functions unless absolutely necessary as they can lead to unpredictable initialization order and make the code harder to understand. -8. Provide comments for all public interfaces explaining what they do and +1. Provide comments for all public interfaces explaining what they do and how they should be used. This helps other developers understand the intended use. -9. Be mindful of resource leaks when using `time.Timer` and `time.Ticker`. +1. Be mindful of resource leaks when using `time.Timer` and `time.Ticker`. Always stop them when they are no longer needed. -10. Use `sync.Pool` to manage a pool of reusable objects, which can help - reduce GC overhead and improve performance in high-throughput scenarios. +1. Use `sync.Pool` to manage a pool of reusable objects, which can help + reduce GC overhead and improve performance in high-throughput scenarios. -11. Avoid using large buffer sizes for channels. Unbounded channels can - lead to memory leaks. Use appropriate buffer sizes based on the - application's needs. +1. Avoid using large buffer sizes for channels. Unbounded channels can + lead to memory leaks. Use appropriate buffer sizes based on the + application's needs. -12. Always handle the case where a channel might be closed. This prevents - panic and ensures graceful shutdowns. +1. Always handle the case where a channel might be closed. This prevents + panic and ensures graceful shutdowns. -13. For small structs, use value receivers to avoid unnecessary heap - allocations. Use pointer receivers for large structs or when mutating - the receiver. +1. For small structs, use value receivers to avoid unnecessary heap + allocations. Use pointer receivers for large structs or when mutating + the receiver. -14. Only use goroutines when necessary. Excessive goroutines can lead to - high memory consumption and increased complexity. +1. Only use goroutines when necessary. Excessive goroutines can lead to + high memory consumption and increased complexity. -15. Use `sync.Cond` for more complex synchronization needs that cannot be - met with simple mutexes and channels. +1. Use `sync.Cond` for more complex synchronization needs that cannot be + met with simple mutexes and channels. -16. Reflection is powerful but should be used sparingly as it can lead to - code that is hard to understand and maintain. Prefer type-safe - solutions. +1. Reflection is powerful but should be used sparingly as it can lead to + code that is hard to understand and maintain. Prefer type-safe + solutions. -17. Avoid storing large or complex data in context. Context should be used - for request-scoped values like deadlines, cancellation signals, and - authentication tokens. +1. Avoid storing large or complex data in context. Context should be used + for request-scoped values like deadlines, cancellation signals, and + authentication tokens. -18. Use `runtime.Callers` and `runtime.CallersFrames` to capture stack - traces for debugging and logging purposes. +1. Use `runtime.Callers` and `runtime.CallersFrames` to capture stack + traces for debugging and logging purposes. -19. Use the `testing.TB` interface to write helper functions that can be - used with both `*testing.T` and `*testing.B`. +1. Use the `testing.TB` interface to write helper functions that can be + used with both `*testing.T` and `*testing.B`. -20. Use struct embedding to reuse code across multiple structs. This is a - form of composition that can simplify code reuse. +1. Use struct embedding to reuse code across multiple structs. This is a + form of composition that can simplify code reuse. -21. Prefer defining explicit interfaces in your packages rather than - relying on implicit interfaces. This makes the intended use of - interfaces clearer and the code more maintainable. +1. Prefer defining explicit interfaces in your packages rather than + relying on implicit interfaces. This makes the intended use of + interfaces clearer and the code more maintainable. # Author @@ -665,3 +722,5 @@ Feedback and suggestions are not only welcome but explicitly encouraged. Do with it what you will. There is no warranty, express or implied, including but not limited to merchantability or fitness for a particular purpose. Use at your own risk. + +# Credit