Redirect to index.html when URL contained a potentially malicious String in spring boot - spring

I'm developing an app with spring boot as its backend and angular 6 as its frontend. I build all frontend files to static folder of the spring boot. In all cases whether it be a (type=Internal Server Error, status=500) error or (type=Not Found, status=404) error, I want my app to not show the "Whitelabel Error Page" and instead redirect the user to index.html file in the static folder. I could achieve it for 404 error code by adding below code to my WebMvcConfigurer:
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
//registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
registry.addResourceHandler("/**/*")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath,
Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
But could not achieve the same result for 500 error code. For example when I type a url with special characters in addressbar, I get this error:
**Whitelabel Error Page**
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Oct 23 13:56:11 IRST 2018
There was an unexpected error (type=Internal Server Error, status=500).
The request was rejected because the URL contained a potentially malicious String ";"
but if I type some wrong url without special character, I'd be redirected the the "index.html" file.
I also tried adding these lines to my WebMvcConfigurer but that didn't work either:
#Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/notFound").setViewName("forward:/static/index.html");
registry.addViewController("/login").setViewName("forward:/static/index.html");
registry.addViewController("/error").setViewName("forward:/static/index.html");
}
#Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
return container -> {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error"));
};
}

there is not need to more code for this you can create a folder named 'public' in static directory and create directory in this folder named 'error' after that create file named '500.html' or '404.html' or http_code.html in error directory now you can redirect these files.

Related

Spring Boot doesn't show error pages implementing a custom ErrorController

I'm trying to show custom error pages depending on the HTTP status code. What I have done is implementing Spring's ErrorController interface in a CustomErrorController but it seems that Spring Boot is not recognizing it.
I have followed this tutorial to do that: https://www.baeldung.com/spring-boot-custom-error-page (section 3.1).
There I have read that first you need to get rid of the famous Spring's default Whitelabel Error Page. So I did this:
#SpringBootApplication(exclude = { ErrorMvcAutoConfiguration.class })
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
This seems to work since the Whitelabel error page hasn't appeared anymore but now when an error happens the Apache Tomcat error page (that ugly one with the stack trace included) appears instead of mine.
Then I've just implemented my CustomErrorController like this:
#Controller
#RequestMapping("/error")
public class CustomErrorController implements ErrorController {
#RequestMapping
public String handleError(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (statusCode != null) {
// Specific error page
return "redirect:/error/" + statusCode;
}
// Global error page
return "error/error";
}
#Override
public String getErrorPath() {
return "/error";
}
#GetMapping("/404")
public String notFoundErrorPage() {
return "error/404";
}
// Other error codes mapping methods
}
I'm using Thymeleaf and my error views are under src/main/resources/views/error, where every specific error page name follows the recommended format of <error_code>.html so, for instance, a 404 error would have a 404.html page associated.
I haven't had any problem with other application views resolving so far. Actually, I have configured my Spring Security to call the /error/403 endpoint if access denied occurs and the error page is shown properly.
Same happens with /error/500, that is called when an internal server exception occurs since I have also implemented the following #ControllerAdvice #ExceptionHandler method:
#ControllerAdvice
#Log4j2
public class GlobalDefaultExceptionHandler {
#ExceptionHandler(Exception.class)
public String defaultErrorHandler(Exception exception) throws Exception {
if (AnnotationUtils.findAnnotation(exception.getClass(), ResponseStatus.class) != null) {
throw exception;
}
log.catching(exception);
return "redirect:/error/500";
}
}
So, if each of these endpoints works individually, why if Spring throws an error the handleError method is not called ever?
Thank you.
Seems as if your GlobalDefaultExceptionHandler is catching every Exception upfront. That's why handleError is never called.
Your other endpoints work, cause you are calling them directly - as you describe it.
I would recommend using #ControllerAdvice to handle specific Exceptions an let your CustomErrorController implementation handle all not already handled Exceptions. Spring boot will wrap them inside NestedServletException with Http Status 500. You can get the root cause inside handleError with:
Object exception = request.getAttribute("javax.servlet.error.exception");
if (String.valueOf(exception) != null) {
log.info("Nested Exception: " + String.valueOf(exception));
}
Check those answers for further information on ordering and the error work flow in spring boot:
order
spring boot error handling flow

