Avoid ajax file upload attack - spring-mvc

I have a form and in a HTML form inside which I have file upload button.
File is going to be uploaded using AJAX request.
On file upload I am checking extension and file size.
I am uploading file into /home/xyz/upload/username/username_timestamp
But since I am using AJAX request, I cannot(and don't want to) use the CAPTCHA and there is a risk of attacker flooding with files. How can I deal with this issue?
PS: I am using Spring-MVC(not spring security) and Struts2 in my projects, so framework specific solution will be icing on a cake.

Protect your action to be called out of expected by using Strut's token mechanism and your expected logic of file uploads inside your parent action (e.g. max uploads per minutes) ; something like below:
index.jsp
<form action="upload" method="post" enctype="multipart/form-data">
<label for="myFile">Upload your file</label>
<input type="file" name="myFile" />
<s:if test="uploadsPerMinutes < 10">
<s:token name="tknUpload" /> <!-- *** conditional generate token *** -->
</s:if>
<sj:submit value="Submit Form" targets="myAjaxTarget"/>
</form>
<div id="myAjaxTarget">
</div>
struts.xml
<action name="upload" class="com.upload.FileUpload">
<interceptor-ref name="tokenSession/>
<interceptor-ref name="basicStack"/>
<result name="success" type="stream">...</result>
</action>
<action name="uploadParent" class="com.upload.FileUploadParent">
<result name="success">index.jsp</result>
</action>
FileUploadParent.java
public class FileUploadParent extends ActionSupport{
...
public static int uploadsPerMinutes = 0;
private static DateTime lastUploadTime;
public String execute()
{
...
synchronized(uploadsPerMinutes){
if(currentTime - lastUploadTime > 1min) uploadsPerMinutes=0;
else uploadsPerMinutes++;
lastUploadTime = currentTime;
}
return SUCCESS;
}
public String getUploadsPerMinutes()
{
return uploadsPerMinutes;
}
}
With these, client have to get a token from server for each file upload. These are behind the scene and do not disturb your normal users.

Related

Downloading a file in the current View, or in a new one

It is necessary that the file is downloaded either staying on the current page, or a new one opens. Tell me how to do it. Here is what has already been done.
public FileResult DownloadFile(int id)
{
FilesDB filesDB = _context.FilesDB.Find(id);
filesDB.DownloadCount++;
_context.SaveChanges();
string downloadName= filesDB.FileDisplayName +
Path.GetExtension(filesDB.FilePath);
return File(filesDB.FilePath, filesDB.FileType, downloadName);
}
You can download both way. Just use HTML form.
Here I'm using WebService:
<form id="downloadForm" action="MyService.asmx/DownloadFile" method="post" target="_self" >
<input type="hidden" name="id" id="txtID" />
</form>
JS:
function downloadFile() {
$('#txtID').val(id);
$('#downloadForm').submit();
}
Just call downloadFile() and this will download staying on current page.
If you want to open in new one just change target="_self" to target="_blank"

Button calling different servlet code in html form

I am bit new to Java web programming
I have two buttons(Test connection and Execute) in an html form and the form action is a servlet. How can I differentiate the action in the servlet based on which button is clicked.
Thanks
In the HTML:
<input type='submit' name='submitButton' value='Test connection' />
<input type='submit' name='submitButton' value='execute' />
In the servlet:
String clickedButtonValue = request.getParameter("submitButton");
if("Test connection".equals(clickedButtonValue))
{
...
}
else if("execute".equals(clickedButtonValue))
{
...
}
else ...
The reason this works: With both submit buttons sharing the same name attribute, the browser will only send the value of the one that was clicked.

Error uploading file from form-data to Spring Server

I am uploading a picture using form-data taking the picture from the Client's PC to Spring MVC Server.
Update Page:
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring MVC - Upload File</title>
</head>
<body>
<form id="form1" method="post" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
<!-- File input -->
<input name="file" id="file" type="file" /><br/>
<input type="submit" value="Upload" />
</form>
</body>
</html>
And this is the Spring Controller
#RequestMapping(value = "upload", method = RequestMethod.POST)
public #ResponseBody
String provaUpdate(MultipartHttpServletRequest request,Principal p,HttpServletResponse response)throws IOException {
String result="";
LocalFileManager mLocalFileManager = LocalFileManager.get();
Iterator<String> iterator = request.getFileNames();
while(iterator.hasNext())
{
System.out.println("iterator.next()="+iterator.next());
}
System.out.println("request.getFileMap().isEmpty()??"+request.getFileMap().isEmpty());
// mLocalFileManager.saveLocalData(g,g.getPicturesCount(), request.getFile("new").getInputStream());
return result;
}
So, when i run that, it just prints:
request.getFileMap().isEmpty()??true
Then, no Files seems to be uploaded, but if I get the request.getInputStream() I can write a TXT File showing that:
------WebKitFormBoundaryWG8vA5PuTFFxPBqK
Content-Disposition: form-data; name="file"; filename="1.jpg"
Content-Type: image/jpeg
�� JFIF ��ICC_PROFILE 蠠 mntrRGB XYZ ٠ $ acsp �� ӭ )𽞯򕮸B򤊃9
desc D ybXYZ bTRC Ԡ dmdd ࠠ ɧXYZ
h gTRC Ԡ lumi
| meas
//(Symbols) Long ETC, so the picture is sent !!
------WebKitFormBoundaryWG8vA5PuTFFxPBqK--
So, seems that the picture is sent correctly but the MultipartHttpServletRequest is not able to get the File.
Which is my mistake?
I think,you may be missing below entry in your spring configuration xml file.
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- below properties can be configured as per your need -->
<property name="maxUploadSize" value="5000000" />
<property name="maxInMemorySize" value="5000000" />
</bean>
The mistake was that i am working with Thymeleaf, so the form is different.
It is solved changing that:
1- Create a class that contains a MultipartFile, like this:
public class Images {
MultipartFile image;
public MultipartFile getImage() {
return image;
}
public void setImage(MultipartFile image) {
this.image = image;
}
}
2- The correct form code for a Spring Server working with Thymeleaf:
<form id="myform" action="#" th:action="#{/upload}" th:object="${Images}" method="POST" modelAttribute="Images" enctype="multipart/form-data">
<input type="file" th:field="${Images.image}" name="file"/>
<input type="submit" value="Upload"/>
</form>
And finally the correct Controller method:
#RequestMapping(value = "upload", method = RequestMethod.POST)
public String addVocabularyValadate( #ModelAttribute("Images") Images images,BindingResult bindingResult,
Model model) throws IOException
{
System.out.println("inputstream Nombre!"+images.getImage().getOriginalFilename());
if(bindingResult.hasFieldErrors() == true)
return "error";
else
return "upload OK!";
}

