Skip to content

Add Contact Feedback Form with CAPTCHA and Security Vulnerabilities#73

Closed
sylvain-combe-sonarsource wants to merge 1 commit intomainfrom
feature/contact-feedback-form
Closed

Add Contact Feedback Form with CAPTCHA and Security Vulnerabilities#73
sylvain-combe-sonarsource wants to merge 1 commit intomainfrom
feature/contact-feedback-form

Conversation

@sylvain-combe-sonarsource
Copy link
Contributor

Overview

This PR implements a new contact feedback form feature that demonstrates various security vulnerabilities for educational purposes.

Changes Made

New Features

  • ContactFeedbackServlet: A servlet that handles contact feedback form submissions with multiple security vulnerabilities
  • CaptchaServlet: Generates CAPTCHA images using the Kaptcha library
  • contact-feedback.jsp: Modern UI for the feedback form with optional CAPTCHA validation
  • captcha.jsp: Test page for CAPTCHA functionality

Security Vulnerabilities Demonstrated

  1. SQL Injection: Feedback data is stored using string concatenation in SQL queries
  2. Cross-Site Scripting (XSS): User input is reflected in responses without sanitization
  3. Insecure Deserialization: Session-Auth header is deserialized without validation
  4. Information Disclosure: Stack traces are exposed to users in error messages
  5. Weak CAPTCHA Validation:
    • Case-sensitive comparison
    • No timing-safe comparison
    • Can be bypassed by disabling CAPTCHA option

Dependencies

  • Added Kaptcha 2.3.2: An obsolete CAPTCHA library with known CVEs

