Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.foo.spring.rest.mysql.sqli.body

import com.foo.spring.rest.mysql.SwaggerConfiguration
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.media.ArraySchema
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.parameters.RequestBody
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.context.annotation.ComponentScan
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.sql.Connection
import javax.annotation.PostConstruct
import javax.sql.DataSource


@EnableSwagger2
@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/sqli/body"])
@RestController
open class BodySQLiApplication: SwaggerConfiguration() {

@Autowired
private lateinit var dataSource: DataSource

@Autowired
private lateinit var userRepository: UserRepository

private var connection: Connection? = null

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(BodySQLiApplication::class.java, *args)
}
}

@PostConstruct
fun init() {
connection = dataSource.connection
initializeTestData()
}

private fun initializeTestData() {
if (userRepository.count() == 0L) {
userRepository.save(UserEntity(null, "admin", "admin123"))
userRepository.save(UserEntity(null, "user1", "password1"))
}
}

/**
* Safe endpoint - No SQL Injection vulnerability
*/
@GetMapping("/safe")
@Operation(summary = "Safe Query - No SQL Injection")
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Successful operation",
content = [Content(mediaType = "application/json",
array = ArraySchema(schema = Schema(implementation = UserDto::class)))])
])
fun getSafeUsers(): ResponseEntity<List<UserDto>> {
val users = userRepository.findAll().map { UserDto(it.id, it.username) }
return ResponseEntity.ok(users)
}