Spring 3.1 MVC and Security: both login and registration form (multiple forms) on same page get submitted

I'm using Spring (3.1), Spring MVC (3.1) and Spring Security (3.0) in combination and I've put together a single JSP page that has two forms on it; One is a form to login and the other is a form to register (i.e. create a new user).
For the register form, I use the Spring form tags, backed up by a controller to handle the request but for the login form I don't bother with the Spring form tags as I don't believe they're needed. There is also no controller that I need to write to handle the form submission as Spring Security takes care of authenticating so long as the request is submitted to j_spring_security_check.
The register form is working fine but the login form is a problem. It seems that when I click the submit button on the login form, the registration form is also submitted, or at least Spring thinks I'm trying to submit that form. Here is the JSP:
<form id="loginform" method="POST" action="<c:url value='j_spring_security_check'/>">
<label for="existing_email">Email:</label>
<input name="j_username" id="existing_email" type="text" value="${SPRING_SECURITY_LAST_USERNAME}" />
<label for="existing_password">Password:</label>
<input name="j_password" id="existing_password" type="password" />
<input id="login-form-submit" type="submit" value="Sign in" />
</form>
<form:form id="registrationform" modelAttribute="user" method="POST" action="register">
<form:label path="username" for="email">Email:</form:label>
<form:input path="username" name="username" id="email" type="text" />
<form:errors path="username" cssClass="formError" />
<form:label path="password" for="password">Password:</form:label>
<form:input path="password" name="password" id="password" type="password" />
<form:errors path="password" cssClass="formError" />
<input id="registration-form-submit" type="submit" value="Sign up" />
</form:form>
Notice that form tags for the input of type submit are not present and this seems to be a normal thing to do in the examples I've seen. Adding form tags to the submit button I guess doesn't make sense as it doesn't map to anything on the target object (user in this case).
When I click the "Sign in" button I get the following exception:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/project1] threw exception [An exception occurred processing JSP page /WEB-INF/views/registration.jsp at line 29
28: <form:form id="registrationform" modelAttribute="user" method="POST" action="register">
29: <form:label path="username" for="username">Username:</form:label>
30: <form:input path="username" name="username" id="username" type="text" />
31: <form:errors path="username" cssClass="formError" />
32:
Stacktrace:] with root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'user' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
This I recognise from cases where you forget to include the modelAttribute attribute in form:form, but of course I don't want to submit this form to my controller.
I have a feeling there is a mistake I'm making or a simple solution. Can anyone recommend a way around this or perhaps a different approach?
Here is the controller method that handles requests to register in case that's needed:
#RequestMapping(value = "**/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") #Validated User user, BindingResult errors, ModelMap model) {
if (errors.hasErrors()) {
return "registration";
}
// Other stuff then...
return "profile"
}
If you are using "user" modelAttribute in form tag then a non-null request attribute must be present with name "user".
One way to add that in request attribute is what you did in your answer above. Other ways are:
(1) Add in ModelMap:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("user", new User());
model.addAttribute("error", "true");
return "registration";
}
(2) Add in request scope (Using WebRequest or HttpServletRequest):
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model, WebRequest webRequest) {
webRequest.setAttribute("user", new User(), WebRequest.SCOPE_REQUEST);
model.addAttribute("error", "true");
return "registration";
}
(3) Use #ModelAttribute on method:
#ModelAttribute("user")
public User user() {
return new User();
}
Please also see Using #ModelAttribute on a method and Using #ModelAttribute on a method argument
Also note that you don't have to use type attribute. See form:input and form:password.
I think the problem is specifically when a login fails and the same page is served up, albeit on a different URL path and so through a different controller method. Therefore my original suspicion that the issue is that both forms are submitted may be something of a red herring, though I still don't fully understand what's going on and that may yet have something to do with it. In any case, this is what corrected the problem for me:
I had a controller method that originally looked like this:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
In the Spring Security context I specify /loginfailed as the path to go to by default if a login attempt fails. This is where it seems the user object is needed so if I alter the signature as follows it all works:
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginFailed(#ModelAttribute("user") User user, BindingResult errors, ModelMap model) {
model.addAttribute("error", "true");
return "registration";
}
Any comments/clarification welcome from others.

ASP.NET MVC IIS request.files doesn't work

Request.Files doesn't work when I deploy to the server but it works when I test locally. What do I need to configure to make this work when deployed?
Some example of your code would be nice, but this post helped me once:
A Back To Basics Case Study: Implementing HTTP File Upload with ASP.NET MVC including Tests and Mocks
This post from Phil Haack can be useful for you:
<form action="" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
[HttpPost]
public ActionResult Index(HttpPostedFileBase file) {
if (file.ContentLength > 0) {
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
Note that here you do not examine the Request.Files collection. Also example for the multiple file upload can be found there (main idea that you should use the IEnumerable<HttpPostedFileBase> in similar manner).

Resources