Skip to content

Commit 40369a1

Browse files
committed
adding documentation for CWE-798
Signed-off-by: Helge Wehder <[email protected]>
1 parent 928df16 commit 40369a1

File tree

7 files changed

+284
-26
lines changed

7 files changed

+284
-26
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# CWE-798: Use of hardcoded credentials
2+
3+
Ensure that unique keys or secrets can be replaced or rejected at runtime and never hard-code sensitive information, such as passwords, and encryption keys in a component.
4+
5+
User accounts are either for human or machine type of users. Machine users, such as a front end connecting to a backend `SQL`, have it easy to use complexity during identity verification. Hardcoded credentials for machine users are typically caused by a missing strategy or architecture infrastructure to establish trust between components at deployment time. Human users need a level of usability for their identity verification such as a combination of what they have and what they can remember. A human user Identity Management (IDM) system needs to support initial access and users forgetting passphrases or passwords without jeopardizing security.
6+
7+
Examples of hard-coded sensitive information:
8+
9+
* Default usernames with default password
10+
* Database credentials
11+
* API keys, tokens, SSH keys
12+
* Service account credentials or keys used for installation or management.
13+
* Default passwords for administrators after installation.
14+
* Backend IP Addressess
15+
16+
Storing sensitive data as part of a components source code or deliverable package can result in legal consequences governed by:
17+
18+
* Health Insurance Portability and Accountability Act (HIPAA) [US Congress 1996](https://aspe.hhs.gov/reports/health-insurance-portability-accountability-act-1996),
19+
* General Data Protection Regulation (GDPR) [European Parliament 2016](https://gdpr-info.eu/)"
20+
* California Consumer Privacy Act (CCPA) DIVISION 3. OBLIGATIONS 1427 - 3273.16 [CPPA 2025](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150)
21+
22+
Issues with hard-coded sensitive information include:
23+
24+
* Implementation does not scale
25+
* Customers know each others passwords
26+
* Attackers can extract them from packages or byte-code `.pyo` or `.pyc` files
27+
* Hard to replace at runtime.
28+
29+
## Non-Compliant Code Example
30+
31+
The `noncompliant01.py` code `front_end` method simulates our front-end service that wants to connect to a simulated back-end service together with its deployment in one file. A real world example would have each running and delivered on their own. The `TestSimulateDeployingFrontEnd` unit test simulates a deployment of the `front_end`. The implementation of the `front_end` did not consider deployment in separation to delivering the functionality it provides. A real world example would have `front_end` packaging the password and IP information.
32+
33+
[*noncompliant01.py*](noncompliant01.py)
34+
35+
```py
36+
# SPDX-FileCopyrightText: OpenSSF project contributors
37+
# SPDX-License-Identifier: MIT
38+
""" Non-compliant Code Example """
39+
import logging
40+
import unittest
41+
42+
logging.basicConfig(encoding="utf-8", level=logging.DEBUG)
43+
44+
45+
def front_end():
46+
"""Dummy method demonstrating noncompliant implementation"""
47+
# A noncompliant implementation would typically hardcode server_config
48+
# and load it from a project global python file or variable
49+
server_config = {}
50+
server_config["IP"] = "192.168.0.1"
51+
server_config["PORT"] = "192.168.0.1"
52+
server_config["USER"] = "admin"
53+
server_config["PASS"] = "SuperSecret123"
54+
55+
# it would then use the configuration
56+
logging.debug("connecting to server IP %s", server_config["IP"])
57+
logging.debug("connecting to server PORT %s", server_config["IP"])
58+
logging.debug("connecting to server USER %s", server_config["USER"])
59+
logging.debug("connecting to server PASS %s", server_config["PASS"])
60+
61+
62+
class TestSimulateDeployingFrontEnd(unittest.TestCase):
63+
"""
64+
Simulate the deployment starting the front_end to connect
65+
to the backend
66+
"""
67+
68+
def test_front_end(self):
69+
"""Verifiy front_end implementation"""
70+
front_end()
71+
72+
73+
74+
if __name__ == "__main__":
75+
unittest.main()
76+
```
77+
78+
The `noncompliant01.py` example will print the hardcoded connection information and credential information `PASS SuperSecret123` in use.
79+
80+
## Compliant Solution
81+
82+
Create reusable components by separating deployment such as connection information and trust between a deployed front-end and back-end.
83+
84+
|||
85+
|:---|:---|
86+
|__Anti-pattern__|__Recommended pattern__|
87+
|Passwords for machine to machine identity verification|time limited keys or access tokens that are unique per deployment or instances and get assigned at deployment time.|
88+
|Shared usernames|RBAC, ABAC or policy engines|
89+
|Hardcoded `UIDs`, `GIDs`|identity names|
90+
|Hardcoded `IPs` or ports|Rather than hardcoding IP addresses DNS should be properly implemented in the deployment in combination with solutions such as:<br>- `RFC 9250` - [DNS over Dedicated QUIC Connections (ietf.org)](https://datatracker.ietf.org/doc/rfc9250/)<br>- `RFC 7858` - [Specification for DNS over Transport Layer Security (TLS) (ietf.org)](https://datatracker.ietf.org/doc/html/rfc7858)<br>- `RFC 6494` - [Certificate Profile and Certificate Management for SEcure Neighbor Discovery (SEND) (ietf.org) for IPV6](https://datatracker.ietf.org/doc/rfc6494/)<br>- `DNSSEC` [RFC 9364](https://datatracker.ietf.org/doc/html/rfc9364), `RFC 6014`, `5155`, `4641`....<br><br>The order and ways to resolve IPs is configured via `/etc/nsswitch.conf` on most Unix systems.<br><br>Using `mTLS` with a high granularity of machine identities can reduce or remove `DNS` related risks.|
91+
92+
The `compliant01.py` code is using a `config.ini` file that is created by the deployment represented by `TestSimulateDeployingFrontEnd` and provided to the deployed front_end method representing the component we deploy. Using configuration files such as `ini`, `yaml` or `json` allows a language independent solution (`bash` vs `python`). The deployment, represented by `TestSimulateDeployingFrontEnd`, steering these files also secures them by making them read only to a single user via `self.config_file_path.chmod(0o400)`.
93+
94+
*[compliant01.py](compliant01.py):*
95+
96+
```python
97+
""" Compliant Code Example """
98+
# SPDX-FileCopyrightText: OpenSSF project contributors
99+
# SPDX-License-Identifier: MIT
100+
""" Compliant Code Example """
101+
import logging
102+
from pathlib import Path
103+
import unittest
104+
import configparser
105+
106+
logging.basicConfig(encoding="utf-8", level=logging.DEBUG)
107+
108+
109+
def front_end(config_file_path: Path):
110+
"""Dummy method demonstrating noncompliant implementation"""
111+
# A compliant loads connection information from a well protect file
112+
_config = configparser.ConfigParser()
113+
_config.read(config_file_path)
114+
115+
# it would then use the configuration
116+
logging.debug("Loading deployment config %s", config_file_path.absolute())
117+
logging.debug("connecting to server IP %s", _config["SERVER"]["IP"])
118+
logging.debug("connecting to server PORT %s", _config["SERVER"]["PORT"])
119+
logging.debug("connecting to server USER %s", _config["SERVER"]["USER"])
120+
logging.debug("connecting to server pem %s", _config["SERVER"]["CERT_FILE"])
121+
122+
123+
class TestSimulateDeployingFrontEnd(unittest.TestCase):
124+
"""
125+
Simulate the deployment starting the front_end to connect
126+
to the backend
127+
"""
128+
129+
def setUp(self):
130+
131+
config = configparser.ConfigParser()
132+
config["SERVER"] = {
133+
"IP": "192.168.0.1",
134+
"PORT": "8080",
135+
"USER": "admin",
136+
"CERT_FILE": "example.pem",
137+
}
138+
139+
config["LOGGING"] = {
140+
"level": "DEBUG",
141+
}
142+
self.config_file_path = Path("config.ini", exist_ok=True)
143+
with open(self.config_file_path, "w", encoding="utf-8") as config_file:
144+
config.write(config_file)
145+
self.config_file_path.chmod(0o400)
146+
147+
def test_front_end(self):
148+
"""Verify front_end implementation"""
149+
front_end(self.config_file_path)
150+
151+
def tearDown(self):
152+
"""Clean up after us and remove the config file"""
153+
self.config_file_path.unlink()
154+
155+
156+
if __name__ == "__main__":
157+
unittest.main()
158+
```
159+
160+
The `compliant01.py` code avoids using password based authentication in the first place. It prints connection iformation only for convience here and should not be considered in a real world implementation as per [CWE-532: Insertion of Sensitive Information into Log File](https://best.openssf.org/Secure-Coding-Guide-for-Python/CWE-664/CWE-532/) [OSSF 2025].
161+
162+
## Automated Detection
163+
164+
|Tool|Version|Checker|Description|
165+
|:---|:---|:---|:---|
166+
|Bandit|1.7.4 on Python 3.10.4|B105<br>B106<br>B107|[B105: hardcoded_password_string — Bandit documentation](https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html)<br>[B106: hardcoded_password_funcarg — Bandit documentation](https://bandit.readthedocs.io/en/latest/plugins/b106_hardcoded_password_funcarg.html)<br>[B107: hardcoded_password_default — Bandit documentation](https://bandit.readthedocs.io/en/latest/plugins/b107_hardcoded_password_default.html)|
167+
|sonarsource||RSPEC-2068<br>RSPEC-6437|[Python static code analysis: Hard-coded credentials are security-sensitive (sonarsource.com)](https://rules.sonarsource.com/python/RSPEC-2068)<br>[Credentials should not be hard-coded (sonarsource.com)](https://rules.sonarsource.com/python/type/Vulnerability/RSPEC-6437/)|
168+
|codeQL|||[Hard-coded credentials — CodeQL query help documentation (github.com)](https://codeql.github.com/codeql-query-help/python/py-hardcoded-credentials/)|
169+
170+
## Related Guidelines
171+
172+
|||
173+
|:---|:---|
174+
|[MITRE CWE](http://cwe.mitre.org/)|Pillar: [CWE-693: Protection Mechanism Failure (4.12) (mitre.org)](https://cwe.mitre.org/data/definitions/693.html)|
175+
|[MITRE CWE](http://cwe.mitre.org/)|Base: [CWE-798: Use of hardcoded credentials](https://cwe.mitre.org/data/definitions/798.html)|
176+
|[MITRE CWE](http://cwe.mitre.org/)|Variant: [CWE-259: Use of hardcoded password](https://cwe.mitre.org/data/definitions/259.html)|
177+
|[MITRE CWE](http://cwe.mitre.org/)|Variant: [CWE-321: Use of hardcode cryptographic key](https://cwe.mitre.org/data/definitions/321.html)|
178+
|[SEI CERT Oracle Codign Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java)|[MSC03-J: Never hardcode sensitive information](https://wiki.sei.cmu.edu/confluence/display/java/MSC03-J.+Never+hard+code+sensitive+information)|
179+
180+
## Bibliography
181+
182+
|||
183+
|:---|:---|
184+
| [US Congress 1996] | Health Insurance Portability and Accountability Act (HIPAA) [online].Available from: [https://aspe.hhs.gov/reports/health-insurance-portability-accountability-act-1996](https://aspe.hhs.gov/reports/health-insurance-portability-accountability-act-1996) [accessed 27 Februrary 2025]|
185+
| [European Parliament 2016] | General Data Protection Regulation (GDPR) [online]. Available from: [https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150) [accessed 27 Februrary 2025]|
186+
| [CPPA 2025] |DIVISION 3. OBLIGATIONS [1427 - 3273.16] [online]. Available from: [https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150) [accessed 27 Februrary 2025]|
187+
| [OSSF 2025] | CWE-532: Insertion of Sensitive Information into Log File [online]. Available from: [https://best.openssf.org/Secure-Coding-Guide-for-Python/CWE-664/CWE-532/](https://best.openssf.org/Secure-Coding-Guide-for-Python/CWE-664/CWE-532/) [accessed 27 Februrary 2025]|
Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,60 @@
11
# SPDX-FileCopyrightText: OpenSSF project contributors
22
# SPDX-License-Identifier: MIT
3-
""" Compliant Code Example """
4-
import os
5-
print(os.environ["databaseIPAddress"])
3+
"""Compliant Code Example"""
4+
5+
import logging
6+
from pathlib import Path
7+
import unittest
8+
import configparser
9+
10+
logging.basicConfig(encoding="utf-8", level=logging.DEBUG)
11+
12+
13+
def front_end(config_file_path: Path):
14+
"""Dummy method demonstrating noncompliant implementation"""
15+
# A compliant loads connection information from a well protect file
16+
_config = configparser.ConfigParser()
17+
_config.read(config_file_path)
18+
19+
# it would then use the configuration
20+
logging.debug("Loading deployment config %s", config_file_path.absolute())
21+
logging.debug("connecting to server IP %s", _config["SERVER"]["IP"])
22+
logging.debug("connecting to server PORT %s", _config["SERVER"]["PORT"])
23+
logging.debug("connecting to server USER %s", _config["SERVER"]["USER"])
24+
logging.debug("connecting to server pem %s", _config["SERVER"]["CERT_FILE"])
25+
26+
27+
class TestSimulateDeployingFrontEnd(unittest.TestCase):
28+
"""
29+
Simulate the deployment starting the front_end to connect
30+
to the backend
31+
"""
32+
33+
def setUp(self):
34+
config = configparser.ConfigParser()
35+
config["SERVER"] = {
36+
"IP": "192.168.0.1",
37+
"PORT": "8080",
38+
"USER": "admin",
39+
"CERT_FILE": "example.pem",
40+
}
41+
42+
config["LOGGING"] = {
43+
"level": "DEBUG",
44+
}
45+
self.config_file_path = Path("config.ini", exist_ok=True)
46+
with open(self.config_file_path, "w", encoding="utf-8") as config_file:
47+
config.write(config_file)
48+
self.config_file_path.chmod(0o400)
49+
50+
def test_front_end(self):
51+
"""Verify front_end implementation"""
52+
front_end(self.config_file_path)
53+
54+
def tearDown(self):
55+
"""Clean up after us and remove the config file"""
56+
self.config_file_path.unlink()
57+
58+
59+
if __name__ == "__main__":
60+
unittest.main()

docs/Secure-Coding-Guide-for-Python/CWE-693/CWE-798/envars.sh

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,40 @@
11
# SPDX-FileCopyrightText: OpenSSF project contributors
22
# SPDX-License-Identifier: MIT
3-
""" Non-compliant Code Example """
4-
databaseIPAddress = "192.168.0.1"
5-
print(databaseIPAddress)
3+
"""Non-compliant Code Example"""
4+
5+
import logging
6+
import unittest
7+
8+
logging.basicConfig(encoding="utf-8", level=logging.DEBUG)
9+
10+
11+
def front_end():
12+
"""Dummy method demonstrating noncompliant implementation"""
13+
# A noncompliant implementation would typically hardcode server_config
14+
# and load it from a project global python file or variable
15+
server_config = {}
16+
server_config["IP"] = "192.168.0.1"
17+
server_config["PORT"] = "192.168.0.1"
18+
server_config["USER"] = "admin"
19+
server_config["PASS"] = "SuperSecret123"
20+
21+
# it would then use the configuration
22+
logging.debug("connecting to server IP %s", server_config["IP"])
23+
logging.debug("connecting to server PORT %s", server_config["IP"])
24+
logging.debug("connecting to server USER %s", server_config["USER"])
25+
logging.debug("connecting to server PASS %s", server_config["PASS"])
26+
27+
28+
class TestSimulateDeployingFrontEnd(unittest.TestCase):
29+
"""
30+
Simulate the deployment starting the front_end to connect
31+
to the backend
32+
"""
33+
34+
def test_front_end(self):
35+
"""Verifiy front_end implementation"""
36+
front_end()
37+
38+
39+
if __name__ == "__main__":
40+
unittest.main()

docs/Secure-Coding-Guide-for-Python/CWE-693/CWE-798/setup.sh

Lines changed: 0 additions & 9 deletions
This file was deleted.

docs/Secure-Coding-Guide-for-Python/CWE-693/CWE-798/start.sh

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/Secure-Coding-Guide-for-Python/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ It is __not production code__ and requires code-style or python best practices t
7575
|:----------------------------------------------------------------|:----|
7676
|[CWE-184: Incomplete List of Disallowed Input](CWE-693/CWE-184/.)||
7777
|[CWE-330: Use of Insufficiently Random Values](CWE-693/CWE-330/README.md)|[CVE-2020-7548](https://www.cvedetails.com/cve/CVE-2020-7548),<br/>CVSSv3.1: __9.8__,<br/>EPSS: __0.22__ (12.12.2024)|
78-
|[CWE-798: Use of hardcoded credentials](CWE-693/CWE-798/.)||
78+
|[CWE-798: Use of hardcoded credentials](CWE-693/CWE-798/README.md)||
7979

8080
|[CWE-697: Incorrect Comparison](https://cwe.mitre.org/data/definitions/703.html)|Prominent CVE|
8181
|:----------------------------------------------------------------|:----|

0 commit comments

Comments
 (0)