Giter Site home page Giter Site logo

sharonkoch / altoroj_demo Goto Github PK

View Code? Open in Web Editor NEW

This project forked from hcl-tech-software/altoroj

1.0 0.0 0.0 4.94 MB

WARNING: This app contains security vulnerabilities. AltoroJ is a sample banking J2EE web application. It shows what happens when web applications are written with consideration of app functionality but not app security. It's a simple and uncluttered platform for demonstrating and learning more about real-life application security issues.

License: Apache License 2.0

JavaScript 7.24% Java 36.49% CSS 14.00% HTML 12.73% Rich Text Format 29.53%

altoroj_demo's Introduction

Description

WARNING: This application contains security vulnerabilities. Run it only in a backed-up and sheltered environment (such as a VM with a recent snapshot and host-only networking) and at your own risk, escpecially if you enable some of the advanced options described below!

AltoroJ is a sample banking J2EE web application. It shows what happens when web applications are written with consideration of app functionality but not app security. It's a simple and uncluttered platform for demonstrating and learning more about real-life application security issues.

AltoroJ uses standard Java & JSP functionality without relying on any additional frameworks. While vast majority of real-life applications do use frameworks, the exact same principles of Application Security apply in both cases. Frameworks can also be hard to understand for someone not familiar with a particular framework and introduce complexities that detract from the overall learning experience. Not to mention, a large number of large and complex "legacy" Java web applications that look very similar to AltoroJ (but are infinitely more complex of course).

AltoroJ uses Apache Derby as its SQL database that is automatically initialized the first time you log into AltoroJ via its web interface. All of the transactions and operations will then be stored in this database from that point on until you delete your repository folder called "altoro" that is located in your OS home folder (e.g. C:\Users[your_username] or /Users/[your_username]) or enable advanced option to re-initialize your database every time your web application server is restarted (see below).

AltoroJ was created in 2008 and has gone through a number of iterations since then. It currently, being used around the world to demonstrate application security vulnerabilities, educate folks on how easy some of these issues are to exploit and how severe the impact may be, and is even a part of academic curricula. Even though AltoroJ is pretty stable, if you do find a bug or create a cool exploit for one of its vulnerabilities - please let us know!!!

Binaries and hosted versions

If you'd like to try AltoroJ but want to skip all of the cool software development stuff, use publicly hosted version, available at http://altoromutual.com:8080/ . You will not be able to enable any of the advanced options and this site may not always be available, but it's the easiest way to get started

Prereqiusites

AltoroJ has been developed using Eclipse and designed to run on Tomcat 7, but since it's a relatively simple J2EE app, it should be pretty easy to port it to a different J2EE IDE or another J2EE web application server. Here are out-of-the-box requirements:

  • Eclipse 4.6 or newer recommended (requires Java 8)
  • Tomcat 7.x
  • Gradle 3.0 to build from command line
  • Gradle's Buildship Eclipse plug-in to automatically download required 3rd party libraries and run AltoroJ inside Eclipse -- Easiest way to install Buildship is from Eclipse Marketplace (inside Eclipse, go to Help -> Eclipse Marketplace)

Read more about importing AltoroJ into Eclipse from GitHub here

AltoroJ credentials

Main usernames and passwords for AltoroJ are as follows:

  • jsmith/demo1234
  • admin/admin

Advanced options

AltoroJ’s original design goals were to create an application that is easy to deploy, very stable and less dangerous (as far as vulnerable web apps go). However, these goals meant that certain attacks couldn’t be a part of it. Because of this, there are advanced user-configurable properties that can enable AltoroJ behaviors which are disabled by default.These enable extra functionality, new cool attacks and demos as well as optional behaviors.

Please see WEB-INF/app.properties file for more information on each property

REST API

AltoroJ has a fairly extensive REST API, which is documented using Swagger. You can find out more about and interact with the provided REST services by clicking on the REST API link in the footer of almost every AltoroJ page.

Troubleshooting

  • Problem: AltoroJ runs, but an error “Failed to create database 'altoro‘” comes up when you try to log in

  • Cause: AltoroJ database does not get created. This is usually caused by folder permission issues on a locked-down system

  • Solution: To make sure this isn’t a fluke, try to log in again using jsmith/demo1234. AltoroJ uses Java’s user.home property as a base directory for its database so this shouldn’t happen. However, if it does. Take a look at your Eclipse Console, or if running directly on Tomcat, open "catalina.out" file from Tomcat’s logs folder in a text editor and look for “user.home=“. This is the folder that AltoroJ is trying to create another folder in and needs write access. You can then: Give the user Tomcat runs under read/write/create access to this folder (recommended) OR modify Tomcat’s startup to include –Duser.home=“<new_path>” in Java arguments to change DB location

  • Problem: AltoroJ does not run on Tomcat due to compilation errors

  • Cause: If you have compilation errors in Eclipse, Java build path is likely to blame

  • Solution: Run AltoroJ's Gradle build in order to download required third party libraries and build AltoroJ

License

All files found in this project are licensed under the Apache License 2.0.

altoroj_demo's People

Contributors

apvk avatar sharonkoch avatar mattmurp avatar websnke avatar mend-for-github-com[bot] avatar

Stargazers

 avatar

altoroj_demo's Issues

Action Required: Fix Mend Configuration File - .mendsastcli-config.json

There is an error with this repository's Mend configuration file that needs to be fixed. As a precaution, scans will stop until it is resolved.

Errors:

  • Incompatible configurations: “ (U+201C LEFT DOUBLE QUOTATION MARK) and ” (U+201D RIGHT DOUBLE QUOTATION MARK) are not allowed in the configuration file. Please use " (U+0022 QUOTATION MARK) instead.

underscore-min-1.7.0.js: 1 vulnerabilities (highest severity is: 7.2)

Vulnerable Library - underscore-min-1.7.0.js

JavaScript's functional programming helper library.

Library home page: https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js

Path to vulnerable library: /WebContent/swagger/lib/underscore-min.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (underscore-min version) Remediation Possible**
CVE-2021-23358 High 7.2 underscore-min-1.7.0.js Direct underscore - 1.12.1,1.13.0-2

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2021-23358

Vulnerable Library - underscore-min-1.7.0.js

JavaScript's functional programming helper library.

Library home page: https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js

Path to vulnerable library: /WebContent/swagger/lib/underscore-min.js

Dependency Hierarchy:

  • underscore-min-1.7.0.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

The package underscore from 1.13.0-0 and before 1.13.0-2, from 1.3.2 and before 1.12.1 are vulnerable to Arbitrary Code Injection via the template function, particularly when a variable property is passed as an argument as it is not sanitized.

Publish Date: 2021-03-29

URL: CVE-2021-23358

CVSS 3 Score Details (7.2)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: High
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: High
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-23358

Release Date: 2021-03-29

Fix Resolution: underscore - 1.12.1,1.13.0-2

jquery.ba-bbq-1.2.1.min.js: 1 vulnerabilities (highest severity is: 8.8)

Vulnerable Library - jquery.ba-bbq-1.2.1.min.js

jQuery BBQ leverages the HTML5 hashchange event to allow simple, yet powerful bookmarkable #hash history. In addition, jQuery BBQ provides a full .deparam() method, along with both hash state management, and fragment / query string parse and merge utility methods

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery.ba-bbq/1.2.1/jquery.ba-bbq.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery.ba-bbq.min.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (jquery.ba-bbq version) Remediation Possible**
CVE-2021-20086 High 8.8 jquery.ba-bbq-1.2.1.min.js Direct N/A

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2021-20086

Vulnerable Library - jquery.ba-bbq-1.2.1.min.js

