From 25446cac12ad26e8dd11a133f17bfbd66068ebd8 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Fri, 10 Nov 2017 10:37:32 -0300 Subject: [PATCH 1/2] 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"/> From 5de024923e34191c3e524d2fca4e3936799ed7de Mon Sep 17 00:00:00 2001 From: jvelilla <javier.hector@gmail.com> Date: Wed, 22 Nov 2017 17:22:02 -0300 Subject: [PATCH 2/2] Updated xss support. Added a new library wsf_security. Updated test cases to cover protections policy. Added a simple filter using an XSS implementation with WSF_XSS_REQUEST, but it's possible to build custom filters and request using different protection patterns. --- .../support/wsf_protection_patterns.e | 51 -- .../wsf/extension/support/wsf_xss_utilities.e | 108 ---- .../server/wsf/extension/wsf_xss_request.e | 59 -- .../filter/wsf_xss_filter.e | 0 .../support/wsf_protection_patterns.e | 106 ++++ .../security/support/wsf_protection_policy.e | 518 ++++++++++++++++++ library/server/wsf/security/wsf_xss_request.e | 260 +++++++++ .../tests/src/test_wsf_protection_policy.e | 129 +++++ library/server/wsf/tests/tests.ecf | 2 +- library/server/wsf/wsf_extension.ecf | 1 - library/server/wsf/wsf_security.ecf | 22 + 11 files changed, 1036 insertions(+), 220 deletions(-) delete mode 100644 library/server/wsf/extension/support/wsf_protection_patterns.e delete mode 100644 library/server/wsf/extension/support/wsf_xss_utilities.e delete mode 100644 library/server/wsf/extension/wsf_xss_request.e rename library/server/wsf/{extension => security}/filter/wsf_xss_filter.e (100%) create mode 100644 library/server/wsf/security/support/wsf_protection_patterns.e create mode 100644 library/server/wsf/security/support/wsf_protection_policy.e create mode 100644 library/server/wsf/security/wsf_xss_request.e create mode 100644 library/server/wsf/tests/src/test_wsf_protection_policy.e create mode 100644 library/server/wsf/wsf_security.ecf diff --git a/library/server/wsf/extension/support/wsf_protection_patterns.e b/library/server/wsf/extension/support/wsf_protection_patterns.e deleted file mode 100644 index 43007497..00000000 --- a/library/server/wsf/extension/support/wsf_protection_patterns.e +++ /dev/null @@ -1,51 +0,0 @@ -note - description: "[ - {WSF_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_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/extension/support/wsf_xss_utilities.e b/library/server/wsf/extension/support/wsf_xss_utilities.e deleted file mode 100644 index 4f3b868f..00000000 --- a/library/server/wsf/extension/support/wsf_xss_utilities.e +++ /dev/null @@ -1,108 +0,0 @@ -note - description: "Return safe (XSS protection) data for WSF_REQUEST query and form parameters." - 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_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_PROTECTION_PATTERNS - r: REGULAR_EXPRESSION - not_first: BOOLEAN - 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 - elseif - attached {WSF_MULTIPLE_STRING} Result as l_multi_str and then - r.is_compiled - then - across l_multi_str as ic loop - r.match (ic.item.value) - if r.has_matched then - if not_first and then attached {WSF_MULTIPLE_STRING} Result as l_result then - l_result.add_value ( (create {WSF_STRING}.make (ic.item.name, " "))) - else - create {WSF_MULTIPLE_STRING} Result.make_with_string (ic.item.name, " ") - not_first := True - end - end - end - end - - end - end - -feature -- Meta Variables - - safe_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING - -- CGI Meta variable related to `a_name' - require - a_name_valid: a_name /= Void and then not a_name.is_empty - local - l_wsf_xss: WSF_PROTECTION_PATTERNS - r: REGULAR_EXPRESSION - do - r := l_wsf_xss.XSS_regular_expression - Result := a_req.meta_variable (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/extension/wsf_xss_request.e b/library/server/wsf/extension/wsf_xss_request.e deleted file mode 100644 index 630ec149..00000000 --- a/library/server/wsf/extension/wsf_xss_request.e +++ /dev/null @@ -1,59 +0,0 @@ -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/extension/filter/wsf_xss_filter.e b/library/server/wsf/security/filter/wsf_xss_filter.e similarity index 100% rename from library/server/wsf/extension/filter/wsf_xss_filter.e rename to library/server/wsf/security/filter/wsf_xss_filter.e diff --git a/library/server/wsf/security/support/wsf_protection_patterns.e b/library/server/wsf/security/support/wsf_protection_patterns.e new file mode 100644 index 00000000..a41e3e74 --- /dev/null +++ b/library/server/wsf/security/support/wsf_protection_patterns.e @@ -0,0 +1,106 @@ +note + description: "[ + {WSF_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_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 + + XSS_javascript_expression: REGULAR_EXPRESSION + note + EIS: "name=JavaScript Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri" + local + p: STRING_32 + once + p := "<\s*script\b[^>]*>[^<]+<\s*/\s*script\s*>" + Result := compiled_regexp (p, True) + end + +feature -- XPath injections Patterns + + XPath_abbreviated_expression: REGULAR_EXPRESSION + note + EIS: "name=XPath Abbreviated Syntax Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri" + local + p: STRING_32 + once + p := "(/(@?[\w_?\w:\*]+(\[[^]]+\])*)?)+" + Result := compiled_regexp (p, True) + end + + XPath_expanded_expression: REGULAR_EXPRESSION + note + EIS: "name=XPath Expanded Syntax Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri" + local + p: STRING_32 + once + p := "/?(ancestor(-or-self)?|descendant(-or-self)?|following(-sibling))" + Result := compiled_regexp (p, True) + end + +feature -- Server side injection + + Server_side_expression: REGULAR_EXPRESSION + note + EIS: "name=Server-Side Include Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri" + local + p: STRING_32 + once + p := "<!--#(include|exec|echo|config|printenv)\s+.*" + Result := compiled_regexp (p, True) + end + +feature -- SQL injection Patterns + + SQL_injection_regular_expression: REGULAR_EXPRESSION + note + EIS: "name= SQL Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri" + local + p: STRING_32 + once + p := "[\s]*((delete)|(exec)|(drop\s*table)|(insert)|(shutdown)|(update)|(\bor\b))" + 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/security/support/wsf_protection_policy.e b/library/server/wsf/security/support/wsf_protection_policy.e new file mode 100644 index 00000000..9a317260 --- /dev/null +++ b/library/server/wsf/security/support/wsf_protection_policy.e @@ -0,0 +1,518 @@ +note + description: "Return data for WSF_REQUEST query and form parameters using different types of protection policy" + date: "$Date$" + revision: "$Revision$" + +class + WSF_PROTECTION_POLICY + + -- TODO add header protection. + +feature -- Query parameters + + custom_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with custom protections. + do + Result := custom_wsf_value (a_req.query_parameter (a_name), a_protections) + end + + predefined_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with all predefined protections. + -- check {WSF_PROTECTION_PATTERNS} class. + do + Result := predefined_value (a_req.query_parameter (a_name)) + end + + xss_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with xss protection. + do + Result := xss_value (a_req.query_parameter (a_name)) + end + + xss_js_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with xss protection. + do + Result := xss_js_value (a_req.query_parameter (a_name)) + end + + sql_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with sql injection protection. + do + Result := sql_value (a_req.query_parameter (a_name)) + end + + server_side_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with server side injection protection. + do + Result := server_side_value (a_req.query_parameter (a_name)) + end + + xpath_abbreviated_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with XPath_abbreviated injection protection. + do + Result := xpath_abbreviated_value (a_req.query_parameter (a_name)) + end + + xpath_expanded_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Query parameter name `a_name' with XPath expanded injection protection. + do + Result := xpath_expanded_value (a_req.query_parameter (a_name)) + end + +feature -- Form Parameters + + custom_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with custom protections. + do + Result := custom_wsf_value (a_req.form_parameter (a_name), a_protections) + end + + predefined_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with all predefined protections. + -- check {WSF_PROTECTION_PATTERNS} class. + do + Result := predefined_value (a_req.form_parameter (a_name)) + end + + xss_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with xss protection. + do + Result := xss_value (a_req.form_parameter (a_name)) + end + + xss_js_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with xss protection. + do + Result := xss_js_value (a_req.form_parameter (a_name)) + end + + sql_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with sql injection protection. + do + Result := sql_value (a_req.form_parameter (a_name)) + end + + server_side_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with server side injection protection. + do + Result := server_side_value (a_req.form_parameter (a_name)) + end + + xpath_abbreviated_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with server Xpath abbreviated injection protection. + do + Result := xpath_abbreviated_value (a_req.form_parameter (a_name)) + end + + xpath_expanded_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered Form parameter name `a_name' with server Xpath expanded injection protection. + do + Result := xpath_expanded_value (a_req.form_parameter (a_name)) + end + +feature -- Meta Variables + + custom_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable WSF_VALUE + -- Filtered CGI Meta variable name `a_name' with custom protections. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} custom_wsf_value (a_req.meta_variable (a_name), a_protections) as l_result then + Result := l_result + end + end + + predefined_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- Filtered CGI Meta variable name `a_name' with predefined protections. + -- check {WSF_PROTECTION_PATTERNS} class. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} predefined_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end + + xss_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- Filtered CGI Meta variable name `a_name' with xss protection. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} xss_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end + + xss_js_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- Filtered CGI Meta variable name `a_name' with xss protection. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} xss_js_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end + + sql_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- Filtered CGI Meta variable name `a_name' with sql injection protection. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} sql_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end + + server_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- Filtered CGI Meta variable name `a_name' with server side injection protection. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} server_side_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end + + xpath_abbreviated_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- Filtered CGI Meta variable name `a_name' with Xpath abbreviated injection protection. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} xpath_abbreviated_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end + + xpath_expanded_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- Filtered CGI Meta variable name `a_name' with Xpath abbreviated injection protection. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached {WSF_STRING} xpath_expanded_value (a_req.meta_variable (a_name)) as l_result then + Result := l_result + end + end +feature -- HTTP_* + + custom_http_accept (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_accept header with custom protections `a_protections`. + -- Contents of the Accept: header from the current wgi_request, if there is one. + -- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' + do + Result := custom_string_value (a_req.http_accept, a_protections) + end + + custom_http_accept_charset (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_accept_charset header with custom protections `a_protections`. + -- Contents of the Accept-Charset: header from the current wgi_request, if there is one. + -- Example: 'iso-8859-1,*,utf-8'. + + do + Result := custom_string_value (a_req.http_accept_charset, a_protections) + end + + custom_http_accept_encoding (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_accept_encoding header with custom protections `a_protections`. + -- Contents of the Accept-Encoding: header from the current wgi_request, if there is one. + -- Example: 'gzip'. + do + Result := custom_string_value (a_req.http_accept_encoding, a_protections) + end + + custom_http_accept_language (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_accept_language header with custom protections `a_protections`. + -- Contents of the Accept-Language: header from the current wgi_request, if there is one. + -- Example: 'en'. + do + Result := custom_string_value (a_req.http_accept_language, a_protections) + end + + custom_http_connection (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_connection header with custom protections `a_protections`. + -- Contents of the Connection: header from the current wgi_request, if there is one. + -- Example: 'keep-alive'. + do + Result := custom_string_value (a_req.http_connection, a_protections) + end + + custom_http_expect (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_expect header with custom protections `a_protections`. + -- The Expect request-header field is used to indicate that particular server behaviors are required by the client. + -- Example: '100-continue'. + do + Result := custom_string_value (a_req.http_expect, a_protections) + end + + custom_http_host (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_host header with custom protections `a_protections`. + -- Contents of the Host: header from the current wgi_request, if there is one. + do + Result := custom_string_value (a_req.http_host, a_protections) + end + + custom_http_referer (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_referer header with custom protections `a_protections`. + -- The address of the page (if any) which referred the user agent to the current page. + -- This is set by the user agent. + -- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. + -- In short, it cannot really be trusted. + do + Result := custom_string_value (a_req.http_referer, a_protections) + end + + custom_http_user_agent (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_user_agent header with custom protections `a_protections`. + -- Contents of the User-Agent: header from the current wgi_request, if there is one. + -- This is a string denoting the user agent being which is accessing the page. + -- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586). + -- Among other things, you can use this value to tailor your page's + -- output to the capabilities of the user agent. + do + Result := custom_string_value (a_req.http_user_agent, a_protections) + end + + custom_http_authorization (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_authorization header with custom protections `a_protections`. + -- Contents of the Authorization: header from the current wgi_request, if there is one. + do + Result := custom_string_value (a_req.http_authorization, a_protections) + end + + custom_http_transfer_encoding (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_transfer_encoding header with custom protections `a_protections`. + -- Transfer-Encoding + -- for instance chunked. + do + Result := custom_string_value (a_req.http_transfer_encoding, a_protections) + end + + custom_http_access_control_request_headers (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_access_control_request_headers header with custom protections `a_protections`. + -- Indicates which headers will be used in the actual request + -- as part of the preflight request + do + Result := custom_string_value (a_req.http_access_control_request_headers, a_protections) + end + + custom_http_if_match (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_if_match header with custom protections `a_protections`. + -- Existence check on resource. + do + Result := custom_string_value (a_req.http_if_match, a_protections) + end + + custom_http_if_modified_since (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_if_modified_since header with custom protections `a_protections`. + -- Modification check on resource. + do + Result := custom_string_value (a_req.http_if_modified_since, a_protections) + end + + custom_http_if_none_match (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_if_none_match header with custom protections `a_protections`. + -- Existence check on resource. + do + Result := custom_string_value (a_req.http_if_none_match, a_protections) + end + + custom_http_if_range (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_if_range header with custom protections `a_protections`. + -- Existence check on resource. + do + Result := custom_string_value (a_req.http_if_range, a_protections) + end + + custom_http_if_unmodified_since (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_if_unmodified_since header with custom protections `a_protections`. + -- Modification check on resource. + do + Result := custom_string_value (a_req.http_if_unmodified_since, a_protections) + end + + custom_http_last_modified (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_last_modified header with custom protections `a_protections`. + -- Modification check on resource. + do + Result := custom_string_value (a_req.http_last_modified, a_protections) + end + + custom_http_range (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_range header with custom protections `a_protections`. + -- Requested byte-range of resource. + do + Result := custom_string_value (a_req.http_range, a_protections) + end + + custom_http_content_range (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_content_range header with custom protections `a_protections`. + -- Partial range of selected representation enclosed in message payload. + do + Result := custom_string_value (a_req.http_content_range, a_protections) + end + + custom_http_content_encoding (a_req: WSF_REQUEST; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Filtered http_content_encoding header with custom protections `a_protections`. + -- Encoding (usually compression) of message payload. + do + Result := custom_string_value (a_req.http_content_encoding, a_protections) + end + +feature {NONE} -- Implementation + + custom_wsf_value (a_value: detachable WSF_VALUE; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable WSF_VALUE + -- Return value `a_value` filtered by all protections policy. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, a_protections ) + end + + custom_string_value (a_value: detachable READABLE_STRING_8; a_protections: ARRAY [REGULAR_EXPRESSION]): detachable READABLE_STRING_8 + -- Return value `a_value` filtered by all protections policy. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_string_value (a_value, a_protections ) + end + + predefined_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by all predefined protections policy. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, + {ARRAY [REGULAR_EXPRESSION]}<< + l_wsf_xss.XSS_regular_expression, + l_wsf_xss.server_side_expression, + l_wsf_xss.sql_injection_regular_expression, + l_wsf_xss.xpath_abbreviated_expression, + l_wsf_xss.xpath_expanded_expression>>) + end + + xss_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by xss protection. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, {ARRAY [REGULAR_EXPRESSION]}<<l_wsf_xss.XSS_regular_expression>>) + end + + xss_js_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by xss-javascript protection. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, {ARRAY [REGULAR_EXPRESSION]} <<l_wsf_xss.XSS_javascript_expression>>) + end + + sql_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by sql injection protection. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, {ARRAY [REGULAR_EXPRESSION]} <<l_wsf_xss.SQL_injection_regular_expression>>) + end + + server_side_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by server side injection protection. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, {ARRAY [REGULAR_EXPRESSION]} <<l_wsf_xss.Server_side_expression>>) + end + + xpath_abbreviated_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by xpath_abbreviated injection protection. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, {ARRAY [REGULAR_EXPRESSION]} <<l_wsf_xss.Xpath_abbreviated_expression>>) + end + + xpath_expanded_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE + -- Return value `a_value` filtered by Xpath expanded injection protection. + local + l_wsf_xss: WSF_PROTECTION_PATTERNS + do + Result := filter_wsf_value (a_value, {ARRAY [REGULAR_EXPRESSION]} <<l_wsf_xss.Xpath_expanded_expression>>) + end + + filter_wsf_value (a_value: detachable WSF_VALUE; a_regex: ARRAY [REGULAR_EXPRESSION] ): detachable WSF_VALUE + -- Filter value `a_value` with an array of protections policy `a_regex`. + local + not_first: BOOLEAN + do + Result := a_value + if Result /= Void then + if + attached {WSF_STRING} Result as str and then + a_regex.for_all (agent is_compiled) + then + a_regex.do_all (agent match (?, str.value)) + if a_regex.there_exists (agent has_matched) then + create {WSF_STRING} Result.make (str.name, " ") + end + elseif + attached {WSF_MULTIPLE_STRING} Result as l_multi_str and then + a_regex.for_all (agent is_compiled) + then + across l_multi_str as ic loop + a_regex.do_all (agent match (?, ic.item.value)) + if a_regex.there_exists (agent has_matched ) then + if not_first and then attached {WSF_MULTIPLE_STRING} Result as l_result then + l_result.add_value ( (create {WSF_STRING}.make (ic.item.name, " "))) + else + create {WSF_MULTIPLE_STRING} Result.make_with_string (ic.item.name, " ") + not_first := True + end + end + end + end + end + end + + filter_string_value (a_value: detachable READABLE_STRING_8; a_regex: ARRAY [REGULAR_EXPRESSION] ): detachable READABLE_STRING_8 + -- Filter value `a_value` with an array of protections policy `a_regex`. + do + Result := a_value + if Result /= Void then + if + attached a_value as l_value and then + a_regex.for_all (agent is_compiled) + then + a_regex.do_all (agent match (?, l_value)) + if a_regex.there_exists (agent has_matched) then + create {STRING_8} Result.make_empty + end + end + end + end + + is_compiled (a_regex: REGULAR_EXPRESSION): BOOLEAN + -- Is the regular expression 'a_regex' compiled? + do + Result := a_regex.is_compiled + end + + match (a_regex: REGULAR_EXPRESSION; a_value: READABLE_STRING_32) + do + a_regex.match (a_value) + end + + has_matched (a_regex: REGULAR_EXPRESSION): BOOLEAN + do + Result := a_regex.has_matched + 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/security/wsf_xss_request.e b/library/server/wsf/security/wsf_xss_request.e new file mode 100644 index 00000000..342efd7b --- /dev/null +++ b/library/server/wsf/security/wsf_xss_request.e @@ -0,0 +1,260 @@ +note + description: "[ + XSS request, redefine query_parameter and form_parameters filtering the data (using XSS protection) + before return the value. + The XSS protection pattern used is defined here :{WSF_PROTECTION_PATTERNS}.XSS_regular_expression: REGULAR_EXPRESSION + + ]" + date: "$Date$" + revision: "$Revision$" + +class + WSF_XSS_REQUEST + +inherit + WSF_REQUEST + redefine + query_parameter, + form_parameter, + meta_variable, + http_accept, + http_accept_charset, + http_accept_encoding, + http_accept_language, + http_connection, + http_expect, + http_host, + http_referer, + http_user_agent, + http_authorization, + http_transfer_encoding, + http_access_control_request_headers, + http_if_match, + http_if_modified_since, + http_if_none_match, + http_if_range, + http_if_unmodified_since, + http_last_modified, + http_range, + http_content_range, + http_content_encoding + end + + WSF_REQUEST_EXPORTER + + WSF_PROTECTION_POLICY + +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 + -- <Precursor> + do + Result := xss_query_parameter (Current, a_name) + end + +feature -- Form Parameters + + form_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE + -- <Precursor> + do + Result := xss_form_parameter (Current, a_name) + end + +feature -- Meta Variable + + meta_variable (a_name: READABLE_STRING_GENERAL): detachable WSF_STRING + -- <Precursor> + do + Result := xss_meta_variable (Current, a_name) + end + +feature -- HTTP_* + + http_accept: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_accept (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_accept_charset: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_accept_charset (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_accept_encoding: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_accept_encoding (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_accept_language: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_accept_language (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_connection: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_connection (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_expect: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_expect (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_host: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_host (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_referer: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_referer (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_user_agent: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_user_agent (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_authorization: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_authorization (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_transfer_encoding: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_transfer_encoding (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_access_control_request_headers: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_access_control_request_headers (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_if_match: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_if_match (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_if_modified_since: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_if_modified_since (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_if_none_match: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_if_none_match (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_if_range: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_if_range (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_if_unmodified_since: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_if_unmodified_since (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_last_modified: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_last_modified (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_range: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_range (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_content_range: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_content_range (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + end + + http_content_encoding: detachable READABLE_STRING_8 + -- <Precursor> + local + l_protection: WSF_PROTECTION_PATTERNS + do + Result := custom_http_content_encoding (Current, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) + 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_wsf_protection_policy.e b/library/server/wsf/tests/src/test_wsf_protection_policy.e new file mode 100644 index 00000000..e5b8bd7e --- /dev/null +++ b/library/server/wsf/tests/src/test_wsf_protection_policy.e @@ -0,0 +1,129 @@ +note + description: "Summary description for {TEST_WSF_PROTECTION_POLICY}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + TEST_WSF_PROTECTION_POLICY + +inherit + EQA_TEST_SET + WSF_SERVICE + undefine + default_create + end + +feature -- Test + + test_http_expect_attack_without_xss_protection + local + req: WSF_REQUEST + + do + --| Case HTTP header expect attack, no filtered. + req := new_request (<< + ["REQUEST_METHOD", "GET"], + ["QUERY_STRING", ""], + ["REQUEST_URI", "/cookie"], + ["HTTP_EXPECT", "<script>alert(XSS attack)</script>"] + >> + ) + -- no filter + assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", attached req.http_expect as v and then v.is_case_insensitive_equal ("<script>alert(XSS attack)</script>")) + end + + test_http_expect_attack_with_xss_protection + local + req: WSF_REQUEST + sec: WSF_PROTECTION_POLICY + l_protection: WSF_PROTECTION_PATTERNS + do + create sec + --| Case HTTP header expect attack, filtered using {xss_regular_expression} + req := new_request (<< + ["REQUEST_METHOD", "GET"], + ["QUERY_STRING", ""], + ["REQUEST_URI", "/xss_example"], + ["HTTP_EXPECT", "<script>alert(XSS attack)</script>"] + >> + ) + assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", attached {READABLE_STRING_8} sec.custom_http_expect (req, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) as v and then v.is_empty ) + end + + + test_http_expect_attack_with_xss_js_protection + local + req: WSF_REQUEST + sec: WSF_PROTECTION_POLICY + l_protection: WSF_PROTECTION_PATTERNS + do + create sec + --| Case HTTP header expect attack, filtered using {xss_javascript_expression} + req := new_request (<< + ["REQUEST_METHOD", "GET"], + ["QUERY_STRING", ""], + ["REQUEST_URI", "/xss_example"], + ["HTTP_EXPECT", "<script>alert(XSS attack)</script>"] + >> + ) + assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", attached {READABLE_STRING_8} sec.custom_http_expect (req, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_javascript_expression>>) as v and then v.is_empty ) + end + + test_http_referer_attack_with_xss_js_protection_fails + local + req: WSF_REQUEST + sec: WSF_PROTECTION_POLICY + l_protection: WSF_PROTECTION_PATTERNS + l_str: STRING + do + l_str:= "[ + Referer: http://www.google.com/search?hl=en&q=fe525"-alert(1)-"d116a885fd5 + ]" + create sec + --| Case HTTP header referer attack, filtered using {xss_javascript_expression} + req := new_request (<< + ["REQUEST_METHOD", "GET"], + ["QUERY_STRING", ""], + ["REQUEST_URI", "/xss_example"], + ["HTTP_REFERER", l_str] + >> + ) + assert ("HTTP_REFERER", attached {READABLE_STRING_8} sec.custom_http_referer (req, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_javascript_expression>>) as v and then not v.is_empty ) + end + + + test_http_referer_attack_with_xss_protection + local + req: WSF_REQUEST + sec: WSF_PROTECTION_POLICY + l_protection: WSF_PROTECTION_PATTERNS + l_str: STRING + do + l_str:= "[ + Referer: http://www.google.com/search?hl=en&q=fe525"-alert(1)-"d116a885fd5 + ]" + create sec + --| Case HTTP header referer attack, filtered using {xss_javascript_expression} + req := new_request (<< + ["REQUEST_METHOD", "GET"], + ["QUERY_STRING", ""], + ["REQUEST_URI", "/xss_example"], + ["HTTP_REFERER", l_str] + >> + ) + assert ("HTTP_REFERER", attached {READABLE_STRING_8} sec.custom_http_referer (req, {ARRAY [REGULAR_EXPRESSION]}<<l_protection.xss_regular_expression>>) as v and then not v.is_empty ) + end + + +feature {NONE} -- Implementation + + new_request (a_meta: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]): WSF_REQUEST_NULL + local + wgi_req: WGI_REQUEST + do + create {WGI_REQUEST_NULL} wgi_req.make_with_file (a_meta, io.input) + create Result.make_from_wgi (wgi_req) + end + +end diff --git a/library/server/wsf/tests/tests.ecf b/library/server/wsf/tests/tests.ecf index 8c752d43..c63fb036 100644 --- a/library/server/wsf/tests/tests.ecf +++ b/library/server/wsf/tests/tests.ecf @@ -28,7 +28,7 @@ <assertions precondition="true" postcondition="true" check="true" supplier_precondition="true"/> </option> </library> - <library name="wsf_extension" location="..\wsf_extension.ecf" readonly="false"/> + <library name="wsf_security" location="..\wsf_security.ecf" readonly="false"/> <library name="wsf_standalone" location="..\..\wsf\connector\standalone.ecf" readonly="false"/> <cluster name="server" location=".\server\" recursive="true"/> </target> diff --git a/library/server/wsf/wsf_extension.ecf b/library/server/wsf/wsf_extension.ecf index 7803d38f..0cb1459e 100644 --- a/library/server/wsf/wsf_extension.ecf +++ b/library/server/wsf/wsf_extension.ecf @@ -14,7 +14,6 @@ <library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/> <library name="http" location="..\..\network\protocol\http\http.ecf"/> <library name="process" location="$ISE_LIBRARY\library\process\base\base_process.ecf"/> - <library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/> <library name="wsf" location="wsf.ecf"/> <library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/> <cluster name="extension" location=".\extension\" recursive="true"/> diff --git a/library/server/wsf/wsf_security.ecf b/library/server/wsf/wsf_security.ecf new file mode 100644 index 00000000..6684ccee --- /dev/null +++ b/library/server/wsf/wsf_security.ecf @@ -0,0 +1,22 @@ +<?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_security" uuid="6684959A-6F63-4861-A98E-7E144AE77F2E" library_target="wsf_security"> + <target name="wsf_security"> + <root all_classes="true"/> + <file_rule> + <exclude>/EIFGENs$</exclude> + <exclude>/\.git$</exclude> + <exclude>/\.svn$</exclude> + </file_rule> + <option warning="true"> + </option> + <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> + <library name="encoder" location="..\..\text\encoder\encoder.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="process" location="$ISE_LIBRARY\library\process\base\base_process.ecf"/> + <library name="wsf" location="wsf.ecf"/> + <library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/> + <cluster name="security" location=".\security\" recursive="true"/> + </target> +</system>