When URL suffixed by a file extension, Spring Restful ControllerAdvice exception handler response html

I use #RestControllerAdvice to handle exception thrown by a controller globally, and response json string to client. My rest controller #RequestMapping path can accept URL suffixed by a file extension. If the controller throw a exception and URL is suffixed by a known extension , the exception handler will response html instead of json.
build.gradle
...
dependencies {
compile 'com.google.code.gson:gson:2.7'
compileOnly 'org.apache.tomcat:tomcat-servlet-api:8.0.33'
compile 'org.springframework:spring-webmvc:4.3.1.RELEASE'
}
servlet-context.xml
...
<mvc:annotation-driven/>
<context:component-scan base-package="com.demo"/>
...
DemoController.java
#RestController
public class DemoRestController {
#RequestMapping(value = "/{name:.+}")
public String doSomething(#PathVariable String name){
throw new RuntimeException(name);
}
}
RestExceptionHandler
#RestControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(Exception.class)
public Message handleException(Exception ex, WebRequest request) {
Message ret=new Message(System.currentTimeMillis(),ex.getMessage());
return ret;
}
}
client
$ curl localhost:8080/abc //no extension, it's ok
{"time":1479235304196,"url":"abc"}
$ curl localhost:8080/abc.opq //unknown extension, ok
{"time":1479235545303,"url":"abc.opq"}
$ curl localhost:8080/abc.jpg
<!DOCTYPE html><html><head><title>Apache Tomcat/8.0.33 - Error report</title> ...
...
the last output is html, it is not i want, what's matter? can you help me , thank you !
It seems that if the request path has an unknown extension then Spring doesn't know what to do with the return value from handleException and falls back to HTML. You can fix this by rendering JSON directly in your handleException method. This worked in my case because my API always returns JSON in the case of errors, instead of protobuf or csv or whatever.
#RestControllerAdvice(annotations = {RestController.class})
public class ApiExceptionHandler {
private final ObjectMapper objectMapper;
public ApiExceptionHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#ExceptionHandler
public void exceptionHandler(HttpServletRequest req, HttpServletResponse res, Exception x) throws IOException {
Message ret = new Message(System.currentTimeMillis(),ex.getMessage());
res.setStatus(500);
res.setContentType(MediaType.APPLICATION_JSON_UTF8.toString());
objectMapper.writeValue(res.getOutputStream(), ret);
}
}

Spring MVC Test with Validation JSR-303/349: Locale or accept-language does not work in testing

