-
-
Notifications
You must be signed in to change notification settings - Fork 468
Add Challenge 58: Database Connection String Exposure #2123
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
Merged
Merged
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
bfe68fb
Initial plan
Copilot 7a4d274
Implement Challenge 57: Database Connection String Exposure through E…
Copilot 372f123
Fix formatting issues in Challenge 57 files
Copilot 3b94656
Merge branch 'master' into copilot/fix-2122
commjoen c9c4725
Merge branch 'master' into copilot/fix-2122
commjoen cc5f015
Fix GitHub Pages workflow artifact naming conflict
Copilot 161cec1
Implement Challenge 58 and update Challenge 57 with complete GitHub P…
Copilot e2449d3
Fix spotless formatting violations and Python script syntax warning
Copilot 9cf7246
Fix MediaType constant issue in Challenge57Controller
Copilot 7252d92
Merge branch 'master' into copilot/fix-2122
commjoen cc8b721
Fix workflow failures: Black formatting and SpotBugs violation
Copilot e4f177c
Fix HTML and adoc formatting issues and add ZAP whitelist rule
Copilot 5b83209
Merge master and resolve conflicts - keep only Challenge 57 (Database…
Copilot d8e2623
Clean up remaining Challenge58 files after merge
Copilot 7bb7d6a
Fix Challenge structure: Challenge 57 as LLM, Challenge 58 as databas…
Copilot 11b2faa
Restore Challenge 57 LLM files from master and fix Challenge 58 forma…
Copilot a0b85b8
Update preview generation script to include Challenge 58 as latest ch…
Copilot 134b5a1
Fix Challenge 58 missing files and Black formatting issues
Copilot 7f05a3e
Merge branch 'master' into copilot/fix-2122
commjoen c907dad
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] 57aef45
Merge branch 'master' into copilot/fix-2122
commjoen e066120
Merge branch 'master' into copilot/fix-2122
commjoen 6abff4f
undo copilot issues: have git ignore removed and add the right snippets
commjoen 57b4655
extend explanation
commjoen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package org.owasp.wrongsecrets.challenges.docker; | ||
|
||
import com.google.common.base.Strings; | ||
import java.sql.Connection; | ||
import java.sql.DriverManager; | ||
import java.sql.SQLException; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.owasp.wrongsecrets.challenges.Challenge; | ||
import org.owasp.wrongsecrets.challenges.Spoiler; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** Challenge demonstrating database connection string exposure through error messages. */ | ||
@Slf4j | ||
@Component | ||
public class Challenge57 implements Challenge { | ||
|
||
// Simulated database connection string with embedded credentials | ||
private static final String DB_CONNECTION_STRING = | ||
"jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true"; | ||
|
||
private static final String EXPECTED_SECRET = "SuperSecretDB2024!"; | ||
|
||
@Override | ||
public Spoiler spoiler() { | ||
return new Spoiler(EXPECTED_SECRET); | ||
} | ||
|
||
@Override | ||
public boolean answerCorrect(String answer) { | ||
return !Strings.isNullOrEmpty(answer) && EXPECTED_SECRET.equals(answer.trim()); | ||
} | ||
|
||
/** | ||
* This method simulates what happens when an application tries to connect to a database but | ||
* fails, exposing the full connection string (including credentials) in error messages. This is a | ||
* common real-world mistake where developers include sensitive information in connection strings | ||
* and don't properly handle/sanitize database connection errors. | ||
*/ | ||
public String simulateDatabaseConnectionError() { | ||
try { | ||
// This will fail since we don't have a real database, but it demonstrates | ||
// how connection errors can expose credentials | ||
Connection conn = DriverManager.getConnection(DB_CONNECTION_STRING); | ||
conn.close(); | ||
return "Connection successful"; | ||
} catch (SQLException e) { | ||
// Poor error handling - exposing the full connection string in the error message | ||
String errorMessage = | ||
"Database connection failed with connection string: " | ||
+ DB_CONNECTION_STRING | ||
+ "\nError: " | ||
+ e.getMessage(); | ||
|
||
// Log the error (another way credentials get exposed) | ||
log.error("Failed to connect to database: {}", errorMessage); | ||
|
||
return errorMessage; | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Controller.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.owasp.wrongsecrets.challenges.docker; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
/** REST controller for Challenge 57 to trigger database connection error. */ | ||
@Slf4j | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class Challenge57Controller { | ||
|
||
private final Challenge57 challenge; | ||
|
||
/** | ||
* Endpoint to trigger a database connection error that exposes connection string with | ||
* credentials. This simulates what happens when applications try to connect to unavailable | ||
* databases. | ||
*/ | ||
@GetMapping("/error-demo/database-connection") | ||
public String triggerDatabaseError() { | ||
log.info("Attempting database connection for Challenge 57..."); | ||
return challenge.simulateDatabaseConnectionError(); | ||
|
||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
=== Database Connection String Exposure in Error Messages | ||
|
||
One of the most common and dangerous ways secrets leak in real-world applications is through database connection strings that contain embedded credentials. When applications fail to connect to databases, they often expose the full connection string (including usernames and passwords) in error messages, logs, or even user-facing interfaces. | ||
|
||
This challenge demonstrates a scenario where a developer: | ||
|
||
1. **Uses embedded credentials in connection strings** instead of external secret management | ||
2. **Has poor error handling** that exposes the full connection string when database connections fail | ||
3. **Logs sensitive information** without sanitizing credentials first | ||
4. **Displays technical details** that could reach monitoring systems, error tracking tools, or even end users | ||
|
||
**Common places where these exposed connection strings appear:** | ||
|
||
- Application startup logs when database is unavailable | ||
- Exception stack traces in monitoring tools like Sentry, Rollbar, or CloudWatch | ||
- Error messages displayed to users during maintenance windows | ||
- CI/CD pipeline logs when deployment health checks fail | ||
- Docker container logs during orchestration failures | ||
|
||
**Real-world examples:** | ||
|
||
- Applications that fail health checks during Kubernetes deployments | ||
- Microservices that can't reach their database during startup | ||
- Database migration scripts that fail with exposed connection details | ||
- Development/testing environments where error verbosity is set too high | ||
|
||
**How to trigger the error:** | ||
|
||
Visit the `/error-demo/database-connection` endpoint to simulate a database connection failure. This endpoint attempts to connect to a database using a connection string with embedded credentials, and when it fails, it exposes the credentials in both the HTTP response and application logs. | ||
|
||
Can you find the database password that gets exposed when the application tries to connect to the database? | ||
|
||
**Hint:** Look for database connection error messages that reveal more than they should. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Try visiting the `/error-demo/database-connection` endpoint to trigger a database connection error. Look at both the HTTP response and the application logs - database connection failures often expose connection strings with embedded credentials in error messages. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
=== What went wrong? | ||
|
||
The application demonstrates several critical security mistakes commonly found in real-world applications: | ||
|
||
**1. Embedded Credentials in Connection Strings** | ||
The database connection string contains the username and password directly embedded: | ||
``` | ||
jdbc:postgresql://db.example.com:5432/userdb?user=dbadmin&password=SuperSecretDB2024!&ssl=true | ||
``` | ||
|
||
**2. Poor Error Handling** | ||
When the database connection fails, the application exposes the entire connection string in the error message, revealing the embedded credentials to anyone who can see the error. | ||
|
||
**3. Sensitive Information in Logs** | ||
The error gets logged with the full connection string, meaning the credentials are now stored in log files that may be: | ||
- Accessible to multiple team members | ||
- Shipped to centralized logging systems | ||
- Stored long-term in log archives | ||
- Exposed through log monitoring tools | ||
|
||
**4. No Secret Sanitization** | ||
The application doesn't sanitize sensitive information before logging or displaying errors. | ||
|
||
**How to fix this:** | ||
|
||
1. **Use external secret management:** Store database credentials in environment variables, secret management systems (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), or configuration files not checked into version control. | ||
|
||
2. **Separate connection parameters:** Keep the connection URL separate from credentials: | ||
``` | ||
DB_HOST=db.example.com | ||
DB_PORT=5432 | ||
DB_NAME=userdb | ||
DB_USER=dbadmin | ||
DB_PASS=SuperSecretDB2024! | ||
``` | ||
|
||
3. **Implement proper error handling:** Sanitize error messages before logging or displaying them. Never include connection strings or other sensitive data in error messages. | ||
|
||
4. **Use connection pooling with secure configuration:** Modern frameworks like Spring Boot support secure configuration of connection pools without exposing credentials. | ||
|
||
5. **Monitor and audit:** Regularly review logs and error messages to ensure sensitive information isn't being inadvertently exposed. | ||
|
||
This type of credential exposure is extremely common in production applications and represents one of the most frequent ways database credentials are accidentally leaked. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57ControllerTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package org.owasp.wrongsecrets.challenges.docker; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.InjectMocks; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class Challenge57ControllerTest { | ||
|
||
@Mock private Challenge57 challenge; | ||
|
||
@InjectMocks private Challenge57Controller controller; | ||
|
||
@Test | ||
void triggerDatabaseErrorShouldReturnErrorMessage() { | ||
// Given | ||
String expectedError = "Database connection failed with connection string: ..."; | ||
org.mockito.Mockito.when(challenge.simulateDatabaseConnectionError()).thenReturn(expectedError); | ||
|
||
// When | ||
String result = controller.triggerDatabaseError(); | ||
|
||
// Then | ||
assertThat(result).isEqualTo(expectedError); | ||
org.mockito.Mockito.verify(challenge).simulateDatabaseConnectionError(); | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
src/test/java/org/owasp/wrongsecrets/challenges/docker/Challenge57Test.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.owasp.wrongsecrets.challenges.docker; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.owasp.wrongsecrets.challenges.Spoiler; | ||
|
||
class Challenge57Test { | ||
|
||
@Test | ||
void spoilerShouldReturnCorrectAnswer() { | ||
var challenge = new Challenge57(); | ||
assertThat(challenge.spoiler()).isEqualTo(new Spoiler("SuperSecretDB2024!")); | ||
} | ||
|
||
@Test | ||
void answerCorrectShouldReturnTrueForCorrectAnswer() { | ||
var challenge = new Challenge57(); | ||
assertThat(challenge.answerCorrect("SuperSecretDB2024!")).isTrue(); | ||
} | ||
|
||
@Test | ||
void answerCorrectShouldReturnFalseForIncorrectAnswer() { | ||
var challenge = new Challenge57(); | ||
assertThat(challenge.answerCorrect("wronganswer")).isFalse(); | ||
assertThat(challenge.answerCorrect("")).isFalse(); | ||
assertThat(challenge.answerCorrect(null)).isFalse(); | ||
} | ||
|
||
@Test | ||
void answerCorrectShouldTrimWhitespace() { | ||
var challenge = new Challenge57(); | ||
assertThat(challenge.answerCorrect(" SuperSecretDB2024! ")).isTrue(); | ||
} | ||
|
||
@Test | ||
void simulateDatabaseConnectionErrorShouldExposeConnectionString() { | ||
var challenge = new Challenge57(); | ||
String errorMessage = challenge.simulateDatabaseConnectionError(); | ||
|
||
// Verify that the error message contains the exposed connection string | ||
assertThat(errorMessage).contains("jdbc:postgresql://db.example.com:5432/userdb"); | ||
assertThat(errorMessage).contains("user=dbadmin"); | ||
assertThat(errorMessage).contains("password=SuperSecretDB2024!"); | ||
assertThat(errorMessage).contains("Database connection failed"); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.