Spring: Omit CSRF token for GET forms? - spring

Using spring-security-web 3.2.x (old client code), is there a way to not inject the csrf token parameter into forms that use the GET method?
That makes the token to appear in the URL when the form is submitted and which can be leaked.
Config is:
<http>
<csrf>
JSP is:
<form:form method="GET" ...
Resulting HTML:
<input type="hidden" name="_csrf" value="********-****-****-****-******" />
</form>
Note that using:
<csrf request-matcher-ref="csrfMatcher"/>
Does not help, as this only controls whether the csrf token is checked when the form is submitted and not whether the csrf hidden field is injected into the form. (GET request are not checked anyway by default).

There is code in CsrfRequestDataValueProcessor.java that should omit the CSRF token for GET forms:
private Pattern DISABLE_CSRF_TOKEN_PATTERN = Pattern.compile("(?i)^(GET|HEAD|TRACE|OPTIONS)$");
public String processAction(HttpServletRequest request, String action, String method) {
if(method != null && DISABLE_CSRF_TOKEN_PATTERN.matcher(method).matches()) {
request.setAttribute(DISABLE_CSRF_TOKEN_ATTR, Boolean.TRUE);
} else {
request.removeAttribute(DISABLE_CSRF_TOKEN_ATTR);
}
return action;
}
But according to both code inspection and debugging it is never called. Probably a bug.
This is spring-security-web-3.2.7 and spring-webmvc-3.2.13

Related

How to set an attribute into a session with thymeleaf?

I'm trying to add an attribute to an HTTP session with thyme leaf, but I can't. I had a "Project" object and I need to pass it to the controller. I had a form in the page, so I've tried this, but it doesn't work:
<input type="hidden" th:attr="${#session.setAttribute('proyecto', '${proyecto}')}"
The controller receives the attribute proyecto but the value is "${proyecto}", not the content of the project object
Send the value using a form and then set the session attribute in your controller.
HTML
<form th:action="#{/newProject}" th:object="${newProject}" method="post">
<input th:field="*{id}"></input>
</form>
Controller
#RequestMapping(value = "/newProject", method = RequestMethod.POST)
public String addProject(HttpSession session, #ModelAttribute("newProject") Project project) {
session.setAttribute("proyecto", project);
return "/newPage";
}
This is just example of course. You would need to add all the project fields in your HTML.

How to handle session to keep login information using Spring MVC

I want to make login form with Spring MVC using Hibernate.
I found that I need to use 'session' to keep login information.
So, I use it in 'Controller.java', '.jsp'.
But It seems didn't work.
Below is my code. Controller.java:
#Controller
public class PassengerController {
#Autowired
private PassengerService passengerService;
public void setPassengerService(PassengerService passengerService) {
this.passengerService = passengerService;
}
#RequestMapping(value = "/login")
public String login(HttpSession session, HttpServletRequest request) {
String id = request.getParameter("idInput");
String pw = request.getParameter("pwInput");
// check DB
// if it is right, add session.
session.setAttribute("id", id);
session.setAttribute("pw", pw);
return "flightschedule";
}
#RequestMapping(value = "/logout")
public String logout(HttpSession session) {
session.invalidate();
return "flightschedule";
}
}
Below is part of flightschedule.jsp:
<c:if test="${sessionScope.loginId eq null}">
<!-- Not login: show login button -->
<div class="loginArea">
<form action="${loginAction}"> <!-- // URL '/login' -->
<input type="text" name="idInput" placeholder="ID" class="loginInput">
<input type="password" name="pwInput" placeholder="PASSWORD" class="loginInput">
<input id="loginButton" type="submit" value="login">
</form>
</div>
</c:if>
<c:if test="${sessionScope.loginId ne null}">
<!-- already login: show logout button -->
<div class="loginArea">
<form action="${logoutAction}"> <!-- // URL '/logout' -->
<input type="button" name="idInput" id="loginInfo" value="Welcome ${sessionScope.loginId}">
<input id="logoutButton" type="submit" value="LOGOUT">
</form>
</div>
</c:if>
I intended that when session.id exists, show log out button and when session.id doesn't exist, show login button.
I don't want to use interceptors or spring security etc..
I thought they're too complex to my little project.
And, I have login/logout form at all most of my pages. I don't use a separate page to login.
So I don't want to use interceptor. I just want to check whether session key exists in some jsp pages. Depending on its presence, I want to change page's view.
Above's code work partly. When login, it shows 'Welcome userId'.
But, when I click page's logo(then go to the first page), It still show 'login' button. It have to show 'log-out' button becuase session.loginId exists!
Do you have any solution?
In login method you put
// check DB
// if it is right, add session.
session.setAttribute("id", id);
session.setAttribute("pw", pw);
but on JSP check sessionScope.loginId , looks like you should check attribute with name id.