I am working with Spring Framework 4.3.3
I have the following situation for a #Controller either for POST or PUT
For example for POST I have the following:
#PostMapping(value="/save",
consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces=MediaType.TEXT_HTML_VALUE)
public String saveOne(#Validated #ModelAttribute Persona persona,
BindingResult result,
RedirectAttributes redirectAttributes){
logger.info("saveOne: {}", persona.toString());
if(result.hasErrors()){
logger.error("# Errors: {}", result.getErrorCount());
logger.error("result: {}", result.toString());
return "persona/saveOne";
}
personaRestController.saveOne(persona);
redirectAttributes.addFlashAttribute("message", "process.successful");
return "redirect:/message";
}
The app works how is expected, through a Web Form this #PostMapping either saves/persists the data or validates the data and if it is invalid the same web form is shown with the error messages for each field and based about i18n such as English, Spanish and Portuguese. It related with JSR-439
About Java config infrastructure configuration I have the following:
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
//localeChangeInterceptor.setHttpMethods("post", "put");
return localeChangeInterceptor;
}
#Bean(name="localeResolver")
public LocaleResolver cookieLocaleResolver(){
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setDefaultLocale(new Locale("en","US"));
return cookieLocaleResolver;
}
And
#EnableWebMvc
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptorConfig.localeChangeInterceptor());
}
The problem is about Spring MVC Test. I have the following:
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
...
#Test
public void saveOneHtmlPostInvalidMaxTest() throws Exception {
logger.info("saveOneHtmlPostInvalidMaxTest");
resultActions = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.locale(locale)
.header("accept-language", locale.toString())
.header("language", locale.toString())
.param("id", personaInvalidMax.getId())
.param("nombre", personaInvalidMax.getNombre())
.param("apellido", personaInvalidMax.getApellido())
....
.andDo(print());
Note observe:
.locale(locale)
.header("accept-language", locale.toString())
.header("language", locale.toString())
Even when some of them have no sense, they are there because I am trying to resolve the following situation:
The validation control from the server side works how is expected until some point.
First
I can confirm through .andDo(print()) method that MockHttpServletRequest prints
Headers = {Content-Type=[application/x-www-form-urlencoded],
accept-language=[pt_BR], language=[pt_BR]}
Observe pt_BR
Second
The #PostMapping that contains:
if(result.hasErrors()){
logger.error("# Errors: {}", result.getErrorCount());
logger.error("result: {}", result.toString());
return "persona/saveOne";
}
Prints...
7370 [Test worker] ERROR PersonaSaveOneController - result:
org.springframework.validation.BeanPropertyBindingResult: 4 errors
Field error in object 'persona' on field 'id': rejected value [0];
codes [Size.persona.id,Size.id,Size.java.lang.String,Size]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [persona.id,id]; arguments []; default message [id],3,3];
default message [The size for the field must be between 3 and 3]
Observe the default message [id],3,3];
default message [The size for the field must be between 3 and 3]
Here two problems:
The locale or accept-language are ignored
The error messages prints always in English
What I need is that the error data prints for example in Portuguese and Spanish according the Locale sent. It should returns the error messages for each field in an expected language, in this case Portuguese
Again, it works when I use directly the Web Form, but not through the Test. I am assuming that exists an internal Spring component that works through production and has not been defined for Testing
Therefore, what is need it to accomplish this approach?
BTW: With the following (see I use classpath:), furthermore my .properties file are located in src/main/resources
#Bean(name="messageSource")
public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
resource.setBasenames(...
"classpath:/com/manuel/jordan/validation/validation");
resource.setDefaultEncoding("UTF-8");
return resource;
}
My #Test methods working around with Rest works fine about show and returning the error message according the Locale or accept-language defined (Access to MessageSource happens in the server side and works fine with the previous configuration). It just fails for non-Rest controllers

Disable redirect to /error for certain urls