jQuery BBQ leverages the HTML5 hashchange event to allow simple, yet powerful bookmarkable #hash history. In addition, jQuery BBQ provides a full .deparam() method, along with both hash state management, and fragment / query string parse and merge utility methods

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery.ba-bbq/1.2.1/jquery.ba-bbq.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery.ba-bbq.min.js

Dependency Hierarchy:

  • jquery.ba-bbq-1.2.1.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') in jquery-bbq 1.2.1 allows a malicious user to inject properties into Object.prototype.

Publish Date: 2021-04-23

URL: CVE-2021-20086

CVSS 3 Score Details (8.8)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: Low
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: High
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

swfobject-2.2.js: 1 vulnerabilities (highest severity is: 9.8)

Vulnerable Library - swfobject-2.2.js

SWFObject is an easy-to-use and standards-friendly method to embed Flash content, which utilizes one small JavaScript file

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swfobject/2.2/swfobject.js

Path to dependency file: /WebContent/bank/stocks.jsp

Path to vulnerable library: /WebContent/util/swfobject.js,/WebContent/bank/../util/swfobject.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (swfobject version) Remediation Possible**
CVE-2012-2400 Critical 9.8 swfobject-2.2.js Direct 3.3.2

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2012-2400

Vulnerable Library - swfobject-2.2.js

SWFObject is an easy-to-use and standards-friendly method to embed Flash content, which utilizes one small JavaScript file

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swfobject/2.2/swfobject.js

Path to dependency file: /WebContent/bank/stocks.jsp

Path to vulnerable library: /WebContent/util/swfobject.js,/WebContent/bank/../util/swfobject.js

Dependency Hierarchy:

  • swfobject-2.2.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Unspecified vulnerability in wp-includes/js/swfobject.js in WordPress before 3.3.2 has unknown impact and attack vectors.

Publish Date: 2012-04-21

URL: CVE-2012-2400

CVSS 3 Score Details (9.8)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: High
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2400

Release Date: 2012-04-21

Fix Resolution: 3.3.2

swagger-ui-3.19.3.js: 1 vulnerabilities (highest severity is: 4.3)

Vulnerable Library - swagger-ui-3.19.3.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.19.3/swagger-ui.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (swagger-ui version) Remediation Possible**
CVE-2018-25031 Medium 4.3 swagger-ui-3.19.3.js Direct swagger-ui - 4.1.3;swagger-ui-dist - 4.1.3

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2018-25031

Vulnerable Library - swagger-ui-3.19.3.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.19.3/swagger-ui.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.js

Dependency Hierarchy:

  • swagger-ui-3.19.3.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Swagger UI before 4.1.3 could allow a remote attacker to conduct spoofing attacks. By persuading a victim to open a crafted URL, an attacker could exploit this vulnerability to display remote OpenAPI definitions.
Mend Note: Converted from WS-2021-0461, on 2022-12-21.

Publish Date: 2022-03-11

URL: CVE-2018-25031

CVSS 3 Score Details (4.3)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: None
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: GHSA-qrmm-w75w-3wpx

Release Date: 2022-03-11

Fix Resolution: swagger-ui - 4.1.3;swagger-ui-dist - 4.1.3

handlebars-2.0.0.min.js: 8 vulnerabilities (highest severity is: 9.8)

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (handlebars version) Remediation Possible**
CVE-2021-23383 Critical 9.8 handlebars-2.0.0.min.js Direct handlebars - 4.7.7
CVE-2021-23369 Critical 9.8 handlebars-2.0.0.min.js Direct com.github.jknack:handlebars:4.2.0, handlebars - 4.7.7
CVE-2019-19919 Critical 9.8 handlebars-2.0.0.min.js Direct handlebars - 3.0.8,4.3.0
CVE-2019-20920 High 8.1 handlebars-2.0.0.min.js Direct handlebars - 4.5.3
WS-2020-0450 High 7.5 handlebars-2.0.0.min.js Direct handlebars - 4.6.0
WS-2019-0064 High 7.3 handlebars-2.0.0.min.js Direct 3.0.7,4.0.14,4.1.2
CVE-2015-8861 Medium 6.1 handlebars-2.0.0.min.js Direct 4.0.0
WS-2019-0103 Medium 5.6 handlebars-2.0.0.min.js Direct 4.1.0

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2021-23383

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

The package handlebars before 4.7.7 are vulnerable to Prototype Pollution when selecting certain compiling options to compile templates coming from an untrusted source.

Publish Date: 2021-05-04

URL: CVE-2021-23383

CVSS 3 Score Details (9.8)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: High
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-23383

Release Date: 2021-05-04

Fix Resolution: handlebars - 4.7.7

CVE-2021-23369

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

The package handlebars before 4.7.7 are vulnerable to Remote Code Execution (RCE) when selecting certain compiling options to compile templates coming from an untrusted source.

Publish Date: 2021-04-12

URL: CVE-2021-23369

CVSS 3 Score Details (9.8)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: High
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Release Date: 2021-04-12

Fix Resolution: com.github.jknack:handlebars:4.2.0, handlebars - 4.7.7

CVE-2019-19919

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Versions of handlebars prior to 4.3.0 are vulnerable to Prototype Pollution leading to Remote Code Execution. Templates may alter an Object's proto and defineGetter properties, which may allow an attacker to execute arbitrary code through crafted payloads.
Mend Note: Converted from WS-2019-0368, on 2022-11-08.

Publish Date: 2019-12-20

URL: CVE-2019-19919

CVSS 3 Score Details (9.8)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: High
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: GHSA-w457-6q6x-cgp9

Release Date: 2019-12-20

Fix Resolution: handlebars - 3.0.8,4.3.0

CVE-2019-20920

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Handlebars before 3.0.8 and 4.x before 4.5.3 is vulnerable to Arbitrary Code Execution. The lookup helper fails to properly validate templates, allowing attackers to submit templates that execute arbitrary JavaScript. This can be used to run arbitrary code on a server processing Handlebars templates or in a victim's browser (effectively serving as XSS).

Publish Date: 2020-09-30

URL: CVE-2019-20920

