Skip to content

Commit

Permalink
feat: add timing attack js (#312)
Browse files Browse the repository at this point in the history
  • Loading branch information
cfabianski authored Feb 23, 2024
1 parent 96ba623 commit 0135cfc
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 7 deletions.
6 changes: 3 additions & 3 deletions rules/go/gosec/file_permissions/file_perm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ auxiliary:
filters:
- either:
- variable: MASK
regex: \A07
regex: \A0o?7
- variable: MASK
regex: \A0\d[1-7]
regex: \A0o?\d[1-7]
- variable: MASK
regex: \A0\d\d[1-7]
regex: \A0o?\d\d[1-7]
languages:
- go
metadata:
Expand Down
2 changes: 1 addition & 1 deletion rules/go/gosec/file_permissions/mkdir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ auxiliary:
- pattern: $<MASK>
filters:
- variable: MASK
regex: \A077
regex: \A0o?77
languages:
- go
metadata:
Expand Down
218 changes: 218 additions & 0 deletions rules/go/lang/observable_timing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
patterns:
- pattern: |
$<KEY1> == $<KEY2>
filters:
- variable: KEY1
regex: /pass(word)?/
- variable: KEY2
regex: /pass(word)?/
# - pattern: |
# return $X === auth_token;
# - pattern: |
# return auth_token === $X;
# - pattern: |
# return $X === token;
# - pattern: |
# return token === $X;
# - pattern: |
# return $X === hash;
# - pattern: |
# return hash === $X;
# - pattern: |
# return $X === password;
# - pattern: |
# return password === $X;
# - pattern: |
# return $X === pass;
# - pattern: |
# return pass === $X;
# - pattern: |
# return $X === apiKey;
# - pattern: |
# return apiKey === $X;
# - pattern: |
# return $X === apiSecret;
# - pattern: |
# return apiSecret === $X;
# - pattern: |
# return $X === api_key;
# - pattern: |
# return api_key === $X;
# - pattern: |
# return $X === api_secret;
# - pattern: |
# return api_secret === $X;
# - pattern: |
# return $X === secret;
# - pattern: |
# return secret === $X;
# - pattern: |
# return $X === api;
# - pattern: |
# return api === $X;
# - pattern: |
# return $X == auth_token;
# - pattern: |
# return auth_token == $X;
# - pattern: |
# return $X == token;
# - pattern: |
# return token == $X;
# - pattern: |
# return $X == hash;
# - pattern: |
# return hash == $X;
# - pattern: |
# return $X == password;
# - pattern: |
# return password == $X;
# - pattern: |
# return $X == pass;
# - pattern: |
# return pass == $X;
# - pattern: |
# return $X == apiKey;
# - pattern: |
# return apiKey == $X;
# - pattern: |
# return $X == apiSecret;
# - pattern: |
# return apiSecret == $X;
# - pattern: |
# return $X == api_key;
# - pattern: |
# return api_key == $X;
# - pattern: |
# return $X == api_secret;
# - pattern: |
# return api_secret == $X;
# - pattern: |
# return $X == secret;
# - pattern: |
# return secret == $X;
# - pattern: |
# return $X == api;
# - pattern: |
# return api == $X;
# - pattern: |
# return $X !== auth_token;
# - pattern: |
# return auth_token !== $X;
# - pattern: |
# return $X !== token;
# - pattern: |
# return token !== $X;
# - pattern: |
# return $X !== hash;
# - pattern: |
# return hash !== $X;
# - pattern: |
# return $X !== password;
# - pattern: |
# return password !== $X;
# - pattern: |
# return $X !== pass;
# - pattern: |
# return pass !== $X;
# - pattern: |
# return $X !== apiKey;
# - pattern: |
# return apiKey !== $X;
# - pattern: |
# return $X !== apiSecret;
# - pattern: |
# return apiSecret !== $X;
# - pattern: |
# return $X !== api_key;
# - pattern: |
# return api_key !== $X;
# - pattern: |
# return $X !== api_secret;
# - pattern: |
# return api_secret !== $X;
# - pattern: |
# return $X !== secret;
# - pattern: |
# return secret !== $X;
# - pattern: |
# return $X !== api;
# - pattern: |
# return api !== $X;
# - pattern: |
# return $X != auth_token;
# - pattern: |
# return auth_token != $X;
# - pattern: |
# return $X != token;
# - pattern: |
# return token != $X;
# - pattern: |
# return $X != hash;
# - pattern: |
# return hash != $X;
# - pattern: |
# return $X != password;
# - pattern: |
# return password != $X;
# - pattern: |
# return $X != pass;
# - pattern: |
# return pass != $X;
# - pattern: |
# return $X != apiKey;
# - pattern: |
# return apiKey != $X;
# - pattern: |
# return $X != apiSecret;
# - pattern: |
# return apiSecret != $X;
# - pattern: |
# return $X != api_key;
# - pattern: |
# return api_key != $X;
# - pattern: |
# return $X != api_secret;
# - pattern: |
# return api_secret != $X;
# - pattern: |
# return $X != secret;
# - pattern: |
# return secret != $X;
# - pattern: |
# return $X != api;
# - pattern: |
# return api != $X;
# auxiliary:
# - id: go_lang_observable_timing_init
# patterns:
# - pattern1
# - pattern: $<INIT>
# filters:
# - variable: INIT
# detection: go_lang_observable_timing_instance
# scope: cursor
# - id: go_lang_observable_timing_instance
# patterns:
# - pattern2
languages:
- go
metadata:
description: ""
remediation_message: |
## Description
## Remediations
## References
- []()
cwe_id:
- 208
id: go_lang_observable_timing
documentation_url: https://docs.bearer.com/reference/rules/go_lang_observable_timing
6 changes: 3 additions & 3 deletions rules/javascript/lang/file_permissions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ auxiliary:
filters:
- either:
- variable: MASK
regex: \A0o7
regex: \A0o?7
- variable: MASK
regex: \A0o\d[1-7]
regex: \A0o?\d[1-7]
- variable: MASK
regex: \A0o\d\d[1-7]
regex: \A0o?\d\d[1-7]
languages:
- javascript
severity: high
Expand Down
80 changes: 80 additions & 0 deletions rules/javascript/lang/observable_timing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
patterns:
- pattern: |
$<KEY1> == $<OTHER>
filters:
- variable: KEY1
detection: javascript_lang_observable_timing_regex
scope: cursor
- not:
variable: OTHER
detection: javascript_lang_observable_timing_other
scope: cursor
- pattern: |
$<OTHER> == $<KEY1>
filters:
- variable: KEY1
detection: javascript_lang_observable_timing_regex
scope: cursor
- not:
variable: OTHER
detection: javascript_lang_observable_timing_other
scope: cursor
auxiliary:
- id: javascript_lang_observable_timing_regex
patterns:
- pattern: $<REGEX>
filters:
- variable: REGEX
regex: (?i)\A(password)|hash|(api|auth)?.?(token|secret)\z

- id: javascript_lang_observable_timing_other
patterns:
- pattern: $<OTHER>
filters:
- variable: OTHER
regex: ^((null)|(undefined))

languages:
- javascript
metadata:
description: "Observable Timing Discrepancy"
remediation_message: |
## Description
Observable Timing Discrepancy refers to vulnerabilities arising from attackers being able to observe differences in the time it takes to perform certain operations.
These discrepancies can lead to the exposure of sensitive information, as attackers can use timing analysis to infer secrets based on the execution time of algorithms, particularly during comparisons of user input against secret values.
## Remediations
✅ **Implement Constant Time Algorithms**
Ensure algorithms that process sensitive information, such as password comparisons, execute in constant time to prevent timing attacks.
✅ **Use Built-in Security Features**
Leverage built-in cryptographic libraries that include timing-attack safe functions for comparing secrets.
✅ **Minimize Client-Side Checks**
Avoid sensitive comparisons directly in client-side JavaScript where possible, as the execution environment is more exposed to timing analysis by attackers.
❌ **Avoid Direct Comparisons**
Do not use direct string comparisons for sensitive information that can lead to early function termination based on the first incorrect character.
❌ **Do Not Rely on Execution Time for Logic**
Ensure the application's logic does not change execution paths in a way that could introduce observable timing differences based on user input or secret values.
## Resources
- [CWE-208: Observable Timing Discrepancy](https://cwe.mitre.org/data/definitions/208.html)
- [OWASP Guide to Cryptography](https://owasp.org/www-project-cheat-sheets/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)
- [MDN Web Docs on SubtleCrypto API](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
cwe_id:
- 409
id: javascript_lang_observable_timing
documentation_url: https://docs.bearer.com/reference/rules/javascript_lang_observable_timing
18 changes: 18 additions & 0 deletions tests/javascript/lang/observable_timing/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const {
createNewInvoker,
getEnvironment,
} = require("../../../helper.js")
const { ruleId, ruleFile, testBase } = getEnvironment(__dirname)

describe(ruleId, () => {
const invoke = createNewInvoker(ruleId, ruleFile, testBase)

test("observable_timing", () => {
const testCase = "app.js"

const results = invoke(testCase)

expect(results.Missing).toEqual([])
expect(results.Extra).toEqual([])
})
})
33 changes: 33 additions & 0 deletions tests/javascript/lang/observable_timing/testdata/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Use bearer:expected javascript_lang_observable_timing to flag expected findings

// bearer:expected javascript_lang_observable_timing
password == "zDE9ET!TDq2uZx2oM!FD2"
// bearer:expected javascript_lang_observable_timing
"zDE9ET!TDq2uZx2oM!FD2" == password
// bearer:expected javascript_lang_observable_timing
"zDE9ET!TDq2uZx2oM!FD2" === password
// bearer:expected javascript_lang_observable_timing
password == passwordInput
// bearer:expected javascript_lang_observable_timing
hash === verify.toString("base64")

function isAuthenticated(username, input) {
var user = FetchUserFromDB(username)
if (input.length !== user.token.length) {
return false
}

// bearer:expected javascript_lang_observable_timing
return input === user.token
}

if (password !== null) {
}

// bearer:expected javascript_lang_observable_timing
if (apiToken === "zDE9ET!TDq2uZx2oM!FD2") {
}

if (inputToken.length !== secretToken.length) {
return false
}

0 comments on commit 0135cfc

Please sign in to comment.