Skip to content

Commit

Permalink
#406 secured implementations for Union SQL Injection (#452)
Browse files Browse the repository at this point in the history
* Add secured implementations for Union SQL Injection
---------

Co-authored-by: philipp.delmonego <[email protected]>
Co-authored-by: Sebastian Klawin <[email protected]>
Co-authored-by: Karan Preet Singh Sasan <[email protected]>
  • Loading branch information
4 people authored Dec 18, 2023
1 parent a867b67 commit 4986733
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package org.sasanlabs.service.vulnerability.sqlInjection;

import javax.persistence.*;

/** @author [email protected] KSASAN */
@Access(AccessType.FIELD)
@Entity
@Table(name = "cars")
@NamedQuery(name = "findById", query = "select c from CarInformation c where c.id=:id")
public class CarInformation {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

private String name;

@Column(name = "image")
private String imagePath;

public CarInformation() {}
Expand Down Expand Up @@ -38,4 +50,8 @@ public String getImagePath() {
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}

public void setImage(String imagePath) {
this.imagePath = imagePath;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sasanlabs.service.vulnerability.sqlInjection;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CarInformationRepository extends JpaRepository<CarInformation, Integer> {

Optional<CarInformation> findById(Integer id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Optional;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.sasanlabs.internal.utility.LevelConstants;
import org.sasanlabs.internal.utility.Variant;
import org.sasanlabs.internal.utility.annotations.AttackVector;
Expand All @@ -12,7 +18,11 @@
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.web.bind.annotation.RequestParam;

/**
Expand All @@ -28,10 +38,19 @@
public class UnionBasedSQLInjectionVulnerability {

private final JdbcTemplate applicationJdbcTemplate;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final CarInformationRepository carInformationRepository;
private final EntityManager entityManager;

public UnionBasedSQLInjectionVulnerability(
@Qualifier("applicationJdbcTemplate") final JdbcTemplate applicationJdbcTemplate) {
@Qualifier("applicationJdbcTemplate") final JdbcTemplate applicationJdbcTemplate,
NamedParameterJdbcTemplate namedParameterJdbcTemplate,
CarInformationRepository carInformationRepository,
EntityManager entityManager) {
this.applicationJdbcTemplate = applicationJdbcTemplate;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
this.carInformationRepository = carInformationRepository;
this.entityManager = entityManager;
}

@AttackVector(
Expand Down Expand Up @@ -92,6 +111,83 @@ public ResponseEntity<CarInformation> getCarInformationLevel4(
this::resultSetToResponse);
}

@VulnerableAppRequestMapping(
value = LevelConstants.LEVEL_5,
variant = Variant.SECURE,
htmlTemplate = "LEVEL_1/SQLInjection_Level1")
public ResponseEntity<CarInformation> getCarInformationLevel5(
@RequestParam final Map<String, String> queryParams) {
final String id = queryParams.get("id");
SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", id);
CarInformation s =
namedParameterJdbcTemplate.queryForObject(
"select * from cars where id=:id",
namedParameters,
new BeanPropertyRowMapper<>(CarInformation.class));
return new ResponseEntity<>(s, HttpStatus.OK);
}

@VulnerableAppRequestMapping(
value = LevelConstants.LEVEL_6,
variant = Variant.SECURE,
htmlTemplate = "LEVEL_1/SQLInjection_Level1")
public ResponseEntity<CarInformation> getCarInformationLevel6(
@RequestParam final Map<String, String> queryParams) {
final String id = queryParams.get("id");
String jql = "from CarInformation where id = :id";
TypedQuery<CarInformation> q =
entityManager
.createQuery(jql, CarInformation.class)
.setParameter("id", Integer.valueOf(id));
return new ResponseEntity<>(q.getSingleResult(), HttpStatus.OK);
}

@VulnerableAppRequestMapping(
value = LevelConstants.LEVEL_7,
variant = Variant.SECURE,
htmlTemplate = "LEVEL_1/SQLInjection_Level1")
public ResponseEntity<CarInformation> getCarInformationLevel7(
@RequestParam final Map<String, String> queryParams) {
final String id = queryParams.get("id");

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<CarInformation> cq = cb.createQuery(CarInformation.class);
Root<CarInformation> root = cq.from(CarInformation.class);

cq.select(root).where(cb.equal(root.get("id"), id));

TypedQuery<CarInformation> q = entityManager.createQuery(cq);
return new ResponseEntity<>(q.getSingleResult(), HttpStatus.OK);
}

@VulnerableAppRequestMapping(
value = LevelConstants.LEVEL_8,
variant = Variant.SECURE,
htmlTemplate = "LEVEL_1/SQLInjection_Level1")
public ResponseEntity<CarInformation> getCarInformationLevel8(
@RequestParam final Map<String, String> queryParams) {
final String id = queryParams.get("id");
TypedQuery<CarInformation> q =
entityManager
.createNamedQuery("findById", CarInformation.class)
.setParameter("id", Integer.valueOf(id));
return new ResponseEntity<>(q.getSingleResult(), HttpStatus.OK);
}

@VulnerableAppRequestMapping(
value = LevelConstants.LEVEL_9,
variant = Variant.SECURE,
htmlTemplate = "LEVEL_1/SQLInjection_Level1")
public ResponseEntity<CarInformation> getCarInformationLevel9(
@RequestParam final Map<String, String> queryParams) {
final String id = queryParams.get("id");
Optional<CarInformation> carInformation =
carInformationRepository.findById(Integer.valueOf(id));
return carInformation
.map(information -> new ResponseEntity<>(information, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}

private ResponseEntity<CarInformation> resultSetToResponse(final ResultSet rs)
throws SQLException {
final CarInformation carInformation = new CarInformation();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
package org.sasanlabs.service.vulnerability.sqlInjection;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import javax.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

class UnionBasedSQLInjectionVulnerabilityTest {

private UnionBasedSQLInjectionVulnerability unionBasedSQLInjectionVulnerability;
private JdbcTemplate template;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private CarInformationRepository carInformationRepository;
private EntityManager entityManager;

@BeforeEach
void setUp() throws IOException {
void setUp() {
template = Mockito.mock(JdbcTemplate.class);
namedParameterJdbcTemplate = Mockito.mock(NamedParameterJdbcTemplate.class);
carInformationRepository = Mockito.mock(CarInformationRepository.class);
entityManager = Mockito.mock(EntityManager.class);

// mock database
doReturn(null)
Expand All @@ -36,11 +46,16 @@ void setUp() throws IOException {
(PreparedStatementSetter) any(),
(ResultSetExtractor<? extends Object>) any());

unionBasedSQLInjectionVulnerability = new UnionBasedSQLInjectionVulnerability(template);
unionBasedSQLInjectionVulnerability =
new UnionBasedSQLInjectionVulnerability(
template,
namedParameterJdbcTemplate,
carInformationRepository,
entityManager);
}

@Test
void getCarInformationLevel1_ExpectParamInjected() throws IOException {
void getCarInformationLevel1_ExpectParamInjected() {
// Act
final Map<String, String> params =
Collections.singletonMap("id", "1 UNION SELECT * FROM cars;");
Expand All @@ -54,7 +69,7 @@ void getCarInformationLevel1_ExpectParamInjected() throws IOException {
}

@Test
void getCarInformationLevel2_ExpectParamInjected() throws IOException {
void getCarInformationLevel2_ExpectParamInjected() {
// Act
final Map<String, String> params =
Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --");
Expand All @@ -68,7 +83,7 @@ void getCarInformationLevel2_ExpectParamInjected() throws IOException {
}

@Test
void getCarInformationLevel3_ExpectParamEscaped() throws IOException {
void getCarInformationLevel3_ExpectParamEscaped() {
// Act
final Map<String, String> params =
Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --");
Expand All @@ -82,7 +97,7 @@ void getCarInformationLevel3_ExpectParamEscaped() throws IOException {
}

@Test
void getCarInformationLevel4_ExpecParamEscaped() throws IOException {
void getCarInformationLevel4_ExpectParamEscaped() {
// Act
final Map<String, String> params =
Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --");
Expand All @@ -95,4 +110,27 @@ void getCarInformationLevel4_ExpecParamEscaped() throws IOException {
(PreparedStatementSetter) any(),
(ResultSetExtractor<? extends Object>) any());
}

@Test
void getCarInformationLevel5_ExpectParamEscaped() {
// Act
final Map<String, String> params =
Collections.singletonMap("id", "1' UNION SELECT * FROM cars; --");
final String id = "1' UNION SELECT * FROM cars; --";
unionBasedSQLInjectionVulnerability.getCarInformationLevel5(params);
// Assert
ArgumentMatcher<MapSqlParameterSource> argumentMatcher =
sqlParameterSource ->
Objects.requireNonNull(sqlParameterSource.getValue("id").equals(id));
verify(namedParameterJdbcTemplate)
.queryForObject(
eq("select * from cars where id=:id"),
argThat(argumentMatcher),
(RowMapper<Object>)
argThat(
val ->
((BeanPropertyRowMapper) val)
.getMappedClass()
.equals(CarInformation.class)));
}
}

0 comments on commit 4986733

Please sign in to comment.