HTTP Status 400 - Required String parameter 'testParam' is not present in spring MVC

I am trying to understand how spring works..
I have created a form and a controller. I intentionally added a param to the method which did not match to the parameter in the request and i get an error like below.
HTTP Status 400 - Required String parameter 'testParam' is not present
#RequestMapping("/processForm")
public String processForm(#RequestParam("testParam") String title,
Model model){
return "formResult";
}
<form action="processForm" method="GET" >
<input type="text" name="hello" />
<input type="submit" />
</form>
The requestParam which is "testParam" is mandatory here so i get this error.
My question is where did this error handled. Did the dispatcherServlet checked the methods and returned 404 immediately or it tried to pass it to the controller`s method and the error happened there?
I hope the question is clear.. Thanks.
The controller method is not called, the handling and parameter check is done before this call. The handling flow seems like this:
DispatcherServlet.doDispatch() ->
RequestMappingHandlerAdapter.invokeHandlerMethod() ->
ServletInvocableHandlerMethod.invokeForRequest() ->
InvocableHandlerMethod.getMethodArgumentValues() ->
HandlerMethodArgumentResolver.resolveArgument()
At this point is detected that the required parameters don't match and a MissingServletRequestParameterException is thrown.
DefaultHandlerExceptionResolver handles the MissingServletRequestParameterException which results in a response with status code: Bad Request 400.

<form:form have method GET or POST

This may be a very basic question but I am confused. I have couple of doubts:
In spring form <form:form if method is not specified then is it GET or POST?
If a spring form has <form:form with commandName then is that GET or POST?
The second question is because I see a "form:form commandName=xyz action=abc" in the code
When I check the HTML code (view source) it translates to
"form action=abc method=POST"
Please help me with this.
HTML form without specified action is always GET. It's HTML standard.
http://www.w3.org/TR/html401/interact/forms.html#h-17.3
But when you look inside FormTag in Spring source you'll notice this code:
public class FormTag extends AbstractHtmlElementTag {
/** The default HTTP method using which form values are sent to the server: "post" */
private static final String DEFAULT_METHOD = "post";
So for spring tag <form:form action is post by default.
commandName is just name for model attribute binded with your form. It has nothing to method type. Moreover, it's equivalent to modelAttribute so you can use either.
Spring form has default method as POST. If you want to do the get, you have to write, method="get" in form:form tag.

How can I use multiple ajax forms with AntiForgery validation on the same MVC3 page?

When we have more than one possible form to post to the controller on the same cshtml page,
the Antiforgery validation does not work. We went through the MVC3 code and we found the problem is in this part of the code:
if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal)) {
// error: form token does not match cookie token
throw CreateValidationException();
}
The cshtml that we have is something like this:
#using (#Ajax.BeginForm()) {
#Html.AntiForgeryToken()
<input type="submit" class="buttonBlue" value="form1" />
}
#using (#Ajax.BeginForm()) {
#Html.AntiForgeryToken()
<input type="submit" class="buttonBlue" value="form2" />
}
Can you help me to fix this issue? We found that after removing one of the antiforgery tokens eveyrthing seems to work as expected.
We tried setting the machine key for the antiforgery and it didn't work either.
Regards.
In order to use the AntiForgeryToken multiple times, I do the following. First, at the top of my view, I add the following line:
ViewBag.__RequestVerificationToken = #Html.AntiForgeryToken().ToString();
Then, in each form where I need the token, I add the following:
#Html.Raw(ViewBag.__RequestVerificationToken)

Resources