CVSS 3 Score Details (8.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: High
    • Privileges Required: None
    • User Interaction: None
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: High
    • Integrity Impact: Low
    • Availability Impact: Low

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://www.npmjs.com/advisories/1316

Release Date: 2020-10-15

Fix Resolution: handlebars - 4.5.3

WS-2020-0450

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Handlebars before 4.6.0 vulnerable to Prototype Pollution. Prototype access to the template engine allows for potential code execution, which may lead to Denial Of Service (DoS).

Publish Date: 2020-01-09

URL: WS-2020-0450

CVSS 3 Score Details (7.5)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: None
    • Integrity Impact: None
    • Availability Impact: High

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Release Date: 2020-01-09

Fix Resolution: handlebars - 4.6.0

WS-2019-0064

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Versions of handlebars prior to 4.0.14 are vulnerable to Prototype Pollution. Templates may alter an Objects' prototype, thus allowing an attacker to execute arbitrary code on the server.

Publish Date: 2019-01-30

URL: WS-2019-0064

CVSS 3 Score Details (7.3)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: Low

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://www.npmjs.com/advisories/755/

Release Date: 2019-01-30

Fix Resolution: 3.0.7,4.0.14,4.1.2

CVE-2015-8861

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

The handlebars package before 4.0.0 for Node.js allows remote attackers to conduct cross-site scripting (XSS) attacks by leveraging a template with an attribute that is not quoted.

Publish Date: 2017-01-23

URL: CVE-2015-8861

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://www.npmjs.com/advisories/61

Release Date: 2017-01-23

Fix Resolution: 4.0.0

WS-2019-0103

Vulnerable Library - handlebars-2.0.0.min.js

Handlebars provides the power necessary to let you build semantic templates effectively with no frustration

Library home page: https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js

Path to vulnerable library: /WebContent/swagger/lib/handlebars-2.0.0.js

Dependency Hierarchy:

  • handlebars-2.0.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Handlebars.js before 4.1.0 has Remote Code Execution (RCE)

Publish Date: 2019-01-30

URL: WS-2019-0103

CVSS 3 Score Details (5.6)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: High
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: Low

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Release Date: 2019-01-30

Fix Resolution: 4.1.0

Code Security Report: 24 high severity findings, 41 total findings

Code Security Report

Scan Metadata

Latest Scan: 2024-04-30 04:19am
Total Findings: 41 | New Findings: 41 | Resolved Findings: 41
Tested Project Files: 135
Detected Programming Languages: 2 (JavaScript / Node.js, Java*)

  • Check this box to manually trigger a scan

Most Relevant Findings

The list below presents the 10 most relevant findings that need your attention. To view information on the remaining findings, navigate to the Mend Application.

Automatic Remediation Available (6)

SeverityVulnerability TypeCWEFileData FlowsDate
HighSQL Injection

CWE-89

DBUtil.java:219

32024-04-30 04:20am
Vulnerable Code

return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */

3 Data Flow/s detected
View Data Flow 1

String passwd = request.getParameter("passwd");

if (DBUtil.isValidUser(user.getUsername(), passwd.trim())) {

public static boolean isValidUser(String user, String password) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */

View Data Flow 2

String password = request.getParameter("passw");

password = password.trim().toLowerCase(); //in real life the password usually is case sensitive and this cast would not be done

if (!DBUtil.isValidUser(username, password)){

public static boolean isValidUser(String user, String password) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */

View Data Flow 3

public Response login(String bodyJSON, @Context HttpServletRequest request) throws JSONException {

password = myJson.get("password").toString().toLowerCase();

if (!DBUtil.isValidUser(username, password)) {

public static boolean isValidUser(String user, String password) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */

⛑️ Remediation Suggestion

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
@@ -39,0 +39,1 @@
+import java.sql.PreparedStatement;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
@@ -207,0 +208,1 @@
+
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
@@ -217,1 +219,5 @@
- Statement statement = connection.createStatement();
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = ? AND PASSWORD = ?"
+ );
+ preparedStatement.setString(1, user);
+ preparedStatement.setString(2, password);
@@ -219,1 +225,1 @@
- ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
+ ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
@@ -232,0 +238,2 @@
+
+
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request
Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

 
HighSQL Injection

CWE-89

DBUtil.java:519

22024-04-30 04:20am
Vulnerable Code

public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);

2 Data Flow/s detected
View Data Flow 1

String feedbackId = OperationsUtil.sendFeedback(name, email, subject, comments);

public static String sendFeedback(String name, String email,

long id = DBUtil.storeFeedback(name, email, subject, comments);

public static long storeFeedback(String name, String email, String subject, String comments) {

statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);

View Data Flow 2

public Response sendFeedback(String bodyJSON, @Context HttpServletRequest request) throws JSONException{

String feedbackId = OperationsUtil.sendFeedback(name, email, subject, comments);

public static String sendFeedback(String name, String email,

long id = DBUtil.storeFeedback(name, email, subject, comments);

public static long storeFeedback(String name, String email, String subject, String comments) {

statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);

⛑️ Remediation Suggestion

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
@@ -39,0 +39,1 @@
+import java.sql.PreparedStatement;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
@@ -507,0 +508,1 @@
+
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
@@ -518,3 +520,10 @@
- Statement statement = connection.createStatement();
- statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
- ResultSet rs= statement.getGeneratedKeys();
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES (?, ?, ?, ?)",
+ Statement.RETURN_GENERATED_KEYS
+ );
+ preparedStatement.setString(1, name);
+ preparedStatement.setString(2, email);
+ preparedStatement.setString(3, subject);
+ preparedStatement.setString(4, comments);
+ preparedStatement.executeUpdate();
+ ResultSet rs= preparedStatement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request
Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

 
HighSQL Injection

CWE-89

DBUtil.java:494

22024-04-30 04:20am
Vulnerable Code

public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");

2 Data Flow/s detected
View Data Flow 1

String firstname = request.getParameter("firstname");

String error = DBUtil.addUser(username, password1, firstname, lastname);

public static String addUser(String username, String password, String firstname, String lastname) {

statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");

View Data Flow 2

public Response addUser(String bodyJSON, @Context HttpServletRequest request) throws IOException{

username = bodyJson.get("username").toString();

error = DBUtil.addUser(username, password1, firstname, lastname);

public static String addUser(String username, String password, String firstname, String lastname) {

statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");

⛑️ Remediation Suggestion

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
@@ -39,0 +39,1 @@
+import java.sql.PreparedStatement;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
@@ -482,1 +483,9 @@
- statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
+
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES (?, ?, ?, ?, 'user')"
+ );
+ preparedStatement.setString(1, username);
+ preparedStatement.setString(2, password);
+ preparedStatement.setString(3, firstname);
+ preparedStatement.setString(4, lastname);
+ preparedStatement.executeUpdate();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
@@ -493,2 +502,8 @@
- Statement statement = connection.createStatement();
- statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES (?, ?, ?, ?, 'user')"
+ );
+ preparedStatement.setString(1, username);
+ preparedStatement.setString(2, password);
+ preparedStatement.setString(3, firstname);
+ preparedStatement.setString(4, lastname);
+ preparedStatement.executeUpdate();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
@@ -505,2 +520,8 @@
- Statement statement = connection.createStatement();
- statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "UPDATE PEOPLE SET PASSWORD = ? WHERE USER_ID = ?"
+ );
+ preparedStatement.setString(1, password);
+ preparedStatement.setString(2, username);
+ preparedStatement.executeUpdate();
+
+
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
@@ -22,0 +22,1 @@
+import java.sql.PreparedStatement;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
@@ -493,2 +494,5 @@
- Statement statement = connection.createStatement();
- statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
+ PreparedStatement statement = connection
+ .prepareStatement("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"
+ + username + "','" + password + "', " + "?" + ", '" + lastname + "','user')");
+ statement.setString(1, firstname);
+ statement.execute();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request
Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

 
HighSQL Injection

CWE-89

DBUtil.java:506

22024-04-30 04:20am
Vulnerable Code

public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");

2 Data Flow/s detected
View Data Flow 1

String password1 = request.getParameter("password1");

String error = DBUtil.changePassword(username, password1);

public static String changePassword(String username, String password) {

statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");

View Data Flow 2

public Response changePassword(String bodyJSON, @Context HttpServletRequest request) throws IOException{

username = bodyJson.get("username").toString();

error = DBUtil.changePassword(username, password1);

public static String changePassword(String username, String password) {

statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");

⛑️ Remediation Suggestion

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
@@ -39,0 +39,1 @@
+import java.sql.PreparedStatement;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
@@ -494,1 +495,9 @@
- statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
+
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES (?, ?, ?, ?, 'user')"
+ );
+ preparedStatement.setString(1, username);
+ preparedStatement.setString(2, password);
+ preparedStatement.setString(3, firstname);
+ preparedStatement.setString(4, lastname);
+ preparedStatement.execute();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
@@ -505,2 +514,6 @@
- Statement statement = connection.createStatement();
- statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "UPDATE PEOPLE SET PASSWORD = ? WHERE USER_ID = ?"
+ );
+ preparedStatement.setString(1, password);
+ preparedStatement.setString(2, username);
+ preparedStatement.execute();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
@@ -518,1 +531,18 @@
- Statement statement = connection.createStatement();
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO FEEDBACK (NAME, EMAIL, SUBJECT, COMMENTS) VALUES (?, ?, ?, ?)"
+ );
+ preparedStatement.setString(1, name);
+ preparedStatement.setString(2, email);
+ preparedStatement.setString(3, subject);
+ preparedStatement.setString(4, comments);
+ preparedStatement.execute();
+ ResultSet resultSet = preparedStatement.getGeneratedKeys();
+ if (resultSet.next())
+ return resultSet.getLong(1);
+ else
+ return -1;
+ } catch (SQLException e){
+ return -1;
+ }
+ }
+
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
@@ -22,0 +22,1 @@
+import java.sql.PreparedStatement;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
@@ -505,2 +506,4 @@
- Statement statement = connection.createStatement();
- statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
+ PreparedStatement statement = connection
+ .prepareStatement("UPDATE PEOPLE SET PASSWORD = " + "?" + " WHERE USER_ID = '" + username + "'");
+ statement.setString(1, password);
+ statement.execute();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request
Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

 
HighSQL Injection

CWE-89

DBUtil.java:471

12024-04-30 04:20am
Vulnerable Code

public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");

1 Data Flow/s detected

String acctType = request.getParameter("accttypes");

String error = DBUtil.addAccount(username, acctType);

public static String addAccount(String username, String acctType) {

statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");

⛑️ Remediation Suggestion

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
@@ -39,0 +39,1 @@
+import java.sql.PreparedStatement;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
@@ -459,0 +460,1 @@
+
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
@@ -470,2 +472,6 @@
- Statement statement = connection.createStatement();
- statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES (?, ?, 0)"
+ );
+ preparedStatement.setString(1, username);
+ preparedStatement.setString(2, acctType);
+ preparedStatement.executeUpdate();
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
@@ -481,2 +487,8 @@
- Statement statement = connection.createStatement();
- statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES (?, ?, ?, ?, 'user')"
+ );
+ preparedStatement.setString(1, username);
+ preparedStatement.setString(2, password);
+ preparedStatement.setString(3, firstname);
+ preparedStatement.setString(4, lastname);
+ preparedStatement.executeUpdate();
return null;
@@ -484,0 +496,2 @@
+
+
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request
Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

 
HighSQL Injection

CWE-89

DBUtil.java:242

22024-04-30 04:20am
Vulnerable Code

if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */

2 Data Flow/s detected
View Data Flow 1

Cookie accountCookie = ServletUtil.establishSession(username,session);

public static Cookie establishSession(String username, HttpSession session){

User user = DBUtil.getUserInfo(username);

public static User getUserInfo(String username) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */

View Data Flow 2

String accessToken = request.getHeader("Authorization").replaceAll("Bearer ", "");

String decodedToken = new String(Base64.decodeBase64(accessToken));

StringTokenizer tokenizer = new StringTokenizer(decodedToken,":");

String username = new String(Base64.decodeBase64(tokenizer.nextToken()));

public static User getUserInfo(String username) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */

⛑️ Remediation Suggestion

--- original
+++ remediated
@@ -0,0 +0,0 @@
/**
This application is for demonstration use only. It contains known application security
vulnerabilities that were created expressly for demonstrating the functionality of
application security testing tools. These vulnerabilities may present risks to the
technical environment in which the application is installed. You must delete and
uninstall this demonstration application upon completion of the demonstration for
which it is intended.
IBM DISCLAIMS ALL LIABILITY OF ANY KIND RESULTING FROM YOUR USE OF THE APPLICATION
OR YOUR FAILURE TO DELETE THE APPLICATION FROM YOUR ENVIRONMENT UPON COMPLETION OF
A DEMONSTRATION. IT IS YOUR RESPONSIBILITY TO DETERMINE IF THE PROGRAM IS APPROPRIATE
OR SAFE FOR YOUR TECHNICAL ENVIRONMENT. NEVER INSTALL THE APPLICATION IN A PRODUCTION
ENVIRONMENT. YOU ACKNOWLEDGE AND ACCEPT ALL RISKS ASSOCIATED WITH THE USE OF THE APPLICATION.
IBM AltoroJ
(c) Copyright IBM Corp. 2008, 2013 All Rights Reserved.
*/
package com.ibm.security.appscan.altoromutual.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.ibm.security.appscan.Log4AltoroJ;
import com.ibm.security.appscan.altoromutual.model.Account;
import com.ibm.security.appscan.altoromutual.model.Feedback;
import com.ibm.security.appscan.altoromutual.model.Transaction;
import com.ibm.security.appscan.altoromutual.model.User;
import com.ibm.security.appscan.altoromutual.model.User.Role;
@@ -39,0 +39,1 @@
+import java.sql.PreparedStatement;
/**
* Utility class for database operations
* @author Alexei
*
*/
public class DBUtil {
private static final String PROTOCOL = "jdbc:derby:";
private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
public static final String CREDIT_CARD_ACCOUNT_NAME = "Credit Card";
public static final String CHECKING_ACCOUNT_NAME = "Checking";
public static final String SAVINGS_ACCOUNT_NAME = "Savings";
public static final double CASH_ADVANCE_FEE = 2.50;
private static DBUtil instance = null;
private Connection connection = null;
private DataSource dataSource = null;
//private constructor
private DBUtil(){
/*
**
** Default location for the database is current directory:
** System.out.println(System.getProperty("user.home"));
** to change DB location, set derby.system.home property:
** System.setProperty("derby.system.home", "[new_DB_location]");
**
*/
String dataSourceName = ServletUtil.getAppProperty("database.alternateDataSource");
/* Connect to an external database (e.g. DB2) */
if (dataSourceName != null && dataSourceName.trim().length() > 0){
try {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
dataSource = (DataSource)environmentContext.lookup(dataSourceName.trim());
} catch (Exception e) {
e.printStackTrace();
Log4AltoroJ.getInstance().logError(e.getMessage());
}
/* Initialize connection to the integrated Apache Derby DB*/
} else {
System.setProperty("derby.system.home", System.getProperty("user.home")+"/altoro/");
System.out.println("Derby Home=" + System.getProperty("derby.system.home"));
try {
//load JDBC driver
Class.forName(DRIVER).newInstance();
} catch (Exception e) {
Log4AltoroJ.getInstance().logError(e.getMessage());
e.printStackTrace();
}
}
}
private static Connection getConnection() throws SQLException{
if (instance == null)
instance = new DBUtil();
if (instance.connection == null || instance.connection.isClosed()){
//If there is a custom data source configured use it to initialize
if (instance.dataSource != null){
instance.connection = instance.dataSource.getConnection();
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
return instance.connection;
}
// otherwise initialize connection to the built-in Derby database
try {
//attempt to connect to the database
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro");
if (ServletUtil.isAppPropertyTrue("database.reinitializeOnStart")){
instance.initDB();
}
} catch (SQLException e){
//if database does not exist, create it an initialize it
if (e.getErrorCode() == 40000){
instance.connection = DriverManager.getConnection(PROTOCOL+"altoro;create=true");
instance.initDB();
//otherwise pass along the exception
} else {
throw e;
}
}
}
return instance.connection;
}
/*
* Create and initialize the database
*/
private void initDB() throws SQLException{
Statement statement = connection.createStatement();
try {
statement.execute("DROP TABLE PEOPLE");
statement.execute("DROP TABLE ACCOUNTS");
statement.execute("DROP TABLE TRANSACTIONS");
statement.execute("DROP TABLE FEEDBACK");
} catch (SQLException e) {
// not a problem
}
statement.execute("CREATE TABLE PEOPLE (USER_ID VARCHAR(50) NOT NULL, PASSWORD VARCHAR(20) NOT NULL, FIRST_NAME VARCHAR(100) NOT NULL, LAST_NAME VARCHAR(100) NOT NULL, ROLE VARCHAR(50) NOT NULL, PRIMARY KEY (USER_ID))");
statement.execute("CREATE TABLE FEEDBACK (FEEDBACK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1022, INCREMENT BY 1), NAME VARCHAR(100) NOT NULL, EMAIL VARCHAR(50) NOT NULL, SUBJECT VARCHAR(100) NOT NULL, COMMENTS VARCHAR(500) NOT NULL, PRIMARY KEY (FEEDBACK_ID))");
statement.execute("CREATE TABLE ACCOUNTS (ACCOUNT_ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 800000, INCREMENT BY 1), USERID VARCHAR(50) NOT NULL, ACCOUNT_NAME VARCHAR(100) NOT NULL, BALANCE DOUBLE NOT NULL, PRIMARY KEY (ACCOUNT_ID))");
statement.execute("CREATE TABLE TRANSACTIONS (TRANSACTION_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 2311, INCREMENT BY 1), ACCOUNTID BIGINT NOT NULL, DATE TIMESTAMP NOT NULL, TYPE VARCHAR(100) NOT NULL, AMOUNT DOUBLE NOT NULL, PRIMARY KEY (TRANSACTION_ID))");
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('admin', 'admin', 'Admin', 'User','admin'), ('jsmith','demo1234', 'John', 'Smith','user'),('jdoe','demo1234', 'Jane', 'Doe','user'),('sspeed','demo1234', 'Sam', 'Speed','user'),('tuser','tuser','Test', 'User','user')");
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('admin','Corporate', 52394783.61), ('admin','"+CHECKING_ACCOUNT_NAME+"', 93820.44), ('jsmith','"+SAVINGS_ACCOUNT_NAME+"', 10000.42), ('jsmith','"+CHECKING_ACCOUNT_NAME+"', 15000.39), ('jdoe','"+SAVINGS_ACCOUNT_NAME+"', 10.00), ('jdoe','"+CHECKING_ACCOUNT_NAME+"', 25.00), ('sspeed','"+SAVINGS_ACCOUNT_NAME+"', 59102.00), ('sspeed','"+CHECKING_ACCOUNT_NAME+"', 150.00)");
statement.execute("INSERT INTO ACCOUNTS (ACCOUNT_ID,USERID,ACCOUNT_NAME,BALANCE) VALUES (4539082039396288,'jsmith','"+CREDIT_CARD_ACCOUNT_NAME+"', 100.42),(4485983356242217,'jdoe','"+CREDIT_CARD_ACCOUNT_NAME+"', 10000.97)");
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID,DATE,TYPE,AMOUNT) VALUES (800003,'2017-03-19 15:02:19.47','Withdrawal', -100.72), (800002,'2017-03-19 15:02:19.47','Deposit', 100.72), (800003,'2018-03-19 11:33:19.21','Withdrawal', -1100.00), (800002,'2018-03-19 11:33:19.21','Deposit', 1100.00), (800003,'2018-03-19 18:00:00.33','Withdrawal', -600.88), (800002,'2018-03-19 18:00:00.33','Deposit', 600.88), (800002,'2019-03-07 04:22:19.22','Withdrawal', -400.00), (800003,'2019-03-07 04:22:19.22','Deposit', 400.00), (800002,'2019-03-08 09:00:00.22','Withdrawal', -100.00), (800003,'2019-03-08 09:22:00.22','Deposit', 100.00), (800002,'2019-03-11 16:00:00.10','Withdrawal', -400.00), (800003,'2019-03-11 16:00:00.10','Deposit', 400.00), (800005,'2018-01-10 15:02:19.47','Withdrawal', -100.00), (800004,'2018-01-10 15:02:19.47','Deposit', 100.00), (800004,'2018-04-14 04:22:19.22','Withdrawal', -10.00), (800005,'2018-04-14 04:22:19.22','Deposit', 10.00), (800004,'2018-05-15 09:00:00.22','Withdrawal', -10.00), (800005,'2018-05-15 09:22:00.22','Deposit', 10.00), (800004,'2018-06-11 11:01:30.10','Withdrawal', -10.00), (800005,'2018-06-11 11:01:30.10','Deposit', 10.00)");
Log4AltoroJ.getInstance().logInfo("Database initialized");
}
/**
* Retrieve feedback details
* @param feedbackId specific feedback ID to retrieve or Feedback.FEEDBACK_ALL to retrieve all stored feedback submissions
*/
public static ArrayList<Feedback> getFeedback (long feedbackId){
ArrayList<Feedback> feedbackList = new ArrayList<Feedback>();
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
String query = "SELECT * FROM FEEDBACK";
if (feedbackId != Feedback.FEEDBACK_ALL){
query = query + " WHERE FEEDBACK_ID = "+ feedbackId +"";
}
ResultSet resultSet = statement.executeQuery(query);
while (resultSet.next()){
String name = resultSet.getString("NAME");
String email = resultSet.getString("EMAIL");
String subject = resultSet.getString("SUBJECT");
String message = resultSet.getString("COMMENTS");
long id = resultSet.getLong("FEEDBACK_ID");
Feedback feedback = new Feedback(id, name, email, subject, message);
feedbackList.add(feedback);
}
} catch (SQLException e) {
Log4AltoroJ.getInstance().logError("Error retrieving feedback: " + e.getMessage());
}
return feedbackList;
}
/**
* Authenticate user
* @param user user name
* @param password password
* @return true if valid user, false otherwise
* @throws SQLException
*/
public static boolean isValidUser(String user, String password) throws SQLException{
if (user == null || password == null || user.trim().length() == 0 || password.trim().length() == 0)
return false;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT COUNT(*)FROM PEOPLE WHERE USER_ID = '"+ user +"' AND PASSWORD='" + password + "'"); /* BAD - user input should always be sanitized */
if (resultSet.next()){
if (resultSet.getInt(1) > 0)
return true;
}
return false;
}
@@ -230,0 +231,1 @@
+
/**
* Get user information
* @param username
* @return user information
* @throws SQLException
*/
public static User getUserInfo(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
@@ -241,2 +243,5 @@
- Statement statement = connection.createStatement();
- ResultSet resultSet =statement.executeQuery("SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = '"+ username +"' "); /* BAD - user input should always be sanitized */
+ PreparedStatement preparedStatement = connection.prepareStatement(
+ "SELECT FIRST_NAME,LAST_NAME,ROLE FROM PEOPLE WHERE USER_ID = ?"
+ );
+ preparedStatement.setString(1, username);
+ ResultSet resultSet = preparedStatement.executeQuery();
String firstName = null;
String lastName = null;
String roleString = null;
if (resultSet.next()){
firstName = resultSet.getString("FIRST_NAME");
lastName = resultSet.getString("LAST_NAME");
roleString = resultSet.getString("ROLE");
}
if (firstName == null || lastName == null)
return null;
@@ -255,0 +260,2 @@
+
+
User user = new User(username, firstName, lastName);
if (roleString.equalsIgnoreCase("admin"))
user.setRole(Role.Admin);
return user;
}
/**
* Get all accounts for the specified user
* @param username
* @return
* @throws SQLException
*/
public static Account[] getAccounts(String username) throws SQLException{
if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
long accountId = resultSet.getLong("ACCOUNT_ID");
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountId, name, balance);
accounts.add(newAccount);
}
return accounts.toArray(new Account[accounts.size()]);
}
/**
* Transfer funds between specified accounts
* @param username
* @param creditActId
* @param debitActId
* @param amount
* @return
*/
public static String transferFunds(String username, long creditActId, long debitActId, double amount) {
try {
User user = getUserInfo(username);
Connection connection = getConnection();
Statement statement = connection.createStatement();
Account debitAccount = Account.getAccount(debitActId);
Account creditAccount = Account.getAccount(creditActId);
if (debitAccount == null){
return "Originating account is invalid";
}
if (creditAccount == null)
return "Destination account is invalid";
java.sql.Timestamp date = new Timestamp(new java.util.Date().getTime());
//in real life we would want to do these updates and transaction entry creation
//as one atomic operation
long userCC = user.getCreditCardNumber();
/* this is the account that the payment will be made from, thus negative amount!*/
double debitAmount = -amount;
/* this is the account that the payment will be made to, thus positive amount!*/
double creditAmount = amount;
/* Credit card account balance is the amount owed, not amount owned
* (reverse of other accounts). Therefore we have to process balances differently*/
if (debitAccount.getAccountId() == userCC)
debitAmount = -debitAmount;
//create transaction record
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"',"+((debitAccount.getAccountId() == userCC)?"'Cash Advance'":"'Withdrawal'")+","+debitAmount+")," +
"("+creditAccount.getAccountId()+",'"+date+"',"+((creditAccount.getAccountId() == userCC)?"'Payment'":"'Deposit'")+","+creditAmount+")");
Log4AltoroJ.getInstance().logTransaction(debitAccount.getAccountId()+" - "+ debitAccount.getAccountName(), creditAccount.getAccountId()+" - "+ creditAccount.getAccountName(), amount);
if (creditAccount.getAccountId() == userCC)
creditAmount = -creditAmount;
//add cash advance fee since the money transfer was made from the credit card
if (debitAccount.getAccountId() == userCC){
statement.execute("INSERT INTO TRANSACTIONS (ACCOUNTID, DATE, TYPE, AMOUNT) VALUES ("+debitAccount.getAccountId()+",'"+date+"','Cash Advance Fee',"+CASH_ADVANCE_FEE+")");
debitAmount += CASH_ADVANCE_FEE;
Log4AltoroJ.getInstance().logTransaction(String.valueOf(userCC), "N/A", CASH_ADVANCE_FEE);
}
//update account balances
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (debitAccount.getBalance()+debitAmount) + " WHERE ACCOUNT_ID = " + debitAccount.getAccountId());
statement.execute("UPDATE ACCOUNTS SET BALANCE = " + (creditAccount.getBalance()+creditAmount) + " WHERE ACCOUNT_ID = " + creditAccount.getAccountId());
return null;
} catch (SQLException e) {
return "Transaction failed. Please try again later.";
}
}
/**
* Get transaction information for the specified accounts in the date range (non-inclusive of the dates)
* @param startDate
* @param endDate
* @param accounts
* @param rowCount
* @return
*/
public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {
if (accounts == null || accounts.length == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
if (rowCount > 0)
statement.setMaxRows(rowCount);
StringBuffer acctIds = new StringBuffer();
acctIds.append("ACCOUNTID = " + accounts[0].getAccountId());
for (int i=1; i<accounts.length; i++){
acctIds.append(" OR ACCOUNTID = "+accounts[i].getAccountId());
}
String dateString = null;
if (startDate != null && startDate.length()>0 && endDate != null && endDate.length()>0){
dateString = "DATE BETWEEN '" + startDate + " 00:00:00' AND '" + endDate + " 23:59:59'";
} else if (startDate != null && startDate.length()>0){
dateString = "DATE > '" + startDate +" 00:00:00'";
} else if (endDate != null && endDate.length()>0){
dateString = "DATE < '" + endDate + " 23:59:59'";
}
String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);
} catch (SQLException e){
int errorCode = e.getErrorCode();
if (errorCode == 30000)
throw new SQLException("Date-time query must be in the format of yyyy-mm-dd HH:mm:ss", e);
throw e;
}
ArrayList<Transaction> transactions = new ArrayList<Transaction>();
while (resultSet.next()){
int transId = resultSet.getInt("TRANSACTION_ID");
long actId = resultSet.getLong("ACCOUNTID");
Timestamp date = resultSet.getTimestamp("DATE");
String desc = resultSet.getString("TYPE");
double amount = resultSet.getDouble("AMOUNT");
transactions.add(new Transaction(transId, actId, date, desc, amount));
}
return transactions.toArray(new Transaction[transactions.size()]);
}
public static String[] getBankUsernames() {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
//at the moment this query limits transfers to
//transfers between two user accounts
ResultSet resultSet =statement.executeQuery("SELECT USER_ID FROM PEOPLE");
ArrayList<String> users = new ArrayList<String>();
while (resultSet.next()){
String name = resultSet.getString("USER_ID");
users.add(name);
}
return users.toArray(new String[users.size()]);
} catch (SQLException e){
e.printStackTrace();
return new String[0];
}
}
public static Account getAccount(long accountNo) throws SQLException {
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE ACCOUNT_ID = "+ accountNo +" "); /* BAD - user input should always be sanitized */
ArrayList<Account> accounts = new ArrayList<Account>(3);
while (resultSet.next()){
String name = resultSet.getString("ACCOUNT_NAME");
double balance = resultSet.getDouble("BALANCE");
Account newAccount = new Account(accountNo, name, balance);
accounts.add(newAccount);
}
if (accounts.size()==0)
return null;
return accounts.get(0);
}
public static String addAccount(String username, String acctType) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO ACCOUNTS (USERID,ACCOUNT_NAME,BALANCE) VALUES ('"+username+"','"+acctType+"', 0)");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addSpecialUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO SPECIAL_CUSTOMERS (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String addUser(String username, String password, String firstname, String lastname) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO PEOPLE (USER_ID,PASSWORD,FIRST_NAME,LAST_NAME,ROLE) VALUES ('"+username+"','"+password+"', '"+firstname+"', '"+lastname+"','user')");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static String changePassword(String username, String password) {
try {
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("UPDATE PEOPLE SET PASSWORD = '"+ password +"' WHERE USER_ID = '"+username+"'");
return null;
} catch (SQLException e){
return e.toString();
}
}
public static long storeFeedback(String name, String email, String subject, String comments) {
try{
Connection connection = getConnection();
Statement statement = connection.createStatement();
statement.execute("INSERT INTO FEEDBACK (NAME,EMAIL,SUBJECT,COMMENTS) VALUES ('"+name+"', '"+email+"', '"+subject+"', '"+comments+"')", Statement.RETURN_GENERATED_KEYS);
ResultSet rs= statement.getGeneratedKeys();
long id = -1;
if (rs.next()){
id = rs.getLong(1);
}
return id;
} catch (SQLException e){
Log4AltoroJ.getInstance().logError(e.getMessage());
return -1;
}
}
}

  • Create Pull Request
Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

