Skip to content

Commit 67489e1

Browse files
authored
CWE-755 and CWE-532 (#647)
* CWE-755 and CWE-732 Signed-off-by: ebakrra <[email protected]> * Updated README.md Added missing links and backticks as per comments Signed-off-by: BartyBoi1128 <[email protected]> * Update README.md Updated compliant01 mistakes, and removed Risk Assessment section Signed-off-by: BartyBoi1128 <[email protected]> * Update README.md Updated references Signed-off-by: BartyBoi1128 <[email protected]> * Update README.md Updated references again. Signed-off-by: BartyBoi1128 <[email protected]> * Update main readme.md to contain links to CWE-532 and CWE-755 Signed-off-by: BartyBoi1128 <[email protected]> * Updated CWE-532 README.md Cosmetic changes and typo fix Signed-off-by: BartyBoi1128 <[email protected]> * Adding licenses on top of compliant01.py Signed-off-by: BartyBoi1128 <[email protected]> * Adding licenses on top of compliant02.py Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of example01.py Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of noncompliant01.py Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of noncompliant02.py Signed-off-by: BartyBoi1128 <[email protected]> * Cosmetic changes to CWE-755 README.md Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of complian01.py (CWE-755) Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of complian02.py (CWE-755) Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of noncompliant01.py (CWE-755) Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of noncompliant02.py (CWE-755) Signed-off-by: BartyBoi1128 <[email protected]> * Added licenses on top of noncompliant03.py (CWE-755) Signed-off-by: BartyBoi1128 <[email protected]> --------- Signed-off-by: ebakrra <[email protected]> Signed-off-by: BartyBoi1128 <[email protected]>
1 parent 1e5fe08 commit 67489e1

File tree

13 files changed

+695
-0
lines changed

13 files changed

+695
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# CWE-532: Insertion of Sensitive Information into Log File
2+
3+
Do not log any sensitive information such as passwords or credit card numbers. Encrypt or anonymize personal information such as user names, and date of birth in plain text.
4+
5+
Logging is crucial for tasks like debugging, forensic analysis, and the general management of various incidents, however, it poses significant challenges when it comes to personal information and sensitive information.
6+
7+
National authorities have fines for data protection violations such as:
8+
9+
* California Consumer Privacy Act (CCPA) USA [CCPA DIVISION 3. OBLIGATIONS [1427 - 3273.16]](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150)
10+
* General Data Protection Regulation (GDPR) [Fines / Penalties - General Data Protection Regulation (GDPR) (gdpr-info.eu)](https://gdpr-info.eu/issues/fines-penalties/)
11+
12+
Cross-border data transfer regulations limit what type of data can be transferred across borders even within a single company or organization such as the [Rules on international data transfers - European Commission (europa.eu)](https://commission.europa.eu/law/law-topic/data-protection/international-dimension-data-protection/rules-international-data-transfers_en)
13+
14+
Software applications often offer different levels of data protection. Certain data, such as access times, can be logged without concern. Other data can be logged but should be accessible only to specific administrators. Sensitive information, such as credit card numbers, should be logged only in encrypted form, while passwords should never be logged. [[SEI CERT 2021 FIO13-J. Do not log sensitive information outside a trust boundary]](https://wiki.sei.cmu.edu/confluence/display/java/FIO13-J.+Do+not+log+sensitive+information+outside+a+trust+boundary)
15+
16+
## Noncompliant Code Example (Logging)
17+
18+
In the `noncompliant01.py` code example the user is asked to input sensitive information during login, and the entered information is logged.
19+
20+
In a real-world scenario, the logging data printed by the `noncompliant01.py` would have to be forwarded to a central logging system.
21+
22+
Storing or displaying a password typically exposes it to a wider audience and must be avoided.
23+
24+
*[noncompliant01.py](noncompliant01.py):*
25+
26+
```py
27+
""" Non-compliant Code Example """
28+
29+
import logging
30+
31+
32+
def login_user(username, password, security_question):
33+
"""Function to login user with username password, and security question"""
34+
logging.info(
35+
"User %s login attempt: password=%s, security answer=%s",
36+
username, password, security_question
37+
)
38+
39+
# Continue to other login functionality
40+
41+
42+
def main():
43+
"""Main function showing login functionality"""
44+
logger = logging.getLogger()
45+
logger.setLevel(logging.INFO)
46+
username = input("Enter your username: ")
47+
password = input("Enter your password: ")
48+
security_question = input("What is the name of your favorite pet?: ")
49+
login_user(username, password, security_question)
50+
51+
52+
main()
53+
```
54+
55+
## Example Solution
56+
57+
The `example01.py` solution uses a custom filter in Python's logging module to automatically mask sensitive data in the logs. While this is a good solution for central handling in large software project it does require that all modules use the same string such as `password=`, variations such as `pass=` or pass: won't work and continue to print the plain text password.
58+
59+
*[example01.py](example01.py):*
60+
61+
```py
62+
""" Code Example """
63+
64+
import logging
65+
import re
66+
67+
68+
class RedactingFilter(logging.Filter):
69+
"""Function to redact any sensitive information from getting logged"""
70+
def filter(self, record):
71+
message = record.getMessage()
72+
73+
# Perform redactions on copy of message
74+
message = re.sub(r"password=\S+", "password=REDACTED", message)
75+
message = re.sub(r"security_question_answer=\S+",
76+
"security_question_answer=REDACTED", message)
77+
78+
record.msg = message
79+
record.args = () # Clear args to prevent further formatting attempts
80+
81+
return True
82+
83+
84+
def login_user(username, password, security_question):
85+
"""Function to login user with username password, and security question"""
86+
logging.info(
87+
"User %s login attempt: password=%s, security_question_answer=%s",
88+
username,
89+
password,
90+
security_question,
91+
)
92+
93+
# Continue to other login functionality
94+
95+
96+
def main():
97+
"""Main function showing login functionality"""
98+
logger = logging.getLogger()
99+
logger.setLevel(logging.INFO)
100+
101+
# Add the custom filter to the logger
102+
redacting_filter = RedactingFilter()
103+
logger.addFilter(redacting_filter)
104+
username = input("Enter your username: ")
105+
password = input("Enter your password: ")
106+
security_question = input("What is the name of your favorite pet?: ")
107+
108+
login_user(username, password, security_question)
109+
110+
111+
main()
112+
```
113+
114+
## Compliant Solution (Logging)
115+
116+
The `compliant01.py` solution doesn't log any sensitive information in the first place, which is a more ideal solution and position to be in. Instead of trying to conceal logged strings at a later stage, control over "classification" is decided during the collection/creation of the data.
117+
118+
*[compliant01.py](compliant01.py):*
119+
120+
```py
121+
""" Compliant Code Example """
122+
123+
import logging
124+
125+
126+
def login_user(username, password, security_question):
127+
"""Function to login user with username password, and security question"""
128+
logging.info("User %s login attempt", username)
129+
130+
# Continue to other login functionality
131+
132+
133+
def main():
134+
"""Main function showing login functionality"""
135+
logger = logging.getLogger()
136+
logger.setLevel(logging.INFO)
137+
username = input("Enter your username: ")
138+
password = input("Enter your password: ")
139+
security_question = input("What is the name of your favorite pet?: ")
140+
login_user(username, password, security_question)
141+
142+
143+
main()
144+
```
145+
146+
## Non-Compliant Code Example (Debugging)
147+
148+
For security purposes, sensitive information should never be printed to the console in log messages (for instance, a passenger's age). In Python's logging module, there are five logging levels:
149+
150+
* `DEBUG`
151+
* `INFO`
152+
* `WARNING` (default)
153+
* `ERROR`
154+
* `CRITICAL`
155+
156+
If we set the level to `DEBUG` or `INFO` we will see the info and debug logs being printed to the console.
157+
The `noncompliant02.py` code has the log level set to DEBUG is causing the customer's address to appear in the console.
158+
159+
*[noncompliant02.py](noncompliant02.py):*
160+
161+
```py
162+
""" Non-compliant Code Example """
163+
164+
import logging
165+
166+
167+
def process_order(address):
168+
"""Function for processing some online order"""
169+
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',
170+
level=logging.DEBUG)
171+
logger = logging.getLogger(__name__)
172+
logger.info("Initial logging level: %s",
173+
logging.getLevelName(logger.getEffectiveLevel()))
174+
logger.debug("Processing order for address: %s", address)
175+
176+
# Continue to process the order.
177+
178+
179+
process_order("10 Grafton Street")
180+
```
181+
182+
## Compliant Solution (Debugging)
183+
184+
In the `compliant02.py` solution we set the log level to INFO , this ensures that only Info logs will get outputted to the console. The log level should only be set to DEBUG when the code is not on a live deployment. This configuration should be managed at the service or deployment level, not within individual code files.
185+
186+
*[compliant02.py](compliant02.py):*
187+
188+
```py
189+
""" Compliant Code Example """
190+
191+
import logging
192+
193+
194+
def process_order(address):
195+
logging.basicConfig(
196+
format="%(asctime)s %(levelname)s:%(message)s", level=logging.INFO
197+
)
198+
logger = logging.getLogger(__name__)
199+
200+
logger.info("Initial logging level: %s", logger.getEffectiveLevel())
201+
202+
logger.debug("Processing order for address: %s", address)
203+
204+
# Continue to process the order.
205+
206+
207+
process_order("10 Grafton Street")
208+
```
209+
210+
## Related Guidelines
211+
212+
|||
213+
|:---|:---|
214+
|[SEI CERT Oracle Coding Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/SEI+CERT+Oracle+Coding+Standard+for+Java?src=breadcrumbs)|[FIO13-J. Do not log sensitive information outside a trust boundary - SEI CERT Oracle Coding Standard for Java](https://wiki.sei.cmu.edu/confluence/display/java/FIO13-J.+Do+not+log+sensitive+information+outside+a+trust+boundary)|
215+
|[MITRE CWE Base](http://cwe.mitre.org/)|[CWE-359](http://cwe.mitre.org/data/definitions/359.html), Privacy Violation|
216+
|[MITRE CWE Base](http://cwe.mitre.org/)|[CWE-532](http://cwe.mitre.org/data/definitions/532.html), Information Exposure through Log Files|
217+
|[MITRE CWE Pillar](http://cwe.mitre.org/)| [CWE-664](https://cwe.mitre.org/data/definitions/664.html), Improper Control of a Resource Through its Lifetime|
218+
|[RFC](https://www.rfc-editor.org/)| [RFC 5424: The Syslog Protocol](https://www.rfc-editor.org/rfc/rfc5424.html)|
219+
220+
## Automated Detection
221+
222+
|Tool|Version|Checker|Description|
223+
|:----|:----|:----|:----|
224+
|[Pylint](https://pylint.pycqa.org/)|2023.10.1|[W1203:logging-fstring-interpolation](https://pylint.readthedocs.io/en/latest/user_guide/messages/warning/logging-fstring-interpolation.html)|Use lazy % formatting in logging functions|
225+
226+
## Biblography
227+
228+
|||
229+
|:---|:---|
230+
|(Python Software Foundation 2023)|[logging - Logging facilty for Python](https://docs.python.org/3/library/logging.html) [accessed 11 May 2023]|
231+
|California Consumer Privacy Act (CCPA) USA|[CCPA DIVISION 3. OBLIGATIONS [1427 - 3273.16]](https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=CIV&sectionNum=1798.150) [accessed 16 October 2024]|
232+
|General Data Protection Regulation (GDPR)|[Fines / Penalties - General Data Protection Regulation (GDPR) (gdpr-info.eu)](https://gdpr-info.eu/issues/fines-penalties/) [accessed 16 October 2024]|
233+
|European Commission International Data Protection|[Rules on international data transfers](https://commission.europa.eu/law/law-topic/data-protection/international-dimension-data-protection/rules-international-data-transfers_en) [accessed 16 October 2024]|
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Compliant Code Example """
4+
5+
import logging
6+
7+
8+
def login_user(username, password, security_question):
9+
"""Function to login user with username password, and security question"""
10+
logging.info("User %s login attempt", username)
11+
12+
# Continue to other login functionality
13+
14+
15+
def main():
16+
"""Main function showing login functionality"""
17+
logger = logging.getLogger()
18+
logger.setLevel(logging.INFO)
19+
username = input("Enter your username: ")
20+
password = input("Enter your password: ")
21+
security_question = input("What is the name of your favorite pet?: ")
22+
login_user(username, password, security_question)
23+
24+
25+
main()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Compliant Code Example """
4+
5+
import logging
6+
7+
8+
def process_order(address):
9+
"""Function for processing some online order"""
10+
logging.basicConfig(
11+
format="%(asctime)s %(levelname)s:%(message)s", level=logging.INFO
12+
)
13+
logger = logging.getLogger(__name__)
14+
15+
logger.info("Initial logging level: %s", logger.getEffectiveLevel())
16+
17+
logger.debug("Processing order for address: %s", address)
18+
19+
# Continue to process the order.
20+
21+
22+
process_order("10 Grafton Street")
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Code Example """
4+
5+
import logging
6+
import re
7+
8+
9+
class RedactingFilter(logging.Filter):
10+
"""Function to redact any sensitive information from getting logged"""
11+
def filter(self, record):
12+
message = record.getMessage()
13+
14+
# Perform redactions on copy of message
15+
message = re.sub(r"password=\S+", "password=REDACTED", message)
16+
message = re.sub(r"security_question_answer=\S+",
17+
"security_question_answer=REDACTED", message)
18+
19+
record.msg = message
20+
record.args = () # Clear args to prevent further formatting attempts
21+
22+
return True
23+
24+
25+
def login_user(username, password, security_question):
26+
"""Function to login user with username password, and security question"""
27+
logging.info(
28+
"User %s login attempt: password=%s, security_question_answer=%s",
29+
username,
30+
password,
31+
security_question,
32+
)
33+
34+
# Continue to other login functionality
35+
36+
37+
def main():
38+
"""Main function showing login functionality"""
39+
logger = logging.getLogger()
40+
logger.setLevel(logging.INFO)
41+
42+
# Add the custom filter to the logger
43+
redacting_filter = RedactingFilter()
44+
logger.addFilter(redacting_filter)
45+
username = input("Enter your username: ")
46+
password = input("Enter your password: ")
47+
security_question = input("What is the name of your favorite pet?: ")
48+
49+
login_user(username, password, security_question)
50+
51+
52+
main()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Non-compliant Code Example """
4+
5+
import logging
6+
7+
8+
def login_user(username, password, security_question):
9+
"""Function to login user with username password, and security question"""
10+
logging.info(
11+
"User %s login attempt: password=%s, security answer=%s",
12+
username, password, security_question
13+
)
14+
15+
# Continue to other login functionality
16+
17+
18+
def main():
19+
"""Main function showing login functionality"""
20+
logger = logging.getLogger()
21+
logger.setLevel(logging.INFO)
22+
username = input("Enter your username: ")
23+
password = input("Enter your password: ")
24+
security_question = input("What is the name of your favorite pet?: ")
25+
login_user(username, password, security_question)
26+
27+
28+
main()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# SPDX-FileCopyrightText: OpenSSF project contributors
2+
# SPDX-License-Identifier: MIT
3+
""" Non-compliant Code Example """
4+
5+
import logging
6+
7+
8+
def process_order(address):
9+
"""Function for processing some online order"""
10+
logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',
11+
level=logging.DEBUG)
12+
logger = logging.getLogger(__name__)
13+
logger.info("Initial logging level: %s",
14+
logging.getLevelName(logger.getEffectiveLevel()))
15+
logger.debug("Processing order for address: %s", address)
16+
17+
# Continue to process the order.
18+
19+
20+
process_order("10 Grafton Street")

0 commit comments

Comments
 (0)