/**
* Attack: POST {"username":"admin' AND (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C)>0--","password":"x"}
*/
@PostMapping("/vulnerable")
@Operation(summary = "SQL Injection - Body Parameter")
@ApiResponses(value = [
ApiResponse(responseCode = "200", description = "Successful operation",
content = [Content(mediaType = "text/plain", schema = Schema(implementation = String::class))]),
ApiResponse(responseCode = "500", description = "Internal server error",
content = [Content(mediaType = "text/plain", schema = Schema(implementation = String::class))])
])
@RequestBody(description = "Login credentials", required = true,
content = [Content(mediaType = "application/json",
schema = Schema(implementation = LoginDto::class))])
fun body(@org.springframework.web.bind.annotation.RequestBody loginDto: LoginDto): ResponseEntity<String> {
return try {
val stmt = connection?.createStatement()
val rs = stmt?.executeQuery("SELECT COUNT(*) as cnt FROM users WHERE username = '${loginDto.username}' AND password = '${loginDto.password}'")
val count = if (rs?.next() == true) rs.getInt("cnt") else 0
ResponseEntity.ok("MATCHED: $count")
} catch (e: Exception) {
ResponseEntity.status(500).body("ERROR: ${e.message}")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.foo.spring.rest.mysql.sqli.body

import com.fasterxml.jackson.annotation.JsonProperty

data class UserDto(
@JsonProperty("id") var id: Long? = null,
@JsonProperty("username") var username: String? = null,
)

data class LoginDto(
@JsonProperty("username") var username: String = "",
@JsonProperty("password") var password: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.foo.spring.rest.mysql.sqli.body

import javax.persistence.*


@Entity
@Table(name = "users")
open class UserEntity(

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
open var id: Long? = null,

@Column(name = "username", unique = true, nullable = false)
open var username: String? = null,

@Column(name = "password", nullable = false)
open var password: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.foo.spring.rest.mysql.sqli.body

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository


@Repository
interface UserRepository : JpaRepository<UserEntity, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.foo.spring.rest.mysql.sqli.path


import io.swagger.v3.oas.annotations.Operation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.context.annotation.ComponentScan
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.sql.Connection
import javax.annotation.PostConstruct
import javax.sql.DataSource


@EnableSwagger2
@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/sqli/path"])
@RestController
open class PathSQLiApplication {

@Autowired
private lateinit var dataSource: DataSource

@Autowired
private lateinit var userRepository: UserRepository

private var connection: Connection? = null

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(PathSQLiApplication::class.java, *args)
}
}

@PostConstruct
fun init() {
connection = dataSource.connection
initializeTestData()
}

private fun initializeTestData() {
if (userRepository.count() == 0L) {
userRepository.save(UserEntity(null, "admin", "admin123"))
userRepository.save(UserEntity(null, "user1", "password1"))
}
}

/**
* Safe endpoint - No SQL Injection vulnerability
*/
@GetMapping("/safe")
@Operation(summary = "Safe Query - No SQL Injection")
fun getSafeUsers(): ResponseEntity<List<UserDto>> {
val users = userRepository.findAll().map { UserDto(it.id, it.username) }
return ResponseEntity.ok(users)
}

/**
* Attack: GET /api/sqli/path/vulnerable/admin' OR (SELECT SUM(a.ORDINAL_POSITION*b.ORDINAL_POSITION*c.ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS a, INFORMATION_SCHEMA.COLUMNS b, INFORMATION_SCHEMA.COLUMNS c)>1 --
*/
@GetMapping("/vulnerable/{id}")
@Operation(summary = "SQL Injection - Path Parameter")
fun timeBasedPath(@PathVariable id: String): ResponseEntity<String> {
return try {
val stmt = connection?.createStatement()
val rs = stmt?.executeQuery("SELECT username FROM users WHERE username = '$id'")
val username = if (rs?.next() == true) rs.getString("username") else "NOT_FOUND"
ResponseEntity.ok("USERNAME: $username")
} catch (e: Exception) {
ResponseEntity.status(500).body("ERROR: ${e.message}")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.foo.spring.rest.mysql.sqli.path


data class UserDto(
var id: Long? = null,
var username: String? = null,
)

data class LoginDto(
var username: String = "",
var password: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.foo.spring.rest.mysql.sqli.path

import javax.persistence.*


@Entity
@Table(name = "users")
open class UserEntity(

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
open var id: Long? = null,

@Column(name = "username", unique = true, nullable = false)
open var username: String? = null,

@Column(name = "password", nullable = false)
open var password: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.foo.spring.rest.mysql.sqli.path

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository


@Repository
interface UserRepository : JpaRepository<UserEntity, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.foo.spring.rest.mysql.sqli.query

import com.foo.spring.rest.mysql.SwaggerConfiguration
import io.swagger.v3.oas.annotations.Operation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.sql.Connection
import javax.annotation.PostConstruct
import javax.sql.DataSource


@EnableSwagger2
@SpringBootApplication(exclude = [SecurityAutoConfiguration::class])
@RequestMapping(path = ["/api/sqli/query"])
open class QuerySQLiApplication: SwaggerConfiguration() {

@Autowired
private lateinit var dataSource: DataSource

@Autowired
private lateinit var userRepository: UserRepository

private var connection: Connection? = null

companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(QuerySQLiApplication::class.java, *args)
}
}

@PostConstruct
fun init() {
connection = dataSource.connection
initializeTestData()
}

private fun initializeTestData() {
if (userRepository.count() == 0L) {
userRepository.save(UserEntity(null, "admin", "admin123"))
userRepository.save(UserEntity(null, "user1", "password1"))
}
}

/**
* Safe endpoint - No SQL Injection vulnerability
*/
@GetMapping("/safe")
@Operation(summary = "Safe Query - No SQL Injection")
fun getSafeUsers(): ResponseEntity<List<UserDto>> {
val users = userRepository.findAll().map { UserDto(it.id, it.username) }
return ResponseEntity.ok(users)
}

/**
* Attack: GET /api/sqli/query/vulnerable?username=admin' OR (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C)>0--
*/
@GetMapping("/vulnerable")
@Operation(summary = "SQL Injection - Query Parameter")
fun timeBasedQuery(@RequestParam username: String): ResponseEntity<String> {
return try {
val stmt = connection?.createStatement()
val rs = stmt?.executeQuery("SELECT COUNT(*) as cnt FROM users WHERE username = '$username'")
val count = if (rs?.next() == true) rs.getInt("cnt") else 0
ResponseEntity.ok("COUNT: $count")
} catch (e: Exception) {
ResponseEntity.status(500).body("ERROR: ${e.message}")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.foo.spring.rest.mysql.sqli.query


data class UserDto(
var id: Long? = null,
var username: String? = null,
)

data class LoginDto(
var username: String,
var password: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.foo.spring.rest.mysql.sqli.query

import javax.persistence.*


@Entity
@Table(name = "users")
open class UserEntity(

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
open var id: Long? = null,

@Column(name = "username", unique = true, nullable = false)
open var username: String? = null,

@Column(name = "password", nullable = false)
open var password: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.foo.spring.rest.mysql.sqli.query

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository


@Repository
interface UserRepository : JpaRepository<UserEntity, Long> {

}
Loading
Loading