No Automatic Remediation (4)

SeverityVulnerability TypeCWEFileData FlowsDate
HighCommand Injection

CWE-78

index.jsp:65

22024-04-30 04:20am
Vulnerable Code

shell = "bash";
shellarg = "-c";
command = "cat '" + path + "/" + content +"'";
}
Process proc = Runtime.getRuntime().exec(new String[] {shell, shellarg, command});

2 Data Flow/s detected
View Data Flow 1

java.lang.String content = request.getParameter("content");

command = "type \"" + path + "\\" + content + "\"";

Process proc = Runtime.getRuntime().exec(new String[] {shell, shellarg, command});

View Data Flow 2

content = request.getParameter("content");

command = "cat '" + path + "/" + content +"'";

Process proc = Runtime.getRuntime().exec(new String[] {shell, shellarg, command});

Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior Command Injection Training

● Videos

   ▪ Secure Code Warrior Command Injection Video

● Further Reading

   ▪ OWASP testing for Command Injection

   ▪ OWASP Command Injection

 
HighCode Injection

CWE-94

serverStatusCheck.html:41

12024-04-30 04:20am
Vulnerable Code

}
function StateChangeForJSON()
{
if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
{
var jsonObj = eval('('+ xmlHttp.responseText + ')');

1 Data Flow/s detected

var jsonObj = eval('('+ xmlHttp.responseText + ')');

Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior Code Injection Training

● Videos

   ▪ Secure Code Warrior Code Injection Video

● Further Reading

   ▪ OWASP Command Injection

 
HighSQL Injection

CWE-89

DBUtil.java:403

22024-04-30 04:20am
Vulnerable Code

String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery(query);

2 Data Flow/s detected
View Data Flow 1

String endString = request.getParameter("endTime");

transactions = user.getUserTransactions(startString, endString, user.getAccounts());

public Transaction[] getUserTransactions(String startDate, String endDate, Account[] accounts) throws SQLException {

transactions = DBUtil.getTransactions(startDate, endDate, accounts, -1);

public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {

dateString = "DATE < '" + endDate + " 23:59:59'";

String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;

resultSet = statement.executeQuery(query);

View Data Flow 2

public Response getTransactions(@PathParam("accountNo") String accountNo,

endString = (String) myJson.get("endDate");

transactions = user.getUserTransactions(startString, endString,

public Transaction[] getUserTransactions(String startDate, String endDate, Account[] accounts) throws SQLException {

transactions = DBUtil.getTransactions(startDate, endDate, accounts, -1);

public static Transaction[] getTransactions(String startDate, String endDate, Account[] accounts, int rowCount) throws SQLException {

dateString = "DATE < '" + endDate + " 23:59:59'";

String query = "SELECT * FROM TRANSACTIONS WHERE (" + acctIds.toString() + ") " + ((dateString==null)?"": "AND (" + dateString + ") ") + "ORDER BY DATE DESC" ;

resultSet = statement.executeQuery(query);

Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

 
HighSQL Injection

CWE-89

DBUtil.java:276

22024-04-30 04:20am
Vulnerable Code

if (username == null || username.trim().length() == 0)
return null;
Connection connection = getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */

2 Data Flow/s detected
View Data Flow 1

Cookie accountCookie = ServletUtil.establishSession(username,session);

public static Cookie establishSession(String username, HttpSession session){

User user = DBUtil.getUserInfo(username);

public static User getUserInfo(String username) throws SQLException{

View remaining steps

Account[] accounts = user.getAccounts();

public static Account[] getAccounts(String username) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */

View Data Flow 2

String accessToken = request.getHeader("Authorization").replaceAll("Bearer ", "");

String decodedToken = new String(Base64.decodeBase64(accessToken));

StringTokenizer tokenizer = new StringTokenizer(decodedToken,":");

String username = new String(Base64.decodeBase64(tokenizer.nextToken()));

View remaining steps

for (Account account : getAccounts()) {

public static Account[] getAccounts(String username) throws SQLException{

ResultSet resultSet =statement.executeQuery("SELECT ACCOUNT_ID, ACCOUNT_NAME, BALANCE FROM ACCOUNTS WHERE USERID = '"+ username +"' "); /* BAD - user input should always be sanitized */

Secure Code Warrior Training Material

● Training

   ▪ Secure Code Warrior SQL Injection Training

● Videos

   ▪ Secure Code Warrior SQL Injection Video

● Further Reading

   ▪ OWASP SQL Injection Prevention Cheat Sheet

   ▪ OWASP SQL Injection

   ▪ OWASP Query Parameterization Cheat Sheet

Findings Overview

Severity Vulnerability Type CWE Language Count
High Code Injection CWE-94 JavaScript / Node.js 1
High Cross-Site Scripting CWE-79 Java* 11
High SQL Injection CWE-89 Java* 8
High Command Injection CWE-78 Java* 1
High DOM Based Cross-Site Scripting CWE-79 JavaScript / Node.js 3
Medium Error Messages Information Exposure CWE-209 Java* 10
Medium Hardcoded Password/Credentials CWE-798 JavaScript / Node.js 1
Medium Trust Boundary Violation CWE-501 Java* 2
Low Unvalidated/Open Redirect CWE-601 JavaScript / Node.js 4

jquery-1.8.0.min.js: 6 vulnerabilities (highest severity is: 6.1)

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (jquery version) Remediation Possible**
CVE-2020-7656 Medium 6.1 jquery-1.8.0.min.js Direct jquery - 1.9.0
CVE-2020-11023 Medium 6.1 jquery-1.8.0.min.js Direct jquery - 3.5.0;jquery-rails - 4.4.0
CVE-2020-11022 Medium 6.1 jquery-1.8.0.min.js Direct jQuery - 3.5.0
CVE-2019-11358 Medium 6.1 jquery-1.8.0.min.js Direct jquery - 3.4.0
CVE-2015-9251 Medium 6.1 jquery-1.8.0.min.js Direct jQuery - 3.0.0
CVE-2012-6708 Medium 6.1 jquery-1.8.0.min.js Direct jQuery - v1.9.0

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2020-7656

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Dependency Hierarchy:

  • jquery-1.8.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

jquery prior to 1.9.0 allows Cross-site Scripting attacks via the load method. The load method fails to recognize and remove "<script>" HTML tags that contain a whitespace character, i.e: "</script >", which results in the enclosed script logic to be executed.

Publish Date: 2020-05-19

URL: CVE-2020-7656

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: GHSA-q4m3-2j7h-f7xw

Release Date: 2020-05-19

Fix Resolution: jquery - 1.9.0

CVE-2020-11023

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Dependency Hierarchy:

  • jquery-1.8.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

In jQuery versions greater than or equal to 1.0.3 and before 3.5.0, passing HTML containing elements from untrusted sources - even after sanitizing it - to one of jQuery's DOM manipulation methods (i.e. .html(), .append(), and others) may execute untrusted code. This problem is patched in jQuery 3.5.0.

Publish Date: 2020-04-29

URL: CVE-2020-11023

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://github.com/jquery/jquery/security/advisories/GHSA-jpcq-cgw6-v4j6,https://github.com/rails/jquery-rails/blob/master/CHANGELOG.md#440

Release Date: 2020-04-29

Fix Resolution: jquery - 3.5.0;jquery-rails - 4.4.0

CVE-2020-11022

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Dependency Hierarchy:

  • jquery-1.8.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

In jQuery versions greater than or equal to 1.2 and before 3.5.0, passing HTML from untrusted sources - even after sanitizing it - to one of jQuery's DOM manipulation methods (i.e. .html(), .append(), and others) may execute untrusted code. This problem is patched in jQuery 3.5.0.

Publish Date: 2020-04-29

URL: CVE-2020-11022

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11022

Release Date: 2020-04-29

Fix Resolution: jQuery - 3.5.0

CVE-2019-11358

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Dependency Hierarchy:

  • jquery-1.8.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution. If an unsanitized source object contained an enumerable proto property, it could extend the native Object.prototype.

Publish Date: 2019-04-20

URL: CVE-2019-11358

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358

Release Date: 2019-04-20

Fix Resolution: jquery - 3.4.0

CVE-2015-9251

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Dependency Hierarchy:

  • jquery-1.8.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

jQuery before 3.0.0 is vulnerable to Cross-site Scripting (XSS) attacks when a cross-domain Ajax request is performed without the dataType option, causing text/javascript responses to be executed.

Publish Date: 2018-01-18

URL: CVE-2015-9251

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://nvd.nist.gov/vuln/detail/CVE-2015-9251

Release Date: 2018-01-18

Fix Resolution: jQuery - 3.0.0

CVE-2012-6708

Vulnerable Library - jquery-1.8.0.min.js

JavaScript library for DOM operations

Library home page: https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.0/jquery.min.js

Path to vulnerable library: /WebContent/swagger/lib/jquery-1.8.0.min.js

Dependency Hierarchy:

  • jquery-1.8.0.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

jQuery before 1.9.0 is vulnerable to Cross-site Scripting (XSS) attacks. The jQuery(strInput) function does not differentiate selectors from HTML in a reliable fashion. In vulnerable versions, jQuery determined whether the input was HTML by looking for the '<' character anywhere in the string, giving attackers more flexibility when attempting to construct a malicious payload. In fixed versions, jQuery only deems the input to be HTML if it explicitly starts with the '<' character, limiting exploitability only to attackers who can control the beginning of a string, which is far less common.

Publish Date: 2018-01-18

URL: CVE-2012-6708

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://nvd.nist.gov/vuln/detail/CVE-2012-6708

Release Date: 2018-01-18

Fix Resolution: jQuery - v1.9.0

swagger-ui-bundle-3.19.3.js: 1 vulnerabilities (highest severity is: 4.3)

Vulnerable Library - swagger-ui-bundle-3.19.3.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.19.3/swagger-ui-bundle.js

Path to dependency file: /WebContent/swagger/index.html

Path to vulnerable library: /WebContent/swagger/swagger-ui-bundle.js,/WebContent/swagger/./swagger-ui-bundle.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (swagger-ui-bundle version) Remediation Possible**
CVE-2018-25031 Medium 4.3 swagger-ui-bundle-3.19.3.js Direct swagger-ui - 4.1.3;swagger-ui-dist - 4.1.3

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

CVE-2018-25031

Vulnerable Library - swagger-ui-bundle-3.19.3.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.19.3/swagger-ui-bundle.js

Path to dependency file: /WebContent/swagger/index.html

Path to vulnerable library: /WebContent/swagger/swagger-ui-bundle.js,/WebContent/swagger/./swagger-ui-bundle.js

Dependency Hierarchy:

  • swagger-ui-bundle-3.19.3.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Swagger UI before 4.1.3 could allow a remote attacker to conduct spoofing attacks. By persuading a victim to open a crafted URL, an attacker could exploit this vulnerability to display remote OpenAPI definitions.
Mend Note: Converted from WS-2021-0461, on 2022-12-21.

Publish Date: 2022-03-11

URL: CVE-2018-25031

CVSS 3 Score Details (4.3)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: None
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: GHSA-qrmm-w75w-3wpx

Release Date: 2022-03-11

Fix Resolution: swagger-ui - 4.1.3;swagger-ui-dist - 4.1.3

swagger-ui-2.1.2.min.js: 6 vulnerabilities (highest severity is: 7.3)

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Vulnerabilities

CVE Severity CVSS Dependency Type Fixed in (swagger-ui version) Remediation Possible**
WS-2016-0034 High 7.3 swagger-ui-2.1.2.min.js Direct v2.1.5
CVE-2016-1000233 Medium 6.5 swagger-ui-2.1.2.min.js Direct 2.2.1
WS-2019-0234 Medium 6.1 swagger-ui-2.1.2.min.js Direct 2.2.1
CVE-2016-1000229 Medium 6.1 swagger-ui-2.1.2.min.js Direct 2.2.1
WS-2017-0143 Medium 5.4 swagger-ui-2.1.2.min.js Direct 2.2.3
CVE-2018-25031 Medium 4.3 swagger-ui-2.1.2.min.js Direct swagger-ui - 4.1.3;swagger-ui-dist - 4.1.3

**In some cases, Remediation PR cannot be created automatically for a vulnerability despite the availability of remediation

Details

WS-2016-0034

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Dependency Hierarchy:

  • swagger-ui-2.1.2.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Swagger-ui has vulnerability when "Produces" and "consumes" Content-types in schema are not escaped and allow XSS

Publish Date: 2016-01-13

URL: WS-2016-0034

CVSS 3 Score Details (7.3)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: Low

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Release Date: 2016-01-13

Fix Resolution: v2.1.5

CVE-2016-1000233

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Dependency Hierarchy:

  • swagger-ui-2.1.2.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

swagger-ui before 2.2.1 automatically executes external Javascript that is loaded in via the url query string parameter

Publish Date: 2019-07-11

URL: CVE-2016-1000233

CVSS 3 Score Details (6.5)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: None
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-16082

Release Date: 2019-07-11

Fix Resolution: 2.2.1

WS-2019-0234

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Dependency Hierarchy:

  • swagger-ui-2.1.2.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Swagger-UI versions before 2.2.1 are vulnerable to XSS when allowing HTML code in the swagger.apiInfo.description value without proper sanitization, which may allow attackers to execute arbitrary JavaScript.

Publish Date: 2015-01-28

URL: WS-2019-0234

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-16082

Release Date: 2015-01-28

Fix Resolution: 2.2.1

CVE-2016-1000229

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Dependency Hierarchy:

  • swagger-ui-2.1.2.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

swagger-ui has XSS in key names

Publish Date: 2019-12-20

URL: CVE-2016-1000229

CVSS 3 Score Details (6.1)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Changed
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: https://www.npmjs.com/advisories/126

Release Date: 2019-12-20

Fix Resolution: 2.2.1

WS-2017-0143

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Dependency Hierarchy:

  • swagger-ui-2.1.2.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Affected versions of the package are vulnerable to Cross-site Scripting (XSS) due to not escaping html script tags.

Publish Date: 2016-09-01

URL: WS-2017-0143

CVSS 3 Score Details (5.4)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: Low
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Release Date: 2016-09-01

Fix Resolution: 2.2.3

CVE-2018-25031

Vulnerable Library - swagger-ui-2.1.2.min.js

Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API

Library home page: https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.2/swagger-ui.min.js

Path to vulnerable library: /WebContent/swagger/swagger-ui.min.js

Dependency Hierarchy:

  • swagger-ui-2.1.2.min.js (Vulnerable Library)

Found in HEAD commit: c7142581c9069b8cb9288ee3a8c017f04d3578b4

Found in base branch: AltoroJ-3.2

Vulnerability Details

Swagger UI before 4.1.3 could allow a remote attacker to conduct spoofing attacks. By persuading a victim to open a crafted URL, an attacker could exploit this vulnerability to display remote OpenAPI definitions.
Mend Note: Converted from WS-2021-0461, on 2022-12-21.

Publish Date: 2022-03-11

URL: CVE-2018-25031

CVSS 3 Score Details (4.3)

Base Score Metrics:

  • Exploitability Metrics:
    • Attack Vector: Network
    • Attack Complexity: Low
    • Privileges Required: None
    • User Interaction: Required
    • Scope: Unchanged
  • Impact Metrics:
    • Confidentiality Impact: Low
    • Integrity Impact: None
    • Availability Impact: None

For more information on CVSS3 Scores, click here.

Suggested Fix

Type: Upgrade version

Origin: GHSA-qrmm-w75w-3wpx

Release Date: 2022-03-11

Fix Resolution: swagger-ui - 4.1.3;swagger-ui-dist - 4.1.3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.