From 407e19638f51b641ecc07d5b3486c8ac429876ae Mon Sep 17 00:00:00 2001 From: Nanne Baars Date: Mon, 13 Apr 2020 15:17:43 +0200 Subject: [PATCH] Add two more assignments for SQL injection where only filtering is applied. --- .../org/owasp/webgoat/IntegrationTest.java | 19 ++++--- .../webgoat/SqlInjectionMitigationTest.java | 11 +++- .../advanced/SqlInjectionLesson6a.java | 2 +- ...sson12a.java => SqlInjectionLesson13.java} | 6 +- .../mitigation/SqlOnlyInputValidation.java | 56 +++++++++++++++++++ .../SqlOnlyInputValidationOnKeywords.java | 56 +++++++++++++++++++ .../html/SqlInjectionMitigations.html | 52 ++++++++++++++++- .../resources/i18n/WebGoatLabels.properties | 20 +++++-- .../js/{assignment12.js => assignment13.js} | 0 .../en/SqlInjection_content12a.adoc | 50 +++-------------- .../en/SqlInjection_content12b.adoc | 7 +++ .../en/SqlInjection_content13.adoc | 53 ++++++++++++++---- .../en/SqlInjection_content14.adoc | 14 +++++ ...est.java => SqlInjectionLesson13Test.java} | 2 +- .../SqlOnlyInputValidationOnKeywordsTest.java | 34 +++++++++++ .../SqlOnlyInputValidationTest.java | 34 +++++++++++ 16 files changed, 341 insertions(+), 75 deletions(-) rename webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/{SqlInjectionLesson12a.java => SqlInjectionLesson13.java} (88%) create mode 100644 webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java create mode 100644 webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java rename webgoat-lessons/sql-injection/src/main/resources/js/{assignment12.js => assignment13.js} (100%) create mode 100644 webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12b.adoc create mode 100644 webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content14.adoc rename webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/{SqlInjectionLesson12aTest.java => SqlInjectionLesson13Test.java} (98%) create mode 100644 webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywordsTest.java create mode 100644 webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationTest.java diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java index 34b48997b..18c8c1ce4 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/IntegrationTest.java @@ -227,14 +227,9 @@ public abstract class IntegrationTest { .extract().path("lessonCompleted"), CoreMatchers.is(expectedResult)); } + //TODO is prefix useful? not every lesson endpoint needs to start with a certain prefix (they are only required to be in the same package) public void checkResults(String prefix) { - Assert.assertThat(RestAssured.given() - .when() - .relaxedHTTPSValidation() - .cookie("JSESSIONID", getWebGoatCookie()) - .get(url("service/lessonoverview.mvc")) - .then() - .statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true))); + checkResults(); Assert.assertThat(RestAssured.given() .when() @@ -246,6 +241,16 @@ public abstract class IntegrationTest { } + public void checkResults() { + Assert.assertThat(RestAssured.given() + .when() + .relaxedHTTPSValidation() + .cookie("JSESSIONID", getWebGoatCookie()) + .get(url("service/lessonoverview.mvc")) + .then() + .statusCode(200).extract().jsonPath().getList("solved"), CoreMatchers.everyItem(CoreMatchers.is(true))); + } + public void checkAssignment(String url, ContentType contentType, String body, boolean expectedResult) { Assert.assertThat( RestAssured.given() diff --git a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java index 40af18a96..d3139f173 100644 --- a/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java +++ b/webgoat-integration-tests/src/test/java/org/owasp/webgoat/SqlInjectionMitigationTest.java @@ -38,6 +38,14 @@ public class SqlInjectionMitigationTest extends IntegrationTest { "}"); checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack10b"), params, true); + params.clear(); + params.put("userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--"); + checkAssignment(url("/WebGoat/SqlOnlyInputValidation/attack"), params, true); + + params.clear(); + params.put("userid_sql_only_input_validation_on_keywords", "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--"); + checkAssignment(url("/WebGoat/SqlOnlyInputValidationOnKeywords/attack"), params, true); + RestAssured.given() .when().relaxedHTTPSValidation().cookie("JSESSIONID", getWebGoatCookie()) .contentType(ContentType.JSON) @@ -57,7 +65,6 @@ public class SqlInjectionMitigationTest extends IntegrationTest { params.put("ip", "104.130.219.202"); checkAssignment(url("/WebGoat/SqlInjectionMitigations/attack12a"), params, true); - checkResults("/SqlInjectionMitigations/"); - + checkResults(); } } diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java index 2f0bd3eaa..7220fee56 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java +++ b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java @@ -53,7 +53,7 @@ public class SqlInjectionLesson6a extends AssignmentEndpoint { // The answer: Smith' union select userid,user_name, password,cookie,cookie, cookie,userid from user_system_data -- } - protected AttackResult injectableQuery(String accountName) { + public AttackResult injectableQuery(String accountName) { String query = ""; try (Connection connection = dataSource.getConnection()) { boolean usedUnion = true; diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson12a.java b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13.java similarity index 88% rename from webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson12a.java rename to webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13.java index 3bc935f84..c5a36057c 100644 --- a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson12a.java +++ b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13.java @@ -38,13 +38,13 @@ import java.sql.ResultSet; import java.sql.SQLException; @RestController -@AssignmentHints(value = {"SqlStringInjectionHint-mitigation-12a-1", "SqlStringInjectionHint-mitigation-12a-2", "SqlStringInjectionHint-mitigation-12a-3", "SqlStringInjectionHint-mitigation-12a-4"}) +@AssignmentHints(value = {"SqlStringInjectionHint-mitigation-13-1", "SqlStringInjectionHint-mitigation-13-2", "SqlStringInjectionHint-mitigation-13-3", "SqlStringInjectionHint-mitigation-13-4"}) @Slf4j -public class SqlInjectionLesson12a extends AssignmentEndpoint { +public class SqlInjectionLesson13 extends AssignmentEndpoint { private final DataSource dataSource; - public SqlInjectionLesson12a(DataSource dataSource) { + public SqlInjectionLesson13(DataSource dataSource) { this.dataSource = dataSource; } diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java new file mode 100644 index 000000000..25dadc0bb --- /dev/null +++ b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java @@ -0,0 +1,56 @@ + +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.sql_injection.mitigation; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AssignmentHints; +import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.sql_injection.advanced.SqlInjectionLesson6a; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@AssignmentHints(value = {"SqlOnlyInputValidation-1", "SqlOnlyInputValidation-2", "SqlOnlyInputValidation-3"}) +public class SqlOnlyInputValidation extends AssignmentEndpoint { + + private final SqlInjectionLesson6a lesson6a; + + public SqlOnlyInputValidation(SqlInjectionLesson6a lesson6a) { + this.lesson6a = lesson6a; + } + + @PostMapping("/SqlOnlyInputValidation/attack") + @ResponseBody + public AttackResult attack(@RequestParam("userid_sql_only_input_validation") String userId) { + if (userId.contains(" ")) { + return failed(this).feedback("SqlOnlyInputValidation-failed").build(); + } + AttackResult attackResult = lesson6a.injectableQuery(userId); + return new AttackResult(attackResult.isLessonCompleted(), attackResult.getFeedback(), attackResult.getOutput(), getClass().getSimpleName()); + } +} diff --git a/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java new file mode 100644 index 000000000..961fbf825 --- /dev/null +++ b/webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java @@ -0,0 +1,56 @@ + +/* + * This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/ + * + * Copyright (c) 2002 - 2019 Bruce Mayhew + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; if + * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Getting Source ============== + * + * Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects. + */ + +package org.owasp.webgoat.sql_injection.mitigation; + +import org.owasp.webgoat.assignments.AssignmentEndpoint; +import org.owasp.webgoat.assignments.AssignmentHints; +import org.owasp.webgoat.assignments.AttackResult; +import org.owasp.webgoat.sql_injection.advanced.SqlInjectionLesson6a; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@AssignmentHints(value = {"SqlOnlyInputValidationOnKeywords-1", "SqlOnlyInputValidationOnKeywords-2", "SqlOnlyInputValidationOnKeywords-3"}) +public class SqlOnlyInputValidationOnKeywords extends AssignmentEndpoint { + + private final SqlInjectionLesson6a lesson6a; + + public SqlOnlyInputValidationOnKeywords(SqlInjectionLesson6a lesson6a) { + this.lesson6a = lesson6a; + } + + @PostMapping("/SqlOnlyInputValidationOnKeywords/attack") + @ResponseBody + public AttackResult attack(@RequestParam("userid_sql_only_input_validation_on_keywords") String userId) { + userId = userId.toUpperCase().replace("FROM", "").replace("SELECT", ""); + if (userId.contains(" ")) { + return failed(this).feedback("SqlOnlyInputValidationOnKeywords-failed").build(); + } + AttackResult attackResult = lesson6a.injectableQuery(userId); + return new AttackResult(attackResult.isLessonCompleted(), attackResult.getFeedback(), attackResult.getOutput(), getClass().getSimpleName()); + } +} diff --git a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html b/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html index b08a2cfe7..033c20bc9 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html +++ b/webgoat-lessons/sql-injection/src/main/resources/html/SqlInjectionMitigations.html @@ -66,14 +66,60 @@
-
+
+
+
+ + + + + + + +
Name:
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + +
Name:
+
+
+
+
+
+ + +
+
- +
-
+
diff --git a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties b/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties index b2544c042..7c24f2fb8 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties +++ b/webgoat-lessons/sql-injection/src/main/resources/i18n/WebGoatLabels.properties @@ -93,12 +93,22 @@ SqlStringInjectionHint-mitigation-10b-3=The wildcard symbol '?' in a prepared st SqlStringInjectionHint-mitigation-10b-4=Make sure to execute your statement. SqlStringInjectionHint-mitigation-10b-5=View the previous lesson to check back on how you can build set up a connection. -SqlStringInjectionHint-mitigation-12a-1=Try sorting and look at the request -SqlStringInjectionHint-mitigation-12a-2=Intercept the request and try to specify a different order by -SqlStringInjectionHint-mitigation-12a-3=Use for example "(case when (true) then hostname else id end)" in the order by and see what happens -SqlStringInjectionHint-mitigation-12a-4=Use for example "(case when (true) then hostname else id end)" in the order by and see what happens +SqlStringInjectionHint-mitigation-13-1=Try sorting and look at the request +SqlStringInjectionHint-mitigation-13-2=Intercept the request and try to specify a different order by +SqlStringInjectionHint-mitigation-13-3=Use for example "(case when (true) then hostname else id end)" in the order by and see what happens +SqlStringInjectionHint-mitigation-13-4=Use for example "(case when (true) then hostname else id end)" in the order by and see what happens SqlInjectionChallengeHint1=The table name is randomized at each start of WebGoat, try to figure out the name first. SqlInjectionChallengeHint2=Find the field which is vulnerable to SQL injection use that to change the password. SqlInjectionChallengeHint3=Change the password through an UPDATE Statement. -SqlInjectionChallengeHint4=The vulnerable field is the username field of the register form. \ No newline at end of file +SqlInjectionChallengeHint4=The vulnerable field is the username field of the register form. + +SqlOnlyInputValidation-failed=Using spaces is not allowed! +SqlOnlyInputValidation-1=Spaces are rejected, try to find a way around this restriction +SqlOnlyInputValidation-2=Try to use a comment in the query +SqlOnlyInputValidation-3=WebGoat uses HSQLDB as a database can you use one of them to make skip the filtering? + +SqlOnlyInputValidationOnKeywords-failed=Use of spaces and/or SQL keywords are not allowed! +SqlOnlyInputValidationOnKeywords-1=Spaces are and SQL keywords are rejected, try to find a way around this restriction +SqlOnlyInputValidationOnKeywords-2=Try to use a comment in the query +SqlOnlyInputValidationOnKeywords-3=WebGoat uses HSQLDB as a database can you use one of them to make skip the filtering? diff --git a/webgoat-lessons/sql-injection/src/main/resources/js/assignment12.js b/webgoat-lessons/sql-injection/src/main/resources/js/assignment13.js similarity index 100% rename from webgoat-lessons/sql-injection/src/main/resources/js/assignment12.js rename to webgoat-lessons/sql-injection/src/main/resources/js/assignment13.js diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12a.adoc b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12a.adoc index 52c8983bf..959459408 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12a.adoc +++ b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12a.adoc @@ -1,47 +1,11 @@ -== Order by clause +== Input validation alone is not enough!! -Question: Does a prepared statement always prevent against an SQL injection? -Answer: No it does not +You need to do both use parametrized queries and validate the input received from the user. On StackOverflow you will +see alot of answers stating that input validation is enough. *However* it only takes you so far before you know it +the validation is broken and you have an SQL injection in your application. -Let us take a look at the following statement: +A nice read why it is not enough can be found https://twitter.com/marcan42/status/1238004834806067200?s=21 ----- -SELECT * FROM users ORDER BY lastname; ----- +Let's repeat one of the previous assignments, the developer fixed the possible SQL injection with filtering, can you +spot the weakness in this approach? -If we look at the specification of the SQL grammar the definition is as follows: - ----- -SELECT ... -FROM tableList -[WHERE Expression] -[ORDER BY orderExpression [, ...]] - -orderExpression: -{ columnNr | columnAlias | selectExpression } - [ASC | DESC] - -selectExpression: -{ Expression | COUNT(*) | { - COUNT | MIN | MAX | SUM | AVG | SOME | EVERY | - VAR_POP | VAR_SAMP | STDDEV_POP | STDDEV_SAMP -} ([ALL | DISTINCT][2]] Expression) } [[AS] label] - -Based on HSQLDB ----- - -This means an `orderExpression` can be a `selectExpression` which can be a function as well, so for example with -a `case` statement we might be able to ask the database some questions, like: - ----- -SELECT * FROM users ORDER BY (CASE WHEN (TRUE) THEN lastname ELSE firstname) ----- - -So we can substitute any kind of boolean operation in the `when(....)` part. The statement will just work because -it is a valid query whether you use a prepared statement or not an order by clause can by definition contain a -expression. - -=== Mitigation - -If you need to provide a sorting column in your web application you should implement a whitelist to validate the value -of the `order by` statement it should always be limited to something like 'firstname' or 'lastname'. diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12b.adoc b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12b.adoc new file mode 100644 index 000000000..41207c3da --- /dev/null +++ b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content12b.adoc @@ -0,0 +1,7 @@ +== Input validation alone is not enough!! + +So the last attempt to validate if the query did not contain any spaces failed, the development team went further +into the direction of only performing input validation, can you find out where it went wrong this time? + + + diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content13.adoc b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content13.adoc index 1e6d01c79..52c8983bf 100644 --- a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content13.adoc +++ b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content13.adoc @@ -1,14 +1,47 @@ -== Least Privilege +== Order by clause -=== Connect with a minimum set of privileges -* The application should connect to the database with different credentials for every trust distinction -* Applications rarely need delete rights to a table or database +Question: Does a prepared statement always prevent against an SQL injection? +Answer: No it does not -=== Database accounts should limit schema access +Let us take a look at the following statement: -=== Define database accounts for read and read/write access +---- +SELECT * FROM users ORDER BY lastname; +---- -=== Multiple connection pools based on access -* Use read only access for the authentication query -* Use read/write access for the data modification queries -* Use execute for access to stored procedure calls +If we look at the specification of the SQL grammar the definition is as follows: + +---- +SELECT ... +FROM tableList +[WHERE Expression] +[ORDER BY orderExpression [, ...]] + +orderExpression: +{ columnNr | columnAlias | selectExpression } + [ASC | DESC] + +selectExpression: +{ Expression | COUNT(*) | { + COUNT | MIN | MAX | SUM | AVG | SOME | EVERY | + VAR_POP | VAR_SAMP | STDDEV_POP | STDDEV_SAMP +} ([ALL | DISTINCT][2]] Expression) } [[AS] label] + +Based on HSQLDB +---- + +This means an `orderExpression` can be a `selectExpression` which can be a function as well, so for example with +a `case` statement we might be able to ask the database some questions, like: + +---- +SELECT * FROM users ORDER BY (CASE WHEN (TRUE) THEN lastname ELSE firstname) +---- + +So we can substitute any kind of boolean operation in the `when(....)` part. The statement will just work because +it is a valid query whether you use a prepared statement or not an order by clause can by definition contain a +expression. + +=== Mitigation + +If you need to provide a sorting column in your web application you should implement a whitelist to validate the value +of the `order by` statement it should always be limited to something like 'firstname' or 'lastname'. diff --git a/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content14.adoc b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content14.adoc new file mode 100644 index 000000000..1e6d01c79 --- /dev/null +++ b/webgoat-lessons/sql-injection/src/main/resources/lessonPlans/en/SqlInjection_content14.adoc @@ -0,0 +1,14 @@ +== Least Privilege + +=== Connect with a minimum set of privileges +* The application should connect to the database with different credentials for every trust distinction +* Applications rarely need delete rights to a table or database + +=== Database accounts should limit schema access + +=== Define database accounts for read and read/write access + +=== Multiple connection pools based on access +* Use read only access for the authentication query +* Use read/write access for the data modification queries +* Use execute for access to stored procedure calls diff --git a/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson12aTest.java b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13Test.java similarity index 98% rename from webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson12aTest.java rename to webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13Test.java index ee8a4231d..7b871ba22 100644 --- a/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson12aTest.java +++ b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlInjectionLesson13Test.java @@ -15,7 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * @since 5/21/17. */ @RunWith(SpringJUnit4ClassRunner.class) -public class SqlInjectionLesson12aTest extends SqlLessonTest { +public class SqlInjectionLesson13Test extends SqlLessonTest { @Test public void knownAccountShouldDisplayData() throws Exception { diff --git a/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywordsTest.java b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywordsTest.java new file mode 100644 index 000000000..3b3ca9680 --- /dev/null +++ b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywordsTest.java @@ -0,0 +1,34 @@ +package org.owasp.webgoat.sql_injection.mitigation; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.owasp.webgoat.sql_injection.SqlLessonTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringJUnit4ClassRunner.class) +public class SqlOnlyInputValidationOnKeywordsTest extends SqlLessonTest { + + @Test + public void solve() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/SqlOnlyInputValidationOnKeywords/attack") + .param("userid_sql_only_input_validation_on_keywords", "Smith';SESELECTLECT/**/*/**/FRFROMOM/**/user_system_data;--")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", is(true))) + .andExpect(jsonPath("$.feedback", containsString("passW0rD"))); + } + + @Test + public void containsForbiddenSqlKeyword() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/SqlOnlyInputValidationOnKeywords/attack") + .param("userid_sql_only_input_validation_on_keywords", "Smith';SELECT/**/*/**/from/**/user_system_data;--")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", is(false))) + .andExpect(jsonPath("$.output", containsString("unexpected token: *
Your query was: SELECT * FROM user_data WHERE last_name = 'SMITH';\\\\\\/**\\\\\\/*\\\\\\/**\\\\\\/\\\\\\/**\\\\\\/USER_SYSTEM_DATA;--'"))); + } +} \ No newline at end of file diff --git a/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationTest.java b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationTest.java new file mode 100644 index 000000000..4961956f5 --- /dev/null +++ b/webgoat-lessons/sql-injection/src/test/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationTest.java @@ -0,0 +1,34 @@ +package org.owasp.webgoat.sql_injection.mitigation; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.owasp.webgoat.sql_injection.SqlLessonTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringJUnit4ClassRunner.class) +public class SqlOnlyInputValidationTest extends SqlLessonTest { + + @Test + public void solve() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/SqlOnlyInputValidation/attack") + .param("userid_sql_only_input_validation", "Smith';SELECT/**/*/**/from/**/user_system_data;--")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", is(true))) + .andExpect(jsonPath("$.feedback", containsString("passW0rD"))); + } + + @Test + public void containsSpace() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/SqlOnlyInputValidation/attack") + .param("userid_sql_only_input_validation", "Smith' ;SELECT from user_system_data;--")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lessonCompleted", is(false))) + .andExpect(jsonPath("$.feedback", containsString("Using spaces is not allowed!"))); + } +} \ No newline at end of file