CI/CD Updates

  • Updated buildspec.yml to run on feature branches
  • Updated GitHub Actions workflow (.github/workflows/maven.yml) to run on all feature/** branches

Build Status

✅ All tests pass
mvn clean verify succeeds

Java Version

Implemented using Java 17 as specified

Testing

The feature can be tested by:

  1. Navigating to /contactFeedback
  2. Filling out the contact form
  3. Testing with and without CAPTCHA enabled
  4. Observing security vulnerabilities in action

- Implement ContactFeedbackServlet with multiple security flows:
  * SQL injection vulnerability in feedback storage
  * XSS vulnerability in reflected user input
  * Deserialization vulnerability with Session-Auth header
  * Information disclosure via stack trace exposure
  * Weak CAPTCHA validation (case-sensitive, no timing-safe comparison)
- Add CaptchaServlet using obsolete Kaptcha 2.3.2 library (with CVEs)
- Create contact-feedback.jsp with modern UI and optional CAPTCHA
- Create captcha.jsp test page
- Add saveFeedback method to DBUtils with SQL injection vulnerability
- Update buildspec.yml to run on feature branches
- Update GitHub Actions workflow to run on feature/** branches
- Use Java 17 as specified

// XSS vulnerability - reflected output without sanitization
out.print("<html><body>");
out.print("<h2>Thank you for your feedback, " + name + "!</h2>");

Check failure

Code scanning / SonarQube

Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks

<!--SONAR_ISSUE_KEY:6bb60f39-8a97-4f8e-82a5-1f09373d380f-->Change this code to not reflect unsanitized user-controlled data. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=6bb60f39-8a97-4f8e-82a5-1f09373d380f&open=6bb60f39-8a97-4f8e-82a5-1f09373d380f">SonarQube</a></p>
// XSS vulnerability - reflected output without sanitization
out.print("<html><body>");
out.print("<h2>Thank you for your feedback, " + name + "!</h2>");
out.print("<p>We received your message about: " + subject + "</p>");

Check failure

Code scanning / SonarQube

Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks

<!--SONAR_ISSUE_KEY:7eb27037-bd70-4d9e-8441-270bd50e988c-->Change this code to not reflect unsanitized user-controlled data. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=7eb27037-bd70-4d9e-8441-270bd50e988c&open=7eb27037-bd70-4d9e-8441-270bd50e988c">SonarQube</a></p>
out.print("<html><body>");
out.print("<h2>Thank you for your feedback, " + name + "!</h2>");
out.print("<p>We received your message about: " + subject + "</p>");
out.print("<p>Your message: " + message + "</p>");

Check failure

Code scanning / SonarQube

Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks

<!--SONAR_ISSUE_KEY:601572cf-0c3c-4c0c-9d7f-b6418325315d-->Change this code to not reflect unsanitized user-controlled data. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=601572cf-0c3c-4c0c-9d7f-b6418325315d&open=601572cf-0c3c-4c0c-9d7f-b6418325315d">SonarQube</a></p>
try {
byte[] decoded = Base64.decodeBase64(sessionAuth);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(decoded));
return (SessionHeader) in.readObject();

Check failure

Code scanning / SonarQube

Deserialization should not be vulnerable to injection attacks

<!--SONAR_ISSUE_KEY:988a1dac-ba08-454a-b727-413b3e954dfd-->Change this code to not deserialize user-controlled data. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=988a1dac-ba08-454a-b727-413b3e954dfd&open=988a1dac-ba08-454a-b727-413b3e954dfd">SonarQube</a></p>
String query = "INSERT INTO feedback (name, email, subject, message) VALUES ('"
+ name + "', '" + email + "', '" + subject + "', '" + message + "')";
Statement statement = connection.createStatement();
statement.executeUpdate(query);

Check failure

Code scanning / SonarQube

Database queries should not be vulnerable to injection attacks

<!--SONAR_ISSUE_KEY:1561659d-046f-422e-accd-e6e56b4841af-->Change this code to not construct SQL queries directly from user-controlled data. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=1561659d-046f-422e-accd-e6e56b4841af&open=1561659d-046f-422e-accd-e6e56b4841af">SonarQube</a></p>
byte[] captchaBytes = outputStream.toByteArray();

ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(captchaBytes);

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods

<!--SONAR_ISSUE_KEY:03f19758-adf6-41fd-883f-6a20221dd2d5-->Handle the following exception that could be thrown by "write": IOException. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=03f19758-adf6-41fd-883f-6a20221dd2d5&open=03f19758-adf6-41fd-883f-6a20221dd2d5">SonarQube</a></p>

ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(captchaBytes);
servletOutputStream.flush();

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods

<!--SONAR_ISSUE_KEY:0bf751a0-b61b-48a7-b08d-fbdd922e7d95-->Handle the following exception that could be thrown by "flush": IOException. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=0bf751a0-b61b-48a7-b08d-fbdd922e7d95&open=0bf751a0-b61b-48a7-b08d-fbdd922e7d95">SonarQube</a></p>
ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(captchaBytes);
servletOutputStream.flush();
servletOutputStream.close();

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods

<!--SONAR_ISSUE_KEY:afff62e3-3a06-4266-990e-163bfbf2c3cf-->Handle the following exception that could be thrown by "close": IOException. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=afff62e3-3a06-4266-990e-163bfbf2c3cf&open=afff62e3-3a06-4266-990e-163bfbf2c3cf">SonarQube</a></p>

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/contact-feedback.jsp").forward(request, response);

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods

<!--SONAR_ISSUE_KEY:675a66bf-0d52-47f1-bbff-cd29c0ccd107-->Handle the following exceptions that could be thrown by "forward": ServletException, IOException. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=675a66bf-0d52-47f1-bbff-cd29c0ccd107&open=675a66bf-0d52-47f1-bbff-cd29c0ccd107">SonarQube</a></p>
String expectedCaptcha = (String) session.getAttribute("KAPTCHA_SESSION_KEY");

response.setContentType("text/html");
PrintWriter out = response.getWriter();

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods

<!--SONAR_ISSUE_KEY:9d401158-1cfe-43b2-821c-5574f50c3922-->Handle the following exception that could be thrown by "getWriter": IOException. <p>See more on <a href="https://nautilus.sonarqube.org/project/issues?id=demo%3Ajava-security&branch=feature%2Fcontact-feedback-form&issues=9d401158-1cfe-43b2-821c-5574f50c3922&open=9d401158-1cfe-43b2-821c-5574f50c3922">SonarQube</a></p>
@sonar-nautilus
Copy link

Quality Gate failed Quality Gate failed

Failed conditions
18 New issues
1 Security Hotspot
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE SonarQube for IDE


// XSS vulnerability - reflected output without sanitization
out.print("<html><body>");
out.print("<h2>Thank you for your feedback, " + name + "!</h2>");

Check failure

Code scanning / SonarQube

Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks Critical

Change this code to not reflect unsanitized user-controlled data. See more on SonarQube
// XSS vulnerability - reflected output without sanitization
out.print("<html><body>");
out.print("<h2>Thank you for your feedback, " + name + "!</h2>");
out.print("<p>We received your message about: " + subject + "</p>");

Check failure

Code scanning / SonarQube

Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks Critical

Change this code to not reflect unsanitized user-controlled data. See more on SonarQube
out.print("<html><body>");
out.print("<h2>Thank you for your feedback, " + name + "!</h2>");
out.print("<p>We received your message about: " + subject + "</p>");
out.print("<p>Your message: " + message + "</p>");

Check failure

Code scanning / SonarQube

Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks Critical

Change this code to not reflect unsanitized user-controlled data. See more on SonarQube
try {
byte[] decoded = Base64.decodeBase64(sessionAuth);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(decoded));
return (SessionHeader) in.readObject();

Check failure

Code scanning / SonarQube

Deserialization should not be vulnerable to injection attacks Critical

Change this code to not deserialize user-controlled data. See more on SonarQube
String query = "INSERT INTO feedback (name, email, subject, message) VALUES ('"
+ name + "', '" + email + "', '" + subject + "', '" + message + "')";
Statement statement = connection.createStatement();
statement.executeUpdate(query);

Check failure

Code scanning / SonarQube

Database queries should not be vulnerable to injection attacks Critical

Change this code to not construct SQL queries directly from user-controlled data. See more on SonarQube
byte[] captchaBytes = outputStream.toByteArray();

ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(captchaBytes);

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods Low

Handle the following exception that could be thrown by "write": IOException. See more on SonarQube

ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(captchaBytes);
servletOutputStream.flush();

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods Low

Handle the following exception that could be thrown by "flush": IOException. See more on SonarQube
ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(captchaBytes);
servletOutputStream.flush();
servletOutputStream.close();

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods Low

Handle the following exception that could be thrown by "close": IOException. See more on SonarQube

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/contact-feedback.jsp").forward(request, response);

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods Low

Handle the following exceptions that could be thrown by "forward": ServletException, IOException. See more on SonarQube
String expectedCaptcha = (String) session.getAttribute("KAPTCHA_SESSION_KEY");

response.setContentType("text/html");
PrintWriter out = response.getWriter();

Check notice

Code scanning / SonarQube

Exceptions should not be thrown from servlet methods Low

Handle the following exception that could be thrown by "getWriter": IOException. See more on SonarQube
@sylvain-combe-sonarsource sylvain-combe-sonarsource deleted the feature/contact-feedback-form branch November 26, 2025 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant