-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Merge codeql-go #7302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merge codeql-go #7302
Conversation
Performance: Fix bad join ordering
Performance: Remove `pragma[noopt]`
Add web framework: github.com/gofiber/fiber
Co-authored-by: Chris Smowton <smowton@github.com>
Required to use this query with the CodeQL CLI
In fact there never was true recursion, but the compiler thought there could be because it supposed that ZipSlip::SanitizerGuard growing may introduce instances that happen to also satisfy TaintedPath::SanitizerGuard. In fact this never happens, but here we make it clear by defining the shared sanitizer guards outside the DataFlow::BarrierGuard hierarchy and then introducing the sanitizers in each query that uses them.
…tizerGuard This avoids computing the full `localTaint` relation when actually there are few `TaintedPath::SanitizerGuard` instances to start from.
…zer-guard-efficiency Improve ZipSlip sanitizer guard efficiency
Add `Where` method of squirrel sql builders to query range
Co-authored-by: Matt Pollard <mattpollard@users.noreply.github.com>
Go: Add Log Injection query (CWE-117)
…face-type Always extract empty interface type
apply the implicit-this patch to the remaining go code
We didn't have any tests involving a function in an imported package.
Going via `getFuncDecl()` didn't work as we don't function declarations from external packages. It works to use `getType()` instead.
Update `isVariadic`
`nonvariadicDeclaredFunction` has the same signature as `variadicDeclaredFunction`, so it is being erroneously reported as variadic.
…type-label Update extractor to distinguish variadic and non-variadic signature types
Conflicts: .codeqlmanifest.json .gitattributes .github/codeql/codeql-config.yml .github/workflows/check-change-note.yml .gitignore .lgtm.yml
dd8fb72 to
f17d356
Compare
|
QHelp previews: go/ql/src/InconsistentCode/ConstantLengthComparison.qhelpConstant length comparisonIndexing operations on arrays, slices, or strings should use an index at most one less than the length. If the operation uses a variable index but checks the length against a constant, this may indicate a logic error which could lead to an out-of-bounds access. RecommendationInspect the code closely to determine whether the length should be compared to the index variable instead. For loops that iterate over every element, using a ExampleThe following example shows a method which checks whether slice package main
func isPrefixOf(xs, ys []int) bool {
for i := 0; i < len(xs); i++ {
if len(ys) == 0 || xs[i] != ys[i] {
return false
}
}
return true
}A loop using an index variable package main
func isPrefixOfGood(xs, ys []int) bool {
for i := 0; i < len(xs); i++ {
if len(ys) <= i || xs[i] != ys[i] {
return false
}
}
return true
}References
go/ql/src/InconsistentCode/InconsistentLoopOrientation.qhelpInconsistent direction of for loopMost The only exception to this are loops whose loop variable is an unsigned integer. In this case, initializing the variable with an upper bound and then decrementing it while checking against the same upper bound is unproblematic: the variable is counted down from the upper bound to zero, and then wraps around to a large positive value, causing the loop to terminate. This is usually intentional, and hence is not flagged by the query. RecommendationExamine the loop carefully to check whether its test expression or update statement are erroneous. ExampleIn the following example, two loops are used to set all elements of a slice package main
func zeroOutExcept(a []int, lower int, upper int) {
// zero out everything below index `lower`
for i := lower - 1; i >= 0; i-- {
a[i] = 0
}
// zero out everything above index `upper`
for i := upper + 1; i < len(a); i-- {
a[i] = 0
}
}To correct this issue, change the second loop to increment its loop variable instead: package main
func zeroOutExcept(a []int, lower int, upper int) {
// zero out everything below index `lower`
for i := lower - 1; i >= 0; i-- {
a[i] = 0
}
// zero out everything above index `upper`
for i := upper + 1; i < len(a); i++ {
a[i] = 0
}
}References
go/ql/src/InconsistentCode/LengthComparisonOffByOne.qhelpOff-by-one comparison against lengthIndexing operations on arrays, slices or strings should use an index at most one less than the length. If the index to be accessed is checked for being less than or equal to the length ( RecommendationUse less than ( ExampleThe following example shows a method which checks whether a value appears in a comma-separated list of values: package main
import "strings"
func containsBad(searchName string, names string) bool {
values := strings.Split(names, ",")
// BAD: index could be equal to length
for i := 0; i <= len(values); i++ {
// When i = length, this access will be out of bounds
if values[i] == searchName {
return true
}
}
return false
}A loop using an index variable One potential solution would be to replace package main
import "strings"
func containsGood(searchName string, names string) bool {
values := strings.Split(names, ",")
// GOOD: Avoid using indexes, use range loop instead
for _, name := range values {
if name == searchName {
return true
}
}
return true
}References
go/ql/src/InconsistentCode/MissingErrorCheck.qhelpMissing error checkWhen a function call returns two values, a pointer and a (subtype of) error, it is conventional to assume that the pointer might be nil until either the pointer or error value has been checked. If the pointer is dereferenced without a check, an unexpected nil pointer dereference panic may occur. RecommendationEnsure that the returned pointer is either directly checked against nil, or the error value is checked before using the returned pointer. ExampleIn the example below, package main
import (
"fmt"
"os"
)
func user(input string) {
ptr, err := os.Open(input)
// BAD: ptr is dereferenced before either it or `err` has been checked.
fmt.Printf("Opened %v\n", *ptr)
if err != nil {
fmt.Printf("Bad input: %s\n", input)
}
}The corrected version of package main
import (
"fmt"
"os"
)
func user(input string) {
ptr, err := os.Open(input)
if err != nil {
fmt.Printf("Bad input: %s\n", input)
return
}
// GOOD: `err` has been checked before `ptr` is used
fmt.Printf("Result was %v\n", *ptr)
}References
go/ql/src/InconsistentCode/MistypedExponentiation.qhelpBitwise exclusive-or used like exponentiationThe caret symbol ( RecommendationTo compute 2 ExampleThe example below prints 34 and not 232 (4294967296). package main
import "fmt"
func main() {
fmt.Println(2 ^ 32) // should be 1 << 32
}References
go/ql/src/InconsistentCode/WhitespaceContradictsPrecedence.qhelpWhitespace contradicts operator precedenceNested expressions where the spacing around operators suggests a different grouping than that imposed by the Go operator precedence rules are problematic: they could indicate a bug where the author of the code misunderstood the precedence rules. Even if there is no a bug, the spacing could be confusing to people who read the code. RecommendationMake sure that the spacing around operators reflects operator precedence, or use parentheses to clarify grouping. ExampleConsider the following function intended for checking whether the bit at position `pos` of the variable `x` is set: package main
func isBitSetBad(x int, pos uint) bool {
return x&1<<pos != 0
}Here, the spacing around To fix this issue and give the expression its intended semantics, parentheses should be used like this: package main
func isBitSetGood(x int, pos uint) bool {
return x&(1<<pos) != 0
}References
go/ql/src/Metrics/FLinesOfCode.qhelpLines of code in filesThere are a number of problems associated with a high number of lines of code:
RecommendationThe solution depends on the reason for the high number of lines:
References
go/ql/src/Metrics/FLinesOfComment.qhelpLines of comments in filesThis metric measures the number of comment lines per file. A low number of comments may indicate files that are difficult to understand due to poor documentation. RecommendationConsider if the file needs more documentation. Most files should have at least a comment explaining their purpose. References
go/ql/src/RedundantCode/CompareIdenticalValues.qhelpComparison of identical valuesComparing two identical expressions typically indicates a mistake such as a missing qualifier or a misspelled variable name. RecommendationCarefully inspect the comparison to determine whether it is a symptom of a bug. ExampleIn the example below, the method package main
type Rectangle struct {
x, y, width, height float64
}
func (r *Rectangle) containsBad(x, y float64) bool {
return r.x <= x &&
y <= y &&
x <= r.x+r.width &&
y <= r.y+r.height
}Note, however, that on line 9 the programmer forgot to qualify package main
func (r *Rectangle) containsGood(x, y float64) bool {
return r.x <= x &&
r.y <= y &&
x <= r.x+r.width &&
y <= r.y+r.height
}go/ql/src/RedundantCode/DeadStoreOfField.qhelpUseless assignment to fieldA value is assigned to a field, but its value is never read. This means that the assignment has no effect, and could indicate a logic error or incomplete code. RecommendationExamine the assignment closely to determine whether it is redundant, or whether it is perhaps a symptom of another bug. ExampleThe following example shows a simple package main
type counter struct {
val int
}
func (c counter) reset() {
c.val = 0
}However, the receiver variable of To fix this, change the type of the receiver variable to package main
func (c *counter) resetGood() {
c.val = 0
}References
go/ql/src/RedundantCode/DeadStoreOfLocal.qhelpUseless assignment to local variableA value is assigned to a variable, but either it is never read, or its value is always overwritten before being read. This means that the original assignment has no effect, and could indicate a logic error or incomplete code. RecommendationRemove assignments to variables that are immediately overwritten, or use the blank identifier ExampleIn the following example, a value is assigned to package main
import "fmt"
func main() {
a := calculateValue()
a = 2
b := calculateValue()
ignore, ignore1 := fmt.Println(a)
ignore, ignore1, err := function()
if err != nil {
panic(err)
}
fmt.Println(a)
}The result of package main
import "fmt"
func main() {
a := 2
fmt.Println(a)
_, _, err := function()
if err != nil {
panic(err)
}
fmt.Println(a)
}References
go/ql/src/RedundantCode/DuplicateBranches.qhelpDuplicate 'if' branchesIf the 'then' and 'else' branches of an 'if' statement are identical, this suggests a copy-paste error where the first branch was copied and then not properly adjusted. RecommendationExamine the two branches to find out what operations were meant to perform. If both the branches and the conditions that they check are identical, then the second branch is duplicate code that can be deleted. If the branches are really meant to perform the same operations, it may be clearer to just have a single branch that checks the disjunction of both conditions. ExampleThe example below shows a buggy implementation of the absolute-value function which checks the sign of its argument, but then returns the same value regardless of the outcome of the check: package main
func abs(x int) int {
if x >= 0 {
return x
} else {
return x
}
}Clearly, the 'else' branch should return package main
func absGood(x int) int {
if x >= 0 {
return x
} else {
return -x
}
}References
go/ql/src/RedundantCode/DuplicateCondition.qhelpDuplicate 'if' conditionIf two conditions in an 'if'-'else if' chain are identical, the second condition will never hold. This most likely indicates a copy-paste error where the first condition was copied and then not properly adjusted. RecommendationExamine the two conditions to find out what they were meant to check. If both the conditions and the branches that depend on them are identical, then the second branch is duplicate code that can be deleted. Otherwise, the second condition needs to be adjusted. ExampleIn the example below, the function package main
func controller(msg string) {
if msg == "start" {
start()
} else if msg == "start" {
stop()
} else {
panic("Message not understood.")
}
}Most likely, the 'else if' branch should compare package main
func controllerGood(msg string) {
if msg == "start" {
start()
} else if msg == "stop" {
stop()
} else {
panic("Message not understood.")
}
}References
go/ql/src/RedundantCode/DuplicateSwitchCase.qhelpDuplicate switch caseIf two cases in a 'switch' statement are identical, the second case will never be executed. This most likely indicates a copy-paste error where the first case was copied and then not properly adjusted. RecommendationExamine the two cases to find out what they were meant to check. If both the conditions and the bodies are identical, then the second case is duplicate code that can be deleted. Otherwise, the second case needs to be adjusted. ExampleIn the example below, the function package main
func controller(msg string) {
switch {
case msg == "start":
start()
case msg == "start":
stop()
default:
panic("Message not understood.")
}
}Most likely, the second case should compare package main
func controllerGood(msg string) {
switch {
case msg == "start":
start()
case msg == "stop":
stop()
default:
panic("Message not understood.")
}
}References
go/ql/src/RedundantCode/ExprHasNoEffect.qhelpExpression has no effectAn expression that has no effects (such as changing variable values or producing output) and occurs in a context where its value is ignored possibly indicates missing code or a latent bug. RecommendationCarefully inspect the expression to ensure it is not a symptom of a bug. ExampleThe following example shows a named type However, when package main
import "fmt"
type Timestamp int
func (t Timestamp) addDays(d int) Timestamp {
return Timestamp(int(t) + d*24*3600)
}
func test(t Timestamp) {
fmt.Printf("Before: %s\n", t)
t.addDays(7)
fmt.Printf("After: %s\n", t)
}Instead, the result of package main
import "fmt"
func testGood(t Timestamp) {
fmt.Printf("Before: %s\n", t)
t = t.addDays(7)
fmt.Printf("After: %s\n", t)
}go/ql/src/RedundantCode/ImpossibleInterfaceNilCheck.qhelpImpossible interface nil checkInterface values in Go are type tagged, that is, they are essentially pairs of the form In particular, if a non-interface value RecommendationCarefully inspect the comparison to ensure it is not a symptom of a bug. ExampleThe following example shows a declaration of a function package main
import "fmt"
func fetch(url string) (string, *RequestError)
func niceFetch(url string) {
var s string
var e error
s, e = fetch(url)
if e != nil {
fmt.Printf("Unable to fetch URL: %v\n", e)
} else {
fmt.Printf("URL contents: %s\n", s)
}
}However, because In this case, the problem can be solved by using a short variable declaration using operator package main
import "fmt"
func niceFetchGood(url string) {
s, e := fetch(url)
if e != nil {
fmt.Printf("Unable to fetch URL: %v\n", e)
} else {
fmt.Printf("URL contents: %s\n", s)
}
}References
go/ql/src/RedundantCode/NegativeLengthCheck.qhelpRedundant check for negative valueThe built-in The same applies to the built-in function RecommendationExamine the length check to see whether it is redundant and can be removed, or a mistake that should be fixed. ExampleThe example below shows a function that returns the first element of an array, triggering a panic if the array is empty: package main
func getFirst(xs []int) int {
if len(xs) < 0 {
panic("No elements provided")
}
return xs[0]
}However, the emptiness check is ineffective: since The check should be rewritten like this: package main
func getFirstGood(xs []int) int {
if len(xs) == 0 {
panic("No elements provided")
}
return xs[0]
}Referencesgo/ql/src/RedundantCode/RedundantExpr.qhelpIdentical operandsMany arithmetic or logical operators yield a trivial result when applied to identical operands: for instance, RecommendationCarefully inspect the expression to ensure it is not a symptom of a bug. ExampleIn the example below, the function package main
func avg(x, y float64) float64 {
return (x + x) / 2
}This problem can be fixed by correcting the typo: package main
func avgGood(x, y float64) float64 {
return (x + y) / 2
}go/ql/src/RedundantCode/RedundantRecover.qhelpRedundant call to recoverThe built-in RecommendationCarefully inspect the code to determine whether it is a mistake that should be fixed. ExampleIn the example below, the function package main
import "fmt"
func callRecover1() {
if recover() != nil {
fmt.Printf("recovered")
}
}
func fun1() {
defer func() {
callRecover1()
}()
panic("1")
}This problem can be fixed by deferring the call to the function which calls package main
import "fmt"
func callRecover1Good() {
if recover() != nil {
fmt.Printf("recovered")
}
}
func fun1Good() {
defer callRecover1Good()
panic("1")
}In the following example, package main
func fun2() {
defer recover()
panic("2")
}We can fix this by instead deferring an anonymous function which calls package main
func fun2Good() {
defer func() { recover() }()
panic("2")
}Referencesgo/ql/src/RedundantCode/SelfAssignment.qhelpSelf assignmentAssigning a variable to itself typically indicates a mistake such as a missing qualifier or a misspelled variable name. RecommendationCarefully inspect the assignment to check for misspellings or missing qualifiers. ExampleIn the example below, the struct type package main
type Rect struct {
x, y, width, height int
}
func (r *Rect) setWidth(width int) {
r.width = width
}
func (r *Rect) setHeight(height int) {
height = height
}Note, however, that in To fix this issue, insert a qualifier: package main
func (r *Rect) setHeightGood(height int) {
r.height = height
}References
go/ql/src/RedundantCode/ShiftOutOfRange.qhelpShift out of rangeShifting an integer value by more than the number of bits in its type always results in -1 for right-shifts of negative values and 0 for other shifts. Hence, such a shift expression is either redundant or indicates a logic mistake. RecommendationExamine the length check to see whether it is redundant and can be removed, or a mistake that should be fixed. ExampleThe following code snippet attempts to compute the value 240 (1099511627776). However, since the left operand package main
func shift(base int32) int32 {
return base << 40
}
var x1 = shift(1)To prevent this, the type of package main
func shiftGood(base int64) int64 {
return base << 40
}
var x2 = shiftGood(1)References
go/ql/src/RedundantCode/UnreachableStatement.qhelpUnreachable statementAn unreachable statement often indicates missing code or a latent bug and should be examined carefully. RecommendationExamine the surrounding code to determine why the statement has become unreachable. If it is no longer needed, remove the statement. ExampleIn the following example, the body of the package main
func mul(xs []int) int {
res := 1
for i := 0; i < len(xs); i++ {
x := xs[i]
res *= x
if res == 0 {
}
return 0
}
return res
}Most likely, the package main
func mulGood(xs []int) int {
res := 1
for i := 0; i < len(xs); i++ {
x := xs[i]
res *= x
if res == 0 {
return 0
}
}
return res
}References
go/ql/src/Security/CWE-020/ExternalAPIsUsedWithUntrustedData.qhelpFrequency counts for external APIs that are used with untrusted dataUsing unsanitized untrusted data in an external API can cause a variety of security issues. This query reports all external APIs that are used with untrusted data, along with how frequently the API is used, and how many unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs may be relevant for security analysis of this application. An external API is defined as a call to a function that is not defined in the source code and is not modeled as a taint step in the default taint library. Calls made in test files are excluded. External APIs may be from the Go standard library, third party dependencies, or from internal dependencies. The query will report the fully-qualified method name, along with either RecommendationFor each result:
Examplepackage main
import (
"fmt"
"net/http"
)
func serve() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
if !isValidUsername(username) {
// BAD: a request parameter is incorporated without validation into the response
fmt.Fprintf(w, "%q is an unknown user", username)
} else {
// TODO: Handle successful login
}
})
http.ListenAndServe(":80", nil)
}If the query were to return the API package main
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)
}If the query were to return the API Note that both examples are correctly handled by the standard taint tracking library and the "Reflected cross-site scripting" query ( References
go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.qhelpIncomplete regular expression for hostnamesSanitizing untrusted URLs is an important technique for preventing attacks such as request forgeries and malicious redirections. Often, this is done by checking that the host of a URL is in a set of allowed hosts. If a regular expression implements such a check, it is easy to accidentally make the check too permissive by not escaping regular-expression meta-characters such as Even if the check is not used in a security-critical context, the incomplete check may still cause undesirable behavior when it accidentally succeeds. RecommendationEscape all meta-characters appropriately when constructing regular expressions for security checks, paying special attention to the ExampleThe following example code checks that a URL redirection will reach the package main
import (
"errors"
"net/http"
"regexp"
)
func checkRedirect(req *http.Request, via []*http.Request) error {
// BAD: the host of `req.URL` may be controlled by an attacker
re := "^((www|beta).)?example.com/"
if matched, _ := regexp.MatchString(re, req.URL.Host); matched {
return nil
}
return errors.New("Invalid redirect")
}The check is however easy to bypass because the unescaped Address this vulnerability by escaping package main
import (
"errors"
"net/http"
"regexp"
)
func checkRedirectGood(req *http.Request, via []*http.Request) error {
// GOOD: the host of `req.URL` must be `example.com`, `www.example.com` or `beta.example.com`
re := "^((www|beta)\\.)?example\\.com/"
if matched, _ := regexp.MatchString(re, req.URL.Host); matched {
return nil
}
return errors.New("Invalid redirect")
}References
go/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.qhelpIncomplete URL scheme checkURLs with the special scheme However, the RecommendationAdd checks covering both ExampleThe following function validates a (presumably untrusted) URL package main
import "net/url"
func sanitizeUrl(urlstr string) string {
u, err := url.Parse(urlstr)
if err != nil || u.Scheme == "javascript" {
return "about:blank"
}
return urlstr
}While this check provides partial projection, it should be extended to cover package main
import "net/url"
func sanitizeUrlGod(urlstr string) string {
u, err := url.Parse(urlstr)
if err != nil || u.Scheme == "javascript" || u.Scheme == "data" || u.Scheme == "vbscript" {
return "about:blank"
}
return urlstr
}References
go/ql/src/Security/CWE-020/MissingRegexpAnchor.qhelpMissing regular expression anchorSanitizing untrusted input with regular expressions is a common technique. However, it is error-prone to match untrusted input against regular expressions without anchors such as Even if the matching is not done in a security-critical context, it may still cause undesirable behavior when the regular expression accidentally matches. RecommendationUse anchors to ensure that regular expressions match at the expected locations. ExampleThe following example code checks that a URL redirection will reach the package main
import (
"errors"
"net/http"
"regexp"
)
func checkRedirect2(req *http.Request, via []*http.Request) error {
// BAD: the host of `req.URL` may be controlled by an attacker
re := "https?://www\\.example\\.com/"
if matched, _ := regexp.MatchString(re, req.URL.String()); matched {
return nil
}
return errors.New("Invalid redirect")
}The check with the regular expression match is, however, easy to bypass. For example, the string Address these shortcomings by using anchors in the regular expression instead: package main
import (
"errors"
"net/http"
"regexp"
)
func checkRedirect2Good(req *http.Request, via []*http.Request) error {
// GOOD: the host of `req.URL` cannot be controlled by an attacker
re := "^https?://www\\.example\\.com/"
if matched, _ := regexp.MatchString(re, req.URL.String()); matched {
return nil
}
return errors.New("Invalid redirect")
}A related mistake is to write a regular expression with multiple alternatives, but to only anchor one of the alternatives. As an example, the regular expression References
go/ql/src/Security/CWE-020/SuspiciousCharacterInRegexp.qhelpSuspicious characters in a regular expressionWhen a character in a string literal or regular expression literal is preceded by a backslash, it is interpreted as part of an escape sequence. For example, the escape sequence RecommendationEnsure that the right number of backslashes is used when escaping characters in strings and regular expressions. ExampleThe following example code fails to check for a forbidden word in an input string: package main
import "regexp"
func broken(hostNames []byte) string {
var hostRe = regexp.MustCompile("\bforbidden.host.org")
if hostRe.Match(hostNames) {
return "Must not target forbidden.host.org"
} else {
// This will be reached even if hostNames is exactly "forbidden.host.org",
// because the literal backspace is not matched
return ""
}
}The check does not work, but can be fixed by escaping the backslash: package main
import "regexp"
func fixed(hostNames []byte) string {
var hostRe = regexp.MustCompile(`\bforbidden.host.org`)
if hostRe.Match(hostNames) {
return "Must not target forbidden.host.org"
} else {
// hostNames definitely doesn't contain a word "forbidden.host.org", as "\\b"
// is the start-of-word anchor, not a literal backspace.
return ""
}
}Alternatively, you can use backtick-delimited raw string literals. For example, the References
go/ql/src/Security/CWE-020/UntrustedDataToExternalAPI.qhelpUntrusted data passed to external APIUsing unsanitized untrusted data in an external API can cause a variety of security issues. This query reports external APIs that use untrusted data. The results have very little filtering so that you can audit almost all examples. The query provides data for security reviews of the application. It can also be used to identify external APIs that should be modeled as either taint steps, or sinks for specific problems. An external API is defined as a call to a function that is not defined in the source code and is not modeled as a taint step in the default taint library. Calls made in test files are excluded. External APIs may be from the Go standard library, third-party dependencies, or from internal dependencies. The query reports uses of untrusted data in either the receiver or as one of the arguments of external APIs. RecommendationFor each result:
ExampleIn this first example, a request parameter is read from package main
import (
"fmt"
"net/http"
)
func serve() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
if !isValidUsername(username) {
// BAD: a request parameter is incorporated without validation into the response
fmt.Fprintf(w, "%q is an unknown user", username)
} else {
// TODO: Handle successful login
}
})
http.ListenAndServe(":80", nil)
}This is an XSS sink. The "Reflected cross-site scripting" query ( In this second example, again a request parameter is read from package main
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)
}If the query reported the call to Note that both examples are correctly handled by the standard taint tracking library, the "Reflected cross-site scripting" query ( References
go/ql/src/Security/CWE-020/UntrustedDataToUnknownExternalAPI.qhelpUntrusted data passed to unknown external APIUsing unsanitized untrusted data in an external API can cause a variety of security issues. This query reports external APIs that use untrusted data. The results have been filtered to only report unknown external APIs. The query provides data for security reviews of the application. It can also be used to identify external APIs that should be modeled as either taint steps, or sinks for specific problems. An external API is defined as a call to a function that is not defined in the source code and is not modeled as a taint step in the default taint library. Calls made in test files are excluded. External APIs may be from the Go standard library, third-party dependencies, or from internal dependencies. The query reports uses of untrusted data in either the receiver or as one of the arguments of external APIs. An external API is considered unknown if it is not in a package which has already been modeled, it is not a sink for an existing query, and it is not in a list of external APIs which have been examined and determined to not be a possible source of security vulnerabilities. RecommendationFor each result:
ExampleIn this first example, a request parameter is read from package main
import (
"fmt"
"net/http"
)
func serve() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
if !isValidUsername(username) {
// BAD: a request parameter is incorporated without validation into the response
fmt.Fprintf(w, "%q is an unknown user", username)
} else {
// TODO: Handle successful login
}
})
http.ListenAndServe(":80", nil)
}This is an XSS sink. The "Reflected cross-site scripting" query ( In this second example, again a request parameter is read from package main
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)
}If the query reported the call to Note that both examples are correctly handled by the standard taint tracking library, the "Reflected cross-site scripting" query ( References
go/ql/src/Security/CWE-022/TaintedPath.qhelpUncontrolled data used in path expressionAccessing files using paths constructed from user-controlled data can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files. RecommendationValidate user input before using it to construct a file path, either using an off-the-shelf library or by performing custom validation. Ideally, follow these rules:
ExampleIn the first example, a file name is read from an HTTP request and then used to access a file. However, a malicious user could enter a file name which is an absolute path, such as "/etc/passwd". In the second example, it appears that the user is restricted to opening a file within the package main
import (
"io/ioutil"
"net/http"
"path/filepath"
)
func handler(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query()["path"][0]
// BAD: This could read any file on the file system
data, _ := ioutil.ReadFile(path)
w.Write(data)
// BAD: This could still read any file on the file system
data, _ = ioutil.ReadFile(filepath.Join("/home/user/", path))
w.Write(data)
}Referencesgo/ql/src/Security/CWE-022/UnsafeUnzipSymlink.qhelpArbitrary file write extracting an archive containing symbolic linksExtracting symbolic links from a malicious zip archive, without validating that the destination file path is within the destination directory, can cause files outside the destination directory to be overwritten. This can happen if there are previously-extracted symbolic links or directory traversal elements and links ( This problem is related to the ZipSlip vulnerability which is detected by the RecommendationEnsure that output paths constructed from zip archive entries are validated. This includes resolving any previously extracted symbolic links, for example using ExampleIn this example, links are extracted from an archive using the syntactic package main
import (
"archive/tar"
"io"
"os"
"path/filepath"
"strings"
)
func isRel(candidate, target string) bool {
// BAD: purely syntactic means are used to check
// that `candidate` does not escape from `target`
if filepath.IsAbs(candidate) {
return false
}
relpath, err := filepath.Rel(target, filepath.Join(target, candidate))
return err == nil && !strings.HasPrefix(filepath.Clean(relpath), "..")
}
func unzipSymlink(f io.Reader, target string) {
r := tar.NewReader(f)
for {
header, err := r.Next()
if err != nil {
break
}
if isRel(header.Linkname, target) && isRel(header.Name, target) {
os.Symlink(header.Linkname, header.Name)
}
}
}To fix this vulnerability, resolve pre-existing symbolic links before checking that the link's target is acceptable: package main
func isRel(candidate, target string) bool {
// GOOD: resolves all symbolic links before checking
// that `candidate` does not escape from `target`
if filepath.IsAbs(candidate) {
return false
}
realpath, err := filepath.EvalSymlinks(filepath.Join(target, candidate))
if err != nil {
return false
}
relpath, err := filepath.Rel(target, realpath)
return err == nil && !strings.HasPrefix(filepath.Clean(relpath), "..")
}References
go/ql/src/Security/CWE-022/ZipSlip.qhelpArbitrary file write during zip extraction ("zip slip")Extracting files from a malicious zip archive without validating that the destination file path is within the destination directory can cause files outside the destination directory to be overwritten, due to the possible presence of directory traversal elements ( Zip archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element ( For example, if a zip file contains a file entry RecommendationEnsure that output paths constructed from zip archive entries are validated to prevent writing files to unexpected locations. The recommended way of writing an output file from a zip archive entry is to check that " ExampleIn this example an archive is extracted without validating file paths. If package main
import (
"archive/zip"
"io/ioutil"
"path/filepath"
)
func unzip(f string) {
r, _ := zip.OpenReader(f)
for _, f := range r.File {
p, _ := filepath.Abs(f.Name)
// BAD: This could overwrite any file on the file system
ioutil.WriteFile(p, []byte("present"), 0666)
}
}To fix this vulnerability, we need to check that the path does not contain any " package main
import (
"archive/zip"
"io/ioutil"
"path/filepath"
"strings"
)
func unzipGood(f string) {
r, _ := zip.OpenReader(f)
for _, f := range r.File {
p, _ := filepath.Abs(f.Name)
// GOOD: Check that path does not contain ".." before using it
if !strings.Contains(p, "..") {
ioutil.WriteFile(p, []byte("present"), 0666)
}
}
}References
go/ql/src/Security/CWE-078/CommandInjection.qhelpCommand built from user-controlled sourcesIf a system command invocation is built from user-provided data without sufficient sanitization, a malicious user may be able to run commands to exfiltrate data or compromise the system. RecommendationIf possible, use hard-coded string literals to specify the command to run. Instead of interpreting user input directly as command names, examine the input and then choose among hard-coded string literals. If this is not possible, then add sanitization code to verify that the user input is safe before using it. ExampleIn the following example, assume the function package main
import (
"net/http"
"os/exec"
)
func handler(req *http.Request) {
cmdName := req.URL.Query()["cmd"][0]
cmd := exec.Command(cmdName)
cmd.Run()
}The handler extracts the name of a system command from the request object, and then runs it without any further checks, which can cause a command-injection vulnerability. References
go/ql/src/Security/CWE-078/StoredCommand.qhelpCommand built from stored dataIf a system command invocation is built from stored data without sufficient sanitization, and that data is stored from a user input, a malicious user may be able to run commands to exfiltrate data or compromise the system. RecommendationIf possible, use hard-coded string literals to specify the command to run. Instead of interpreting stored input directly as command names, examine the input and then choose among hard-coded string literals. If this is not possible, then add sanitization code to verify that the user input is safe before using it. ExampleIn the following example, the function package main
import (
"database/sql"
"os/exec"
)
var db *sql.DB
func run(query string) {
rows, _ := db.Query(query)
var cmdName string
rows.Scan(&cmdName)
cmd := exec.Command(cmdName)
cmd.Run()
}The function extracts the name of a system command from the database query, and then runs it without any further checks, which can cause a command-injection vulnerability. A possible solution is to ensure that commands are checked against a whitelist: package main
import (
"database/sql"
"os/exec"
)
var db *sql.DB
func run(query string) {
rows, _ := db.Query(query)
var cmdName string
rows.Scan(&cmdName)
if cmdName == "mybinary1" || cmdName == "mybinary2" {
cmd := exec.Command(cmdName)
}
cmd.Run()
}References
go/ql/src/Security/CWE-079/ReflectedXss.qhelpReflected cross-site scriptingDirectly writing user input (for example, an HTTP request parameter) to an HTTP response without properly sanitizing the input first, allows for a cross-site scripting vulnerability. This kind of vulnerability is also called reflected cross-site scripting, to distinguish it from other types of cross-site scripting. RecommendationTo guard against cross-site scripting, consider using contextual output encoding/escaping before writing user input to the response, or one of the other solutions that are mentioned in the references. ExampleThe following example code writes part of an HTTP request (which is controlled by the user) directly to the response. This leaves the website vulnerable to cross-site scripting. package main
import (
"fmt"
"net/http"
)
func serve() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
if !isValidUsername(username) {
// BAD: a request parameter is incorporated without validation into the response
fmt.Fprintf(w, "%q is an unknown user", username)
} else {
// TODO: Handle successful login
}
})
http.ListenAndServe(":80", nil)
}Sanitizing the user-controlled data prevents the vulnerability: package main
import (
"fmt"
"html"
"net/http"
)
func serve1() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
if !isValidUsername(username) {
// GOOD: a request parameter is escaped before being put into the response
fmt.Fprintf(w, "%q is an unknown user", html.EscapeString(username))
} else {
// TODO: do something exciting
}
})
http.ListenAndServe(":80", nil)
}References
go/ql/src/Security/CWE-079/StoredXss.qhelpStored cross-site scriptingDirectly using externally-controlled stored values (for example, file names or database contents) to create HTML content without properly sanitizing the input first, allows for a cross-site scripting vulnerability. This kind of vulnerability is also called stored cross-site scripting, to distinguish it from other types of cross-site scripting. RecommendationTo guard against cross-site scripting, consider using contextual output encoding/escaping before using uncontrolled stored values to create HTML content, or one of the other solutions that are mentioned in the references. ExampleThe following example code writes file names directly to an HTTP response. This leaves the website vulnerable to cross-site scripting, if an attacker can choose the file names on the disk. package main
import (
"io"
"io/ioutil"
"net/http"
)
func ListFiles(w http.ResponseWriter, r *http.Request) {
files, _ := ioutil.ReadDir(".")
for _, file := range files {
io.WriteString(w, file.Name()+"\n")
}
}Sanitizing the file names prevents the vulnerability: package main
import (
"html"
"io"
"io/ioutil"
"net/http"
)
func ListFiles1(w http.ResponseWriter, r *http.Request) {
files, _ := ioutil.ReadDir(".")
for _, file := range files {
io.WriteString(w, html.EscapeString(file.Name())+"\n")
}
}References
go/ql/src/Security/CWE-089/SqlInjection.qhelpDatabase query built from user-controlled sourcesIf a database query (such as an SQL or NoSQL query) is built from user-provided data without sufficient sanitization, a malicious user may be able to run commands that exfiltrate, tamper with, or destroy data stored in the database. RecommendationMost database connector libraries offer a way of safely embedding untrusted data into a query by means of query parameters or prepared statements. Use these features rather than building queries by string concatenation. ExampleIn the following example, assume the function package main
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)
}The handler constructs an SQL query involving user input taken from the request object unsafely using Instead, the untrusted query parameter should be safely embedded using placeholder parameters: package main
import (
"database/sql"
"net/http"
)
func handlerGood(db *sql.DB, req *http.Request) {
q := "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='?' ORDER BY PRICE"
db.Query(q, req.URL.Query()["category"])
}References
go/ql/src/Security/CWE-089/StringBreak.qhelpPotentially unsafe quotingCode that constructs a string containing a quoted substring needs to ensure that any user-provided data embedded in between the quotes does not itself contain a quote. Otherwise the embedded data could (accidentally or intentionally) change the structure of the overall string by terminating the quoted substring early, with potentially severe consequences. If, for example, the string is later interpreted as an operating-system command or database query, a malicious attacker may be able to craft input data that enables a command injection or SQL injection attack. RecommendationSanitize the embedded data appropriately to ensure quotes are escaped, or use an API that does not rely on manually constructing quoted substrings. ExampleIn the following example, assume that package main
import (
"encoding/json"
"fmt"
sq "github.com/Masterminds/squirrel"
)
func save(id string, version interface{}) {
versionJSON, _ := json.Marshal(version)
sq.StatementBuilder.
Insert("resources").
Columns("resource_id", "version_md5").
Values(id, sq.Expr(fmt.Sprintf("md5('%s')", versionJSON))).
Exec()
}Note that while Squirrel provides a structured API for building SQL queries that mitigates against common causes of SQL injection vulnerabilities, this code is still vulnerable: if the JSON-encoded representation of To fix this vulnerability, use Squirrel's placeholder syntax, which avoids the need to explicitly construct a quoted string. package main
import (
"encoding/json"
sq "github.com/Masterminds/squirrel"
)
func saveGood(id string, version interface{}) {
versionJSON, _ := json.Marshal(version)
sq.StatementBuilder.
Insert("resources").
Columns("resource_id", "version_md5").
Values(id, sq.Expr("md5(?)", versionJSON)).
Exec()
}References
go/ql/src/Security/CWE-117/LogInjection.qhelpLog entries created from user inputIf unsanitized user input is written to a log entry, a malicious user may be able to forge new log entries. Forgery can occur if a user provides some input with characters that are interpreted when the log output is displayed. If the log is displayed as a plain text file, then new line characters can be used by a malicious user. If the log is displayed as HTML, then arbitrary HTML may be included to spoof log entries. RecommendationUser input should be suitably encoded before it is logged. If the log entries are plain text then line breaks should be removed from user input, using For log entries that will be displayed in HTML, user input should be HTML encoded using ExampleIn the following example, a user name, provided by the user, is logged using a logging framework without any sanitization. package main
import (
"log"
"net/http"
)
// BAD: A user-provided value is written directly to a log.
func handler(req *http.Request) {
username := req.URL.Query()["username"][0]
log.Printf("user %s logged in.\n", username)
}In the next example, package main
import (
"log"
"net/http"
"strings"
)
// GOOD: The user-provided value is escaped before being written to the log.
func handlerGood(req *http.Request) {
username := req.URL.Query()["username"][0]
escapedUsername := strings.Replace(username, "\n", "", -1)
escapedUsername = strings.Replace(escapedUsername, "\r", "", -1)
log.Printf("user %s logged in.\n", escapedUsername)
}References
go/ql/src/Security/CWE-190/AllocationSizeOverflow.qhelpSize computation for allocation may overflowPerforming calculations involving the size of potentially large strings or slices can result in an overflow (for signed integer types) or a wraparound (for unsigned types). An overflow causes the result of the calculation to become negative, while a wraparound results in a small (positive) number. This can cause further issues. If, for example, the result is then used in an allocation, it will cause a runtime panic if it is negative, and allocate an unexpectedly small buffer otherwise. RecommendationAlways guard against overflow in arithmetic operations involving potentially large numbers by doing one of the following:
ExampleIn the following example, assume that there is a function package main
import "encoding/json"
func encryptValue(v interface{}) ([]byte, error) {
jsonData, err := json.Marshal(v)
if err != nil {
return nil, err
}
size := len(jsonData) + (len(jsonData) % 16)
buffer := make([]byte, size)
copy(buffer, jsonData)
return encryptBuffer(buffer)
}When passed a value whose JSON encoding is close to the maximum value of type To guard against this, the function should be improved to check the length of the JSON-encoded value. For example, here is a version of package main
import (
"encoding/json"
"errors"
)
func encryptValueGood(v interface{}) ([]byte, error) {
jsonData, err := json.Marshal(v)
if err != nil {
return nil, err
}
if len(jsonData) > 64*1024*1024 {
return nil, errors.New("value too large")
}
size := len(jsonData) + (len(jsonData) % 16)
buffer := make([]byte, size)
copy(buffer, jsonData)
return encryptBuffer(buffer)
}References
go/ql/src/Security/CWE-209/StackTraceExposure.qhelpInformation exposure through a stack traceSoftware developers often add stack traces to error messages, as a debugging aid. Whenever that error message occurs for an end user, the developer can use the stack trace to help identify how to fix the problem. In particular, stack traces can tell the developer more about the sequence of events that led to a failure, as opposed to merely the final state of the software when the error occurred. Unfortunately, the same information can be useful to an attacker. The sequence of class names in a stack trace can reveal the structure of the application as well as any internal components it relies on. RecommendationSend the user a more generic error message that reveals less information. Either suppress the stack trace entirely, or log it only on the server. ExampleIn the following example, a panic is handled in two different ways. In the first version, labeled BAD, a detailed stack trace is written to a user-facing HTTP response object, which may disclose sensitive information. In the second version, the error message is logged only on the server. That way, the developers can still access and use the error log, but remote users will not see the information. package example
import (
"log"
"net/http"
"runtime"
)
func handlePanic(w http.ResponseWriter, r *http.Request) {
buf := make([]byte, 2<<16)
buf = buf[:runtime.Stack(buf, true)]
// BAD: printing a stack trace back to the response
w.Write(buf)
// GOOD: logging the response to the server and sending
// a more generic message.
log.Printf("Panic: %s", buf)
w.Write([]byte("An unexpected runtime error occurred"))
}References
go/ql/src/Security/CWE-295/DisabledCertificateCheck.qhelpDisabled TLS certificate checkThe field RecommendationDo not set ExampleThe following code snippet shows a function that performs an HTTP request over TLS with certificate verification disabled: package main
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
}While this is acceptable in a test, it should not be used in production code. Instead, certificates should be configured such that verification can be performed. References
go/ql/src/Security/CWE-312/CleartextLogging.qhelpClear-text logging of sensitive informationSensitive information that is logged unencrypted is accessible to an attacker who gains access to the logs. RecommendationEnsure that sensitive information is always encrypted or obfuscated before being logged. In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext. Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored. ExampleThe following example code logs user credentials (in this case, their password) in plain text: package main
import (
"log"
"net/http"
)
func serve() {
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)
}Instead, the credentials should be encrypted, obfuscated, or omitted entirely: package main
import (
"log"
"net/http"
)
func serve1() {
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)
}References
go/ql/src/Security/CWE-322/InsecureHostKeyCallback.qhelpUse of insecure HostKeyCallback implementationThe Not properly verifying the host key returned by a server provides attackers with an opportunity to perform a Machine-in-the-Middle (MitM) attack. A successful attack can compromise the confidentiality and integrity of the information communicated with the server. The RecommendationThe When the allow list contains only a single host key then the function ExampleThe following example shows the use of package main
import (
"golang.org/x/crypto/ssh"
"net"
)
func main() {}
func insecureIgnoreHostKey() {
_ = &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{nil},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
}
func insecureHostKeyCallback() {
_ = &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{nil},
HostKeyCallback: ssh.HostKeyCallback(
func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
}),
}
}The next example shows a secure implementation using the package main
import (
"golang.org/x/crypto/ssh"
"io/ioutil"
)
func main() {}
func secureHostKeyCallback() {
publicKeyBytes, _ := ioutil.ReadFile("allowed_hostkey.pub")
publicKey, _ := ssh.ParsePublicKey(publicKeyBytes)
_ = &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{nil},
HostKeyCallback: ssh.FixedHostKey(publicKey),
}
}References
go/ql/src/Security/CWE-326/InsufficientKeySize.qhelpUse of a weak cryptographic keyIncorrect uses of encryption algorithms may result in sensitive data exposure, key leakage, broken authentication, insecure session, and spoofing attacks. RecommendationEnsure that you use a strong key with a recommended bit size. For RSA encryption the minimum size is 2048 bits. ExampleThe following code uses RSA encryption with insufficient key size. package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
)
func main() {
//Generate Private Key
pvk, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
fmt.Println(err)
}
fmt.Println(pvk)
}In the example below, the key size is set to 2048 bits. package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
)
func main() {
//Generate Private Key
pvk, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
fmt.Println(err)
}
fmt.Println(pvk)
}References
go/ql/src/Security/CWE-327/InsecureTLS.qhelpInsecure TLS configurationThe TLS (Transport Layer Security) protocol secures communications over the Internet. The protocol allows client/server applications to communicate in a way that is designed to prevent eavesdropping, tampering, or message forgery. The current latest version is 1.3 (with the 1.2 version still being considered secure). Older versions are not deemed to be secure anymore because of various security vulnerabilities, and tht makes them unfit for use in securing your applications. Unfortunately, many applications and websites still support deprecated SSL/TLS versions and cipher suites. RecommendationOnly use secure TLS versions (1.3 and 1.2) and avoid using insecure cipher suites (you can see a list here: https://golang.org/src/crypto/tls/cipher_suites.go\#L81) ExampleThe following example shows a few ways how an insecure TLS configuration can be created: package main
import (
"crypto/tls"
)
func main() {}
func insecureMinMaxTlsVersion() {
{
config := &tls.Config{}
config.MinVersion = 0 // BAD: Setting the MinVersion to 0 equals to choosing the lowest supported version (i.e. SSL3.0)
}
{
config := &tls.Config{}
config.MinVersion = tls.VersionSSL30 // BAD: SSL 3.0 is a non-secure version of the protocol; it's not safe to use it as MinVersion.
}
{
config := &tls.Config{}
config.MaxVersion = tls.VersionSSL30 // BAD: SSL 3.0 is a non-secure version of the protocol; it's not safe to use it as MaxVersion.
}
}
func insecureCipherSuites() {
config := &tls.Config{
CipherSuites: []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA, // BAD: TLS_RSA_WITH_RC4_128_SHA is one of the non-secure cipher suites; it's not safe to be used.
},
}
_ = config
}The following example shows how to create a safer TLS configuration: package main
import "crypto/tls"
func saferTLSConfig() {
config := &tls.Config{}
config.MinVersion = tls.VersionTLS12
config.MaxVersion = tls.VersionTLS13
// OR
config.MaxVersion = 0 // GOOD: Setting MaxVersion to 0 means that the highest version available in the package will be used.
}References
go/ql/src/Security/CWE-338/InsecureRandomness.qhelpUse of insufficient randomness as the key of a cryptographic algorithmUsing a cryptographically weak pseudo-random number generator to generate a security-sensitive value, such as a password, makes it easier for an attacker to predict the value. Pseudo-random number generators generate a sequence of numbers that only approximates the properties of random numbers. The sequence is not truly random because it is completely determined by a relatively small set of initial values, the seed. If the random number generator is cryptographically weak, then this sequence may be easily predictable through outside observations. RecommendationUse a cryptographically secure pseudo-random number generator if the output is to be used in a security sensitive context. As a rule of thumb, a value should be considered "security sensitive" if predicting it would allow the attacker to perform an action that they would otherwise be unable to perform. For example, if an attacker could predict the random password generated for a new user, they would be able to log in as that new user. For Go, ExampleThe example below uses the package main
import (
"math/rand"
)
var charset = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
func generatePassword() string {
s := make([]rune, 20)
for i := range s {
s[i] = charset[rand.Intn(len(charset))]
}
return string(s)
}Instead, use package main
import (
"crypto/rand"
"math/big"
)
func generatePasswordGood() string {
s := make([]rune, 20)
for i := range s {
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
// handle err
}
s[i] = charset[idx.Int64()]
}
return string(s)
}References
go/ql/src/Security/CWE-352/ConstantOauth2State.qhelpUse of constant
|


New commits: