|
| 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§ionNum=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 example is simulating a `front-end`, `back-end`, and its deployment in one file. A real world example would have each run and delivered separately. The `TestSimulateDeployingFrontEnd` unit-test simulates a deployment of the `front_end`. The implementation of the `front_end` did not consider leaving connection details to the deployment and hardcoded them instead. |
| 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 | + |
| 40 | +import logging |
| 41 | +import unittest |
| 42 | + |
| 43 | +logging.basicConfig(encoding="utf-8", level=logging.DEBUG) |
| 44 | + |
| 45 | + |
| 46 | +def front_end(): |
| 47 | + """Dummy method demonstrating noncompliant implementation""" |
| 48 | + # A noncompliant implementation would typically hardcode server_config |
| 49 | + # and load it from a project global python file or variable |
| 50 | + server_config = {} |
| 51 | + server_config["IP"] = "192.168.0.1" |
| 52 | + server_config["PORT"] = "8080" |
| 53 | + server_config["USER"] = "admin" |
| 54 | + server_config["PASS"] = "SuperSecret123" |
| 55 | + |
| 56 | + # It would then use the configuration |
| 57 | + logging.debug("connecting to server IP %s", server_config["IP"]) |
| 58 | + logging.debug("connecting to server PORT %s", server_config["PORT"]) |
| 59 | + logging.debug("connecting to server USER %s", server_config["USER"]) |
| 60 | + logging.debug("connecting to server PASS %s", server_config["PASS"]) |
| 61 | + |
| 62 | + |
| 63 | +class TestSimulateDeployingFrontEnd(unittest.TestCase): |
| 64 | + """ |
| 65 | + Simulate the deployment starting the front_end to connect |
| 66 | + to the backend |
| 67 | + """ |
| 68 | + |
| 69 | + def test_front_end(self): |
| 70 | + """Verifiy front_end implementation""" |
| 71 | + front_end() |
| 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 to decouple connection information. The deployment represented by `TestSimulateDeployingFrontEnd` is now in full control of proving connectivity information to the `front-end` and `back-end`. 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)`. The password based identity verfication is replaced with a certificate based solution. |
| 93 | + |
| 94 | +*[compliant01.py](compliant01.py):* |
| 95 | + |
| 96 | +```python |
| 97 | +# SPDX-FileCopyrightText: OpenSSF project contributors |
| 98 | +# SPDX-License-Identifier: MIT |
| 99 | +"""Compliant Code Example""" |
| 100 | + |
| 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 | + """Simulating front end implementation""" |
| 111 | + # A compliant solution loads connection information from a well-protected 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 | + config = configparser.ConfigParser() |
| 131 | + config["SERVER"] = { |
| 132 | + "IP": "192.168.0.1", |
| 133 | + "PORT": "8080", |
| 134 | + "USER": "admin", |
| 135 | + "CERT_FILE": "example.pem", |
| 136 | + } |
| 137 | + |
| 138 | + config["LOGGING"] = { |
| 139 | + "level": "DEBUG", |
| 140 | + } |
| 141 | + self.config_file_path = Path("config.ini", exist_ok=True) |
| 142 | + with open(self.config_file_path, "w", encoding="utf-8") as config_file: |
| 143 | + config.write(config_file) |
| 144 | + self.config_file_path.chmod(0o400) |
| 145 | + |
| 146 | + def test_front_end(self): |
| 147 | + """Verify front_end implementation""" |
| 148 | + front_end(self.config_file_path) |
| 149 | + |
| 150 | + def tearDown(self): |
| 151 | + """Clean up after us and remove the config file""" |
| 152 | + self.config_file_path.unlink() |
| 153 | + |
| 154 | + |
| 155 | +if __name__ == "__main__": |
| 156 | + unittest.main() |
| 157 | +``` |
| 158 | + |
| 159 | +The `compliant01.py` code avoids using password based authentication in the first place. It prints connection information only for convenience 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]. |
| 160 | + |
| 161 | +## Automated Detection |
| 162 | + |
| 163 | +|Tool|Version|Checker|Description| |
| 164 | +|:---|:---|:---|:---| |
| 165 | +|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)| |
| 166 | +|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/)| |
| 167 | +|codeQL|||[Hard-coded credentials — CodeQL query help documentation (github.com)](https://codeql.github.com/codeql-query-help/python/py-hardcoded-credentials/)| |
| 168 | + |
| 169 | +## Related Guidelines |
| 170 | + |
| 171 | +||| |
| 172 | +|:---|:---| |
| 173 | +|[MITRE CWE](http://cwe.mitre.org/)|Pillar: [CWE-693: Protection Mechanism Failure (4.12) (mitre.org)](https://cwe.mitre.org/data/definitions/693.html)| |
| 174 | +|[MITRE CWE](http://cwe.mitre.org/)|Base: [CWE-798: Use of hardcoded credentials](https://cwe.mitre.org/data/definitions/798.html)| |
| 175 | +|[MITRE CWE](http://cwe.mitre.org/)|Variant: [CWE-259: Use of hardcoded password](https://cwe.mitre.org/data/definitions/259.html)| |
| 176 | +|[MITRE CWE](http://cwe.mitre.org/)|Variant: [CWE-321: Use of hardcode cryptographic key](https://cwe.mitre.org/data/definitions/321.html)| |
| 177 | +|[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)| |
| 178 | + |
| 179 | +## Bibliography |
| 180 | + |
| 181 | +||| |
| 182 | +|:---|:---| |
| 183 | +| [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]| |
| 184 | +| [European Parliament 2016] | General Data Protection Regulation (GDPR) [online]. Available from: [https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV§ionNum=1798.150](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV§ionNum=1798.150) [accessed 27 Februrary 2025]| |
| 185 | +| [CPPA 2025] |DIVISION 3. OBLIGATIONS [1427 - 3273.16] [online]. Available from: [https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV§ionNum=1798.150](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV§ionNum=1798.150) [accessed 27 Februrary 2025]| |
| 186 | +| [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]| |
0 commit comments