I have created a springboot application that contains some Rest API endpoints in .../api/myEndpoints... and thymeleaf templates for some UI forms the user can interact with.
Since I added an errorController:
#Controller
#RequestMapping("/error")
public class ErrorController {
#RequestMapping(method = RequestMethod.GET)
public String index(Model model) {
return "error";
}
}
whenever an exception is being thrown in my RestControllers, I receive an empty white website containing the word "error". This maybe makes sense for the web frontend, but not for my api. For the API I want spring to output the standard JSON result e.g.:
{
"timestamp": 1473148776095,
"status": 400,
"error": "Bad request",
"exception": "java.lang.IllegalArgumentException",
"message": "A required parameter is missing (IllegalArgumentException)",
"path": "/api/greet"
}
When I remove the index method from the ErrorController, then I always receive the JSON output.
My question is: Is it somehow possible to exclude the automatic redirection to /error for all api urls (../api/*) only?
Thanks a lot.
There may be a better solution out there, until then... here's how you can achieve what you asked:
(1) Disable ErrorMvcAutoConfiguration
Add this to your application.properties:
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration
(2) Define two ControllerAdvices
Since we disabled ErrorMvcAutoConfiguration, we need to catch the exception ourself. Create one advice to catch error for a specific package, and another advice to catch all other. They each redirect to a different url.
//Catch exception for API.
#ControllerAdvice(basePackageClasses = YourApiController.class)
#Order(Ordered.HIGHEST_PRECEDENCE)
public static class ErrorApiAdvice {
#ExceptionHandler(Throwable.class)
public String catchApiExceptions(Throwable e) {
return "/error/api";
}
}
//Catch all other exceptions
#ControllerAdvice
#Order(Ordered.LOWEST_PRECEDENCE)
public static class ErrorAdvice {
#ExceptionHandler(Throwable.class)
public String catchOtherExceptions() {
return "/error";
}
}
(3) create a controller to handle the error page
This is where you can have different logic in your error handling:
#RestController
public class MyErrorController {
#RequestMapping("/error/api")
public String name(Throwable e) {
return "api error";
}
#RequestMapping("/error")
public String error() {
return "error";
}
}
With Spring-Boot 1.4.x you can also implement ErrorViewResolver (see this doc):
#Component
public class MyErrorViewResolver implements ErrorViewResolver {
#Override
public ModelAndView resolveErrorView(HttpServletRequest request,
HttpStatus status, Map<String, Object> model) {
if("/one".equals(model.get("path"))){
return new ModelAndView("/errorpage/api");
}else{
return new ModelAndView("/errorpage");
}
}
}

Wicket + WAS: calling url causes a redirect to a wrong URL causing 404

Using Websphere Application Server + Wicket 1.6
I am having some issues with my mounted URLs.
When I invoke an url akin to: localhost:9080/danesCooking/pies/meat I see the following in the Chrome network tab:
localhost:9080/danesCooking/pies/meat status code 302
localhost:9080/danesCooking/meat?2 status code 404
So it seems the /pies/ portion gets lost. This behaviour does not occur when I deploy my application to Tomcat\JBoss\... .
Possibly relevant, pies itself is not a mounted page.
I've already looked through some of the wicket issues\forums and it seems most issues seem to be either resolved\conflicting answers\have to do with relative urls (fixed in 1.5.x).
Has anyone experienced this issue and still recalls how to resolve this?
Used WAS *Versions: 7 and 8.5* liberty.
This issue is actually outlined here;
https://issues.apache.org/jira/browse/WICKET-3258
My resolution to the issue in Wicket 6.9.1 was;
public class MyApplication extends WebApplication {
#Override
public Class<? extends WebPage> getHomePage() {
return MyHomePage.class;
}
/* *********************************************** */
// Resolve Websphere Relative URL "sendRedirect" Bug
#Override
protected WebResponse newWebResponse(WebRequest webRequest, HttpServletResponse httpServletResponse) {
return new FixedServletWebResponse((ServletWebRequest) webRequest, httpServletResponse);
}
/**
* Websphere incorrectly handles relative redirect pages when "HttpServletResponse.sendRedirect(url)" is called.
*
* This small fix ensures that Websphere is only ever provided with absolute URLs so that this issue never occurs.
*/
private static class FixedServletWebResponse extends ServletWebResponse {
private final ServletWebRequest webRequest;
protected FixedServletWebResponse(ServletWebRequest webRequest, HttpServletResponse httpServletResponse) {
super(webRequest, httpServletResponse);
this.webRequest = webRequest;
}
#Override
public String encodeRedirectURL(CharSequence url) {
Url relativeUrl = Url.parse(url);
return new UrlRenderer(webRequest).renderFullUrl(relativeUrl);
}
}
/* *********************************************** */
}
You can also solve that using anonymous class as described in the Configuring and Deploying Open Source with WebSphere Application Server Liberty Profile chapter 5.2.4. (Tested with WLP 8.5.5.3 and Wicket 6.8.0)
Override the following method in your WebApplication class:
#Override
protected WebResponse newWebResponse(final WebRequest webRequest, final
HttpServletResponse httpServletResponse)
{
return new ServletWebResponse((ServletWebRequest) webRequest,
httpServletResponse)
{
#Override
public String encodeRedirectURL(final CharSequence relativeURL)
{
return new UrlRenderer(webRequest).renderFullUrl(Url.parse(relativeURL));
}
};
}
UPDATE
Other solution, instead of code change, is to set the following property in the webcontainer (works in 8.5.5.3):
<webContainer com.ibm.ws.webcontainer.redirectwithpathinfo="true" />

Resources