├── .classpath ├── .gitignore ├── .project ├── README ├── WEB-INF └── web.xml.snippet ├── build.xml ├── lib ├── javax.servlet.jar ├── junit-4.4.jar └── mockito-all-1.8.0.jar ├── src └── com │ └── slocumbrau │ └── filter │ ├── InjectionAttackFilter.java │ └── InjectionAttackWrapper.java ├── test └── com │ └── slocumbrau │ └── filter │ ├── AllowedInputTest.java │ ├── ConfigurationParametersTest.java │ ├── CookieXSSTest.java │ ├── EventHandlerInjectionTest.java │ ├── InjectionAttackFilterTest.java │ ├── InjectionAttackWrapperTest.java │ └── SQLInjectionTest.java └── todo.txt /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | classes 2 | jars 3 | junit 4 | testclasses 5 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | InjectionAttackFilter 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | InjectionAttackFilter is a subclass of HttpServletRequestWrapper that will protect against common sql injection and cross-site scripting attacks. 2 | 3 | The filter will look at inbound request parameters, and remove any suspicious characters, letting the remaining data through. If someone submits a parameter: 4 | 5 | name="" 6 | 7 | your application will see the harmless: 8 | 9 | name="scriptalert('hi!');script" 10 | 11 | I chose to do this because I generally want to have incomplete data rather than no data. Also, there could be some situation where an end user has a name that triggers the filter. In that case, I'd rather have partial legitimate user data rather than none. Future enhancements will allow you to totally block any parameter that is suspicious. 12 | 13 | 14 | It's covered under the MIT license. 15 | 16 | Copyright (c) 2011 Andrew C Slocum 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /WEB-INF/web.xml.snippet: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | injectionAttackFilter 9 | com.slocumbrau.filter.InjectionAttackFilter 10 | 11 | 12 | 13 | filter_xss 14 | true 15 | 16 | 17 | filter_sql_injection 18 | true 19 | 20 | 21 | 22 | click_jacking_header 23 | true 24 | 25 | 26 | 27 | 28 | injectionAttackFilter 29 | /* 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /lib/javax.servlet.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acslocum/InjectionAttackFilter/803e3eb85c0c283d81390737153bf3dd0af84770/lib/javax.servlet.jar -------------------------------------------------------------------------------- /lib/junit-4.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acslocum/InjectionAttackFilter/803e3eb85c0c283d81390737153bf3dd0af84770/lib/junit-4.4.jar -------------------------------------------------------------------------------- /lib/mockito-all-1.8.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acslocum/InjectionAttackFilter/803e3eb85c0c283d81390737153bf3dd0af84770/lib/mockito-all-1.8.0.jar -------------------------------------------------------------------------------- /src/com/slocumbrau/filter/InjectionAttackFilter.java: -------------------------------------------------------------------------------- 1 | package com.slocumbrau.filter; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | public class InjectionAttackFilter implements Filter { 15 | private static final String X_FRAME_VALUE = "SAMEORIGIN"; 16 | private static final String X_FRAME_HEADER = "X-FRAME-OPTIONS"; 17 | public static final String FILTER_XSS_PARAM_NAME = "filter_xss"; 18 | public static final String FILTER_SQL_INJECTION_PARAM_NAME = "filter_sql_injection"; 19 | public static final String CLICK_JACKING_HEADER = "click_jacking_header"; 20 | boolean filterXSS = true; 21 | boolean filterSQL = true; 22 | boolean clickJacking = true; 23 | 24 | @Override 25 | public void destroy() { 26 | } 27 | 28 | @Override 29 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 30 | InjectionAttackWrapper wrapper = new InjectionAttackWrapper((HttpServletRequest) servletRequest, filterXSS, filterSQL); 31 | filterClickJack(servletResponse); 32 | filterChain.doFilter(wrapper, servletResponse); 33 | } 34 | 35 | private void filterClickJack(ServletResponse servletResponse) { 36 | if(clickJacking) { 37 | if(servletResponse instanceof HttpServletResponse) { 38 | HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse; 39 | if(!httpServletResponse.containsHeader(X_FRAME_HEADER)) { 40 | httpServletResponse.addHeader(X_FRAME_HEADER, X_FRAME_VALUE); 41 | } 42 | } 43 | } 44 | } 45 | 46 | @Override 47 | public void init(FilterConfig config) throws ServletException { 48 | String filterXSSParam = config.getInitParameter(FILTER_XSS_PARAM_NAME); 49 | String filterSQLParam = config.getInitParameter(FILTER_SQL_INJECTION_PARAM_NAME); 50 | String clickJackingParam = config.getInitParameter(CLICK_JACKING_HEADER); 51 | filterXSS = new Boolean(filterXSSParam); 52 | filterSQL = new Boolean(filterSQLParam); 53 | clickJacking = new Boolean(clickJackingParam); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/com/slocumbrau/filter/InjectionAttackWrapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2011 Andrew C Slocum 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | **/ 10 | 11 | package com.slocumbrau.filter; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | import javax.servlet.http.Cookie; 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.servlet.http.HttpServletRequestWrapper; 20 | 21 | public class InjectionAttackWrapper extends HttpServletRequestWrapper { 22 | private static final String EVENTS = "((?i)onload|onunload|onchange|onsubmit|onreset" + "|onselect|onblur|onfocus|onkeydown|onkeypress|onkeyup" 23 | + "|onclick|ondblclick|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup)"; 24 | private static final String XSS_HTML_TAG = "(%3C)|(%3E)|[<>]+"; 25 | private static final String XSS_INJECTION = "((%22%20)|(%22\\s)|('%22)|(%22\\+))\\w.*|(\\s|%20)" + EVENTS + ".*|(%3D)|(%7C)"; 26 | private static final String XSS_REGEX = XSS_HTML_TAG + "|" + XSS_INJECTION; 27 | private static final String SQL_REGEX = "('.+--)|(--)|(\\|)|(%7C)"; 28 | 29 | boolean filterXSS = true; 30 | boolean filterSQL = true; 31 | 32 | public InjectionAttackWrapper(HttpServletRequest request, boolean filterXSS, boolean filterSQL) { 33 | super(request); 34 | this.filterXSS = filterXSS; 35 | this.filterSQL = filterSQL; 36 | } 37 | 38 | public InjectionAttackWrapper(HttpServletRequest request) { 39 | this(request,true,true); 40 | } 41 | 42 | 43 | @Override 44 | public String getParameter(String name) { 45 | String value = super.getParameter(name); 46 | return filterParamString(value); 47 | } 48 | 49 | @Override 50 | public Map getParameterMap() { 51 | Map rawMap = super.getParameterMap(); 52 | Map filteredMap = new HashMap(rawMap.size()); 53 | Set keys = rawMap.keySet(); 54 | for (String key : keys) { 55 | String[] rawValue = rawMap.get(key); 56 | String[] filteredValue = filterStringArray(rawValue); 57 | filteredMap.put(key, filteredValue); 58 | } 59 | return filteredMap; 60 | } 61 | 62 | protected String[] filterStringArray(String[] rawValue) { 63 | String[] filteredArray = new String[rawValue.length]; 64 | for (int i = 0; i < rawValue.length; i++) { 65 | filteredArray[i] = filterParamString(rawValue[i]); 66 | } 67 | return filteredArray; 68 | } 69 | 70 | @Override 71 | public String[] getParameterValues(String name) { 72 | String[] rawValues = super.getParameterValues(name); 73 | if (rawValues == null) 74 | return null; 75 | String[] filteredValues = new String[rawValues.length]; 76 | for (int i = 0; i < rawValues.length; i++) { 77 | filteredValues[i] = filterParamString(rawValues[i]); 78 | } 79 | return filteredValues; 80 | } 81 | 82 | protected String filterParamString(String rawValue) { 83 | if (rawValue == null) { 84 | return null; 85 | } 86 | if (filterXSS()) { 87 | rawValue = rawValue.replaceAll(XSS_REGEX, ""); 88 | } 89 | if (filterSQL()) { 90 | rawValue = rawValue.replaceAll(SQL_REGEX, ""); 91 | } 92 | return rawValue; 93 | } 94 | 95 | 96 | @Override 97 | public Cookie[] getCookies() { 98 | Cookie[] existingCookies = super.getCookies(); 99 | if(existingCookies != null) { 100 | for(int i=0;i innocuousMap = new HashMap(); 18 | Map injectionMap = new HashMap(); 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | request = Mockito.mock(HttpServletRequest.class); 23 | wrapper = new InjectionAttackWrapper(request); 24 | innocuousMap.put("param1", new String[] { "%20Christina Zhong" }); 25 | innocuousMap.put("param2", new String[] { "%20ona sunny day" }); 26 | innocuousMap.put("param3", new String[] { "%20on a rainy day" }); 27 | innocuousMap.put("param4", new String[] { "Then ona winter day" }); 28 | 29 | String injectedCodeWithHexspace = "%20onMouseOver=alert('today');"; 30 | String injectedCodeWithHexquote = "%22%20string"; 31 | String injectedCodeWithSpace = " onMouseOver=alert('today');"; 32 | String injectedCodeWithQuote = "' string"; 33 | String injectedCodeKeyboardWithSpace = " onkeydown=alert('today');"; 34 | String injectedCodeWithHexspaceAndSpace = "%22+onmouseover=alert(document.cookie)+%22"; 35 | injectionMap.put("param1", new String[] { injectedCodeWithHexspace }); 36 | injectionMap.put("param2", new String[] { injectedCodeWithHexquote }); 37 | injectionMap.put("param3", new String[] { injectedCodeWithSpace }); 38 | injectionMap.put("param4", new String[] { injectedCodeWithQuote }); 39 | injectionMap.put("param5", new String[] { injectedCodeKeyboardWithSpace }); 40 | injectionMap.put("param6", new String[] { injectedCodeWithHexspaceAndSpace }); 41 | } 42 | 43 | @Test 44 | public void shouldNotFilterMessageWithAnOnInAName() { 45 | Mockito.when(request.getParameterMap()).thenReturn(innocuousMap); 46 | 47 | wrapper = new InjectionAttackWrapper(request); 48 | Map output = wrapper.getParameterMap(); 49 | assertEquals("%20Christina Zhong", output.get("param1")[0]); 50 | 51 | } 52 | 53 | @Test 54 | public void shouldNotFilterMessageWithStartingWithOn() { 55 | Mockito.when(request.getParameterMap()).thenReturn(innocuousMap); 56 | 57 | wrapper = new InjectionAttackWrapper(request); 58 | Map output = wrapper.getParameterMap(); 59 | assertEquals("%20ona sunny day", output.get("param2")[0]); 60 | 61 | } 62 | 63 | @Test 64 | public void shouldNotFilterMessageWithAnOnInItLaterInTheString() { 65 | Mockito.when(request.getParameterMap()).thenReturn(innocuousMap); 66 | 67 | wrapper = new InjectionAttackWrapper(request); 68 | Map output = wrapper.getParameterMap(); 69 | assertEquals("Then ona winter day", output.get("param4")[0]); 70 | 71 | } 72 | 73 | @Test 74 | public void shouldNotFilterMessageStartingWithOn() { 75 | Mockito.when(request.getParameterMap()).thenReturn(innocuousMap); 76 | 77 | wrapper = new InjectionAttackWrapper(request); 78 | Map output = wrapper.getParameterMap(); 79 | assertEquals("%20on a rainy day", output.get("param3")[0]); 80 | 81 | } 82 | 83 | @Test 84 | public void shouldFilterInjectedCodeWithHexspace() { 85 | Mockito.when(request.getParameterMap()).thenReturn(injectionMap); 86 | wrapper = new InjectionAttackWrapper(request); 87 | Map output = wrapper.getParameterMap(); 88 | 89 | assertEquals("", ((String[]) output.get("param1"))[0]); 90 | } 91 | 92 | @Test 93 | public void shouldFilterInjectedCodeWithHexquote() { 94 | Mockito.when(request.getParameterMap()).thenReturn(injectionMap); 95 | wrapper = new InjectionAttackWrapper(request); 96 | Map output = wrapper.getParameterMap(); 97 | 98 | assertEquals("", ((String[]) output.get("param2"))[0]); 99 | } 100 | 101 | @Test 102 | public void shouldFilterInjectedCodeWithSpace() { 103 | Mockito.when(request.getParameterMap()).thenReturn(injectionMap); 104 | wrapper = new InjectionAttackWrapper(request); 105 | Map output = wrapper.getParameterMap(); 106 | 107 | assertEquals("", ((String[]) output.get("param3"))[0]); 108 | } 109 | 110 | @Test 111 | public void shouldNotFilterQuoteSpace() { 112 | Mockito.when(request.getParameterMap()).thenReturn(injectionMap); 113 | wrapper = new InjectionAttackWrapper(request); 114 | Map output = wrapper.getParameterMap(); 115 | 116 | assertEquals("' string", ((String[]) output.get("param4"))[0]); 117 | } 118 | 119 | @Test 120 | public void shouldNotFilterInjectedCodeKeyboardWithSpace() { 121 | Mockito.when(request.getParameterMap()).thenReturn(injectionMap); 122 | wrapper = new InjectionAttackWrapper(request); 123 | Map output = wrapper.getParameterMap(); 124 | 125 | assertEquals("", ((String[]) output.get("param5"))[0]); 126 | } 127 | 128 | @Test 129 | public void shouldFilterInjectedCodeHexQuoteWithNormalSpace() { 130 | Mockito.when(request.getParameterMap()).thenReturn(injectionMap); 131 | wrapper = new InjectionAttackWrapper(request); 132 | Map output = wrapper.getParameterMap(); 133 | 134 | assertEquals("", ((String[]) output.get("param6"))[0]); 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /test/com/slocumbrau/filter/InjectionAttackFilterTest.java: -------------------------------------------------------------------------------- 1 | package com.slocumbrau.filter; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | import static org.junit.Assert.assertFalse; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletRequest; 9 | import javax.servlet.ServletResponse; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | 13 | import org.junit.Test; 14 | import org.mockito.Mockito; 15 | 16 | public class InjectionAttackFilterTest { 17 | @Test 18 | public void shouldSetFilterXSSToTrueWhenParamIsSetTrue() throws Exception { 19 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 20 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.FILTER_XSS_PARAM_NAME)).thenReturn("true"); 21 | InjectionAttackFilter filter = new InjectionAttackFilter(); 22 | filter.init(filterConfig); 23 | assertTrue(filter.filterXSS); 24 | } 25 | 26 | @Test 27 | public void shouldSetFilterXSSToFalseWhenParamIsSetFalse() throws Exception { 28 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 29 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.FILTER_XSS_PARAM_NAME)).thenReturn("false"); 30 | InjectionAttackFilter filter = new InjectionAttackFilter(); 31 | filter.init(filterConfig); 32 | assertFalse(filter.filterXSS); 33 | } 34 | 35 | @Test 36 | public void shouldSetFilterSQLToTrueWhenParamIsSetTrue() throws Exception { 37 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 38 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.FILTER_SQL_INJECTION_PARAM_NAME)).thenReturn("true"); 39 | InjectionAttackFilter filter = new InjectionAttackFilter(); 40 | filter.init(filterConfig); 41 | assertTrue(filter.filterSQL); 42 | } 43 | 44 | @Test 45 | public void shouldSetFilterSQLToFalseWhenParamIsSetFalse() throws Exception { 46 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 47 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.FILTER_SQL_INJECTION_PARAM_NAME)).thenReturn("false"); 48 | InjectionAttackFilter filter = new InjectionAttackFilter(); 49 | filter.init(filterConfig); 50 | assertFalse(filter.filterSQL); 51 | } 52 | 53 | @Test 54 | public void shouldAddXFrameOptionsHeaderWhenNotPresent() throws Exception { 55 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 56 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.CLICK_JACKING_HEADER)).thenReturn("true"); 57 | InjectionAttackFilter filter = new InjectionAttackFilter(); 58 | filter.init(filterConfig); 59 | HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 60 | Mockito.when(response.containsHeader("X-FRAME-OPTIONS")).thenReturn(false); 61 | filter.doFilter(Mockito.mock(HttpServletRequest.class), response, Mockito.mock(FilterChain.class)); 62 | Mockito.verify(response).addHeader("X-FRAME-OPTIONS", "SAMEORIGIN"); 63 | } 64 | 65 | @Test 66 | public void shouldNotAddXFrameOptionsHeaderWhenAlreadyPresent() throws Exception { 67 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 68 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.CLICK_JACKING_HEADER)).thenReturn("true"); 69 | InjectionAttackFilter filter = new InjectionAttackFilter(); 70 | filter.init(filterConfig); 71 | HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 72 | Mockito.when(response.containsHeader("X-FRAME-OPTIONS")).thenReturn(true); 73 | filter.doFilter(Mockito.mock(HttpServletRequest.class), response, Mockito.mock(FilterChain.class)); 74 | Mockito.verify(response, Mockito.never()).addHeader("X-FRAME-OPTIONS", "SAMEORIGIN"); 75 | } 76 | 77 | @Test 78 | public void shouldNotAddXFrameOptionsHeaderWhenDisabled() throws Exception { 79 | FilterConfig filterConfig = Mockito.mock(FilterConfig.class); 80 | Mockito.when(filterConfig.getInitParameter(InjectionAttackFilter.CLICK_JACKING_HEADER)).thenReturn("false"); 81 | InjectionAttackFilter filter = new InjectionAttackFilter(); 82 | filter.init(filterConfig); 83 | HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 84 | Mockito.when(response.containsHeader("X-FRAME-OPTIONS")).thenReturn(false); 85 | filter.doFilter(Mockito.mock(HttpServletRequest.class), response, Mockito.mock(FilterChain.class)); 86 | Mockito.verify(response, Mockito.never()).addHeader("X-FRAME-OPTIONS", "SAMEORIGIN"); 87 | } 88 | 89 | @Test 90 | public void falseShouldParseFalse() { 91 | assertFalse( new Boolean("false") ); 92 | } 93 | 94 | @Test 95 | public void trueShouldParseTrue() { 96 | assertTrue( new Boolean("true") ); 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /test/com/slocumbrau/filter/InjectionAttackWrapperTest.java: -------------------------------------------------------------------------------- 1 | package com.slocumbrau.filter; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | import org.junit.Before; 11 | import org.junit.Ignore; 12 | import org.junit.Test; 13 | import org.mockito.Mockito; 14 | 15 | public class InjectionAttackWrapperTest { 16 | private final String DATE_INPUT = "August 30, 2005"; 17 | private final String OTHER_CHARS_INPUT = ".' ,-"; 18 | private final String STRING_WITH_RESTRICTED_CHARS = ""; 19 | private final String MIXED_CASE_STRING="en_SG_AP---->"; 20 | private final String RESTRICTED_2 = "%3Cscript%3Efoo%3C/script%3E"; 21 | private final String RESTRICTED_LIST = "=%<>|$;%\"\n\r\\hi"; 22 | private final String FILTERED_STRING = "scriptfoo/script"; 23 | private InjectionAttackWrapper wrapper; 24 | private HttpServletRequest request; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | request = Mockito.mock(HttpServletRequest.class); 29 | wrapper = new InjectionAttackWrapper(request); 30 | } 31 | 32 | @Test 33 | public void shouldStopScriptlets() { 34 | assertEquals(FILTERED_STRING, wrapper.filterParamString((STRING_WITH_RESTRICTED_CHARS))); 35 | } 36 | 37 | @Test 38 | public void shouldStopEscapedScriptlets() { 39 | assertEquals(FILTERED_STRING, wrapper.filterParamString((RESTRICTED_2))); 40 | } 41 | 42 | @Test 43 | public void shouldFilterLinkInjection() { 44 | assertEquals("%22%27A%20HREF%22%2FWF_XSRF.html%22Injected%20Link%2FA", 45 | wrapper.filterParamString("%22%27%3E%3CA%20HREF%3D%22%2FWF_XSRF.html%22%3EInjected%20Link%3C%2FA%3E")); 46 | } 47 | 48 | @Test 49 | public void shouldFilterEscapedEquals() { 50 | assertEquals("hi", wrapper.filterParamString("%3Dhi")); 51 | } 52 | 53 | @Test 54 | public void shouldFilterCSSInjection() { 55 | assertEquals("something", wrapper.filterParamString("something%22%20style%3D%22background:url(javascript:alert(234))%22%20OA%3D%22")); 56 | } 57 | 58 | @Test 59 | @Ignore 60 | public void shouldRemoveLotsOfBadCharacters() { 61 | assertEquals("hi", wrapper.filterParamString(RESTRICTED_LIST)); 62 | } 63 | 64 | @Test 65 | public void shouldCatchMixesCaseScriptTags() { 66 | assertEquals("en_SG_APsCrIpTalert(45152)/sCrIpT", wrapper.filterParamString(MIXED_CASE_STRING)); 67 | } 68 | 69 | @Test 70 | public void shouldFiltersRestrictedCharsAndSkipsRegularCharsInArray() { 71 | String[] array = new String[] { STRING_WITH_RESTRICTED_CHARS, DATE_INPUT, OTHER_CHARS_INPUT }; 72 | 73 | assertEquals(FILTERED_STRING, wrapper.filterStringArray(array)[0]); 74 | assertEquals(DATE_INPUT, wrapper.filterStringArray(array)[1]); 75 | assertEquals(OTHER_CHARS_INPUT, wrapper.filterStringArray(array)[2]); 76 | } 77 | 78 | @Test 79 | public void shouldFilterParameterMap() { 80 | Map map = new HashMap(); 81 | final String param1 = "param1"; 82 | map.put(param1, new String[] { STRING_WITH_RESTRICTED_CHARS }); 83 | final String param2 = "param2"; 84 | map.put(param2, new String[] { DATE_INPUT }); 85 | 86 | Mockito.when(request.getParameterMap()).thenReturn(map); 87 | 88 | wrapper = new InjectionAttackWrapper(request); 89 | Map output = wrapper.getParameterMap(); 90 | 91 | assertEquals(FILTERED_STRING, ((String[]) output.get(param1))[0]); 92 | assertEquals(DATE_INPUT, ((String[]) output.get(param2))[0]); 93 | } 94 | 95 | @Test 96 | public void shouldFilterEntireQueryString() { 97 | String xssString = "orderNumber=&phoneNumber=>'>"; 98 | Mockito.when(request.getQueryString()).thenReturn(xssString); 99 | wrapper = new InjectionAttackWrapper(request); 100 | assertEquals("orderNumber=&phoneNumber='scriptalert('boooo')/script", wrapper.getQueryString()); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /test/com/slocumbrau/filter/SQLInjectionTest.java: -------------------------------------------------------------------------------- 1 | package com.slocumbrau.filter; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.mockito.Mockito; 10 | 11 | public class SQLInjectionTest { 12 | private final String SQL_INJECTION_ENCODED = "%27%20%7C%7C%20%27foo"; 13 | private final String SQL_INJECTION_UNENCODED = "' || 'foo"; 14 | 15 | private InjectionAttackWrapper wrapper; 16 | private HttpServletRequest request; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | request = Mockito.mock(HttpServletRequest.class); 21 | wrapper = new InjectionAttackWrapper(request); 22 | } 23 | 24 | @Test 25 | public void shouldCatchSqlStrings() { 26 | assertEquals("", wrapper.filterParamString("'select * from users;--")); 27 | } 28 | 29 | @Test 30 | public void shouldFilterEncodedPipes() { 31 | assertEquals("%27%20%20%27foo", wrapper.filterParamString(SQL_INJECTION_ENCODED)); 32 | } 33 | 34 | @Test 35 | public void shouldFilterUnencodedPipes() { 36 | assertEquals("' 'foo", wrapper.filterParamString(SQL_INJECTION_UNENCODED)); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | allow user-configured additional string 2 | strip bad chars vs blank entire string 3 | optional logging (log4j?) --------------------------------------------------------------------------------