From 25446cac12ad26e8dd11a133f17bfbd66068ebd8 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 10 Nov 2017 10:37:32 -0300 Subject: [PATCH] Initial import WSF XSS protection. Added an utility class to get safe query and form parameters. Added a new WSF_XSS_REQUEST to use safe parameters. Added a filter WSF_XSS_FILTER using WSF_XSS_REQUEST. Added test cases Signed-off-by: jvelilla --- .../server/wsf/router/filter/wsf_xss_filter.e | 36 +++ .../src/support/wsf_xss_protection_patterns.e | 51 ++++ .../wsf/src/support/wsf_xss_utilities.e | 68 +++++ library/server/wsf/src/wsf_xss_request.e | 59 ++++ .../server/wsf/tests/src/test_xss_patterns.e | 251 ++++++++++++++++++ library/server/wsf/tests/tests.ecf | 5 +- library/server/wsf/wsf.ecf | 5 +- 7 files changed, 471 insertions(+), 4 deletions(-) create mode 100644 library/server/wsf/router/filter/wsf_xss_filter.e create mode 100644 library/server/wsf/src/support/wsf_xss_protection_patterns.e create mode 100644 library/server/wsf/src/support/wsf_xss_utilities.e create mode 100644 library/server/wsf/src/wsf_xss_request.e create mode 100644 library/server/wsf/tests/src/test_xss_patterns.e diff --git a/library/server/wsf/router/filter/wsf_xss_filter.e b/library/server/wsf/router/filter/wsf_xss_filter.e new file mode 100644 index 00000000..c9bc3733 --- /dev/null +++ b/library/server/wsf/router/filter/wsf_xss_filter.e @@ -0,0 +1,36 @@ +note + description: "[ + {WSF_XSS_FILTER}. + Simple anti cross-site scripting (XSS) filter. + Remove all suspicious strings from request parameters (query strings and form) before returning them to the application + + ]" + date: "$Date$" + revision: "$Revision$" + +class + WSF_XSS_FILTER + +inherit + + WSF_FILTER + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + do + execute_next (create {WSF_XSS_REQUEST}.make_from_request (req), res) + end + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/src/support/wsf_xss_protection_patterns.e b/library/server/wsf/src/support/wsf_xss_protection_patterns.e new file mode 100644 index 00000000..722633a1 --- /dev/null +++ b/library/server/wsf/src/support/wsf_xss_protection_patterns.e @@ -0,0 +1,51 @@ +note + description: "[ + {WSF_XSS_PROTECTION_PATTERNS} + Provide application security parterns to assist in Cross Site Scripting + ]" + date: "$Date$" + revision: "$Revision$" + EIS: "name=OWASP XSS", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri" + EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri" + +expanded class + WSF_XSS_PROTECTION_PATTERNS + + +feature -- xss PATTERNS + + XSS_regular_expression: REGULAR_EXPRESSION + note + EIS: "name= XSS", "src=https://community.apigee.com/questions/27198/xss-threat-protection-patterns.html#answer-27465", "protocol=uri" + local + p: STRING_32 + once + p := "((\%%3C)|<)[^\n]+((\%%3E)|>)" + Result := compiled_regexp (p, True) + end + +feature {NONE} -- Implementation + + compiled_regexp (p: STRING; caseless: BOOLEAN): REGULAR_EXPRESSION + require + p /= Void + do + create Result + Result.set_caseless (caseless) + Result.compile (p) + ensure + Result.is_compiled + end + + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/src/support/wsf_xss_utilities.e b/library/server/wsf/src/support/wsf_xss_utilities.e new file mode 100644 index 00000000..ab51566b --- /dev/null +++ b/library/server/wsf/src/support/wsf_xss_utilities.e @@ -0,0 +1,68 @@ +note + description: "Return safe (XSS protection) data for WSF_REQUEST query and form paramters." + date: "$Date$" + revision: "$Revision$" + +class + WSF_XSS_UTILITIES + + + -- TODO add header protection. + +feature -- Query parameters + + safe_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Safe Query parameter for name `a_name'. + local + l_wsf_xss: WSF_XSS_PROTECTION_PATTERNS + r: REGULAR_EXPRESSION + do + r := l_wsf_xss.XSS_regular_expression + Result := a_req.query_parameter (a_name) + if Result /= Void then + if + attached {WSF_STRING} Result as str and then + r.is_compiled + then + r.match (str.value) + if r.has_matched then + create {WSF_STRING} Result.make (str.name, " ") + end + end + end + end + +feature -- Form Parameters + + safe_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Safe Form parameter for name `a_name'. + local + l_wsf_xss: WSF_XSS_PROTECTION_PATTERNS + r: REGULAR_EXPRESSION + do + r := l_wsf_xss.XSS_regular_expression + Result := a_req.form_parameter (a_name) + if Result /= Void then + if + attached {WSF_STRING} Result as str and then + r.is_compiled + then + r.match (str.value) + if r.has_matched then + create {WSF_STRING} Result.make (str.name, " ") + end + end + end + end + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/src/wsf_xss_request.e b/library/server/wsf/src/wsf_xss_request.e new file mode 100644 index 00000000..630ec149 --- /dev/null +++ b/library/server/wsf/src/wsf_xss_request.e @@ -0,0 +1,59 @@ +note + description: "[ + XSS request, redefine query_parameter and form_parameters filtering the data (using XSS protection) + before return the value. + ]" + date: "$Date$" + revision: "$Revision$" + +class + WSF_XSS_REQUEST + +inherit + WSF_REQUEST + redefine + query_parameter, + form_parameter + end + + WSF_REQUEST_EXPORTER + + WSF_XSS_UTILITIES + +create + make_from_request + +feature {NONE} -- Creation + + make_from_request (req: WSF_REQUEST) + do + make_from_wgi (req.wgi_request) + end + +feature -- Query parameters + + query_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Query parameter for name `a_name'. + do + Result := safe_query_parameter (Current, a_name) + end + +feature -- Form Parameters + + form_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + do + Result := safe_form_parameter (Current, a_name) + end + + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/tests/src/test_xss_patterns.e b/library/server/wsf/tests/src/test_xss_patterns.e new file mode 100644 index 00000000..ea5456aa --- /dev/null +++ b/library/server/wsf/tests/src/test_xss_patterns.e @@ -0,0 +1,251 @@ +note + description: "Summary description for {TEST_XSS_PATTERNS}." + date: "$Date$" + revision: "$Revision$" + EIS: "name=XSS Filter Evasion Cheat Sheet", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri" + +class + TEST_XSS_PATTERNS + +inherit + EQA_TEST_SET + +feature -- Tests + + test_xss_locator + local + r: REGULAR_EXPRESSION + s: STRING + do + s:= "[ +';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//"; +alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//-- +>">'> + ]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("XSS locator", r.has_matched) + end + + test_xss_locator_short + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +'';!--"=&{()} + ]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("XSS locator short", r.has_matched) + end + + test_no_filter_evasion + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ + + ]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("No filter evasion", r.has_matched) + end + + test_filter_bypass_based_polyglot + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +'">>"> +<script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script> +<script>alert(document.cookie)</script>"> +<img/id="confirm&lpar;1)"/alt="/"src="/"onerror=eval(id)>'"> +<img src="http://www.shellypalmer.com/wp-content/images/2015/07/hacked-compressor.jpg"> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Filter bypass based polyglot", r.has_matched) + end + + + test_image_xss_js_directive + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC="javascript:alert('XSS');"> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Image XSS using the JavaScript directive", r.has_matched) + end + + + test_no_quotes_no_semicolon + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC=javascript:alert('XSS')> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("No quotes and no semicolon", r.has_matched) + end + + + test_case_insensitive_xss_vector + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC=JaVaScRiPt:alert('XSS')> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Case insensitive XSS attack vector", r.has_matched) + end + + + test_html_entities + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC=javascript:alert(&quot;XSS&quot;)> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("HTML entities", r.has_matched) + end + + test_grave_accent_obfuscation + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Grave accent obfuscation", r.has_matched) + end + + + test_malformed_a_tags + local + r: REGULAR_EXPRESSION + s: STRING + do + -- Skip the HREF attribute and get to the meat of the XXS... Submitted by David Cross ~ Verified on Chrome + s:="[ +<a onmouseover="alert(document.cookie)">xxs link</a> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Malformed A tags", r.has_matched) + end + + test_malformed_a_tags_2 + local + r: REGULAR_EXPRESSION + s: STRING + do + -- Chrome loves to replace missing quotes for you... if you ever get stuck just leave them off and Chrome will put them + -- in the right place and fix your missing quotes on a URL or script. + s:="[ +<a onmouseover=alert(document.cookie)>xxs link</a> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Malformed A tags", r.has_matched) + end + + + test_malformed_img + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG """><SCRIPT>alert("XSS")</SCRIPT>"> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Malformed IMG tags", r.has_matched) + end + + + test_from_char_code + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("fromCharCode", r.has_matched) + end + + + test_default_src_tag + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC=# onmouseover="alert('xxs')"> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Default SRC tag to get past filters that check SRC domain", r.has_matched) + end + + + test_default_src_tag_2 + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG SRC= onmouseover="alert('xxs')"> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Default SRC tag by leaving it empty", r.has_matched) + end + + test_default_src_tag_3 + local + r: REGULAR_EXPRESSION + s: STRING + do + s:="[ +<IMG onmouseover="alert('xxs')"> +]" + r:= xss_pattern.XSS_regular_expression + r.match (s) + assert ("Default SRC tag by leaving it out entirely", r.has_matched) + end + + + + + + + +feature {NONE} -- Implementation + + xss_pattern: WSF_XSS_PROTECTION_PATTERNS + +end diff --git a/library/server/wsf/tests/tests.ecf b/library/server/wsf/tests/tests.ecf index 0f2564c8..d4f4b4a1 100644 --- a/library/server/wsf/tests/tests.ecf +++ b/library/server/wsf/tests/tests.ecf @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="ISO-8859-1"?> -<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="wsf_tests" uuid="C4FF9CDA-B4E4-4841-97E0-7F799B85B657"> +<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-17-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-17-0 http://www.eiffel.com/developers/xml/configuration-1-17-0.xsd" name="wsf_tests" uuid="C4FF9CDA-B4E4-4841-97E0-7F799B85B657"> <target name="server"> <root class="TEST" feature="make"/> <file_rule> + <exclude>/EIFGENs$</exclude> <exclude>/\.git$</exclude> <exclude>/\.svn$</exclude> - <exclude>/EIFGENs$</exclude> </file_rule> <option debug="false" warning="true"> <assertions precondition="true" postcondition="true" check="true" loop="true" supplier_precondition="true"/> @@ -21,6 +21,7 @@ </library> <library name="http" location="..\..\..\network\protocol\http\http.ecf" readonly="false"/> <library name="http_client" location="..\..\..\network\http_client\net_http_client.ecf" readonly="false"/> + <library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/> <library name="wsf" location="..\wsf.ecf" readonly="false"> <option> diff --git a/library/server/wsf/wsf.ecf b/library/server/wsf/wsf.ecf index 231927ad..2576bc7b 100644 --- a/library/server/wsf/wsf.ecf +++ b/library/server/wsf/wsf.ecf @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="ISO-8859-1"?> -<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="wsf" uuid="A37CE5AA-4D2A-4441-BC6A-0A1D7EC49647" library_target="wsf"> +<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-17-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-17-0 http://www.eiffel.com/developers/xml/configuration-1-17-0.xsd" name="wsf" uuid="A37CE5AA-4D2A-4441-BC6A-0A1D7EC49647" library_target="wsf"> <target name="wsf"> <root all_classes="true"/> <file_rule> + <exclude>/EIFGENs$</exclude> <exclude>/\.git$</exclude> <exclude>/\.svn$</exclude> - <exclude>/EIFGENs$</exclude> </file_rule> <option warning="true"> </option> @@ -18,6 +18,7 @@ <library name="error" location="..\..\utility\general\error\error.ecf"/> <library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/> <library name="http" location="..\..\network\protocol\http\http.ecf"/> + <library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/> <library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/> <library name="uri_template" location="..\..\text\parser\uri_template\uri_template.ecf"/>