File upload & Spring Security - spring-security

Based on the Spring Security documentation, I setup a MultipartFileter as the following:
#Order(1)
public class SecurityWebAppInitializer
extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
In a file upload form, I can see a CSRF input with a not-null value in a HTML file (see the code below).
<form method="POST" enctype="multipart/form-data" action="/upload">
File to upload: <input type="file" name="file" /><br />
Name: <input type="text" name="name" /><br /> <br />
<input type="submit" value="Upload" />
Press here to upload the file!
<input type="hidden" name="_csrf" value="df94be7d-675d-428c-89e5-2ebf0b473c42" />
</form>
After submitting the form, I get an error as
HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
type Status report
message Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
description Access to the specified resource has been forbidden.
What is missing here?

This problem is resolved after changing the Java configuration of the application. The followings are those changes.
In AbstractAnnotationConfigDispatcherServletInitializer class, I add the MultipartFilter in the getServletFilters method and set the MultipartConfig with a MultipartConfigElement in customizeRegistration(ServletRegistration.Dynamic registration) method. A MutlipartConfigElement originally defined in WebMvcConfigurerAdapter class is removed. And a MultipartResolver defined in the class is unchanged.
The Java configuration was set up based on the original XML configuration of the application. The approach doesn't always work based on this case.

Related

Amazon s3 form post/upload redirecting to bucket after success on iOS safari/chrome only how to stop?

I have a web app that allows users to upload photos to an S3 bucket via HTML form. It works exactly as expected on all desktop browsers, which is to do nothing after a successful upload. However in iOS safari and iOS chrome, it redirects to the bucket. If I "Handoff" the page over to safari on my mac it is an xml response of the entire bucket, it appears.
Does anyone know why this would happen and how to fix it? Normally I can find something via google and the forums but I'm finding nothing!
We are not specifying "success action redirect" or "success action status" on our form.
Also, it works as expected in chrome on an android device.
We do not want any sort of redirect to occurs, we handle things on the client after clicking the submit/post button.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<div id ="mainContent">
<form action="https://s3.amazonaws.com/{!awsKeySet.Name}" method="post" enctype="multipart/form-data" id="uploadForm">
<input type="hidden" name="key" id="key" />
<input type="hidden" name="AWSAccessKeyId" value="{!awsKeySet.AWS_AccessKey_Id__c}" />
<input type="hidden" name="policy" value="{!policy}" />
<input type="hidden" name="signature" value="{!signedPolicy}" />
<input type="hidden" name="acl" value="{!acessType}" />
<input type="hidden" name="Content-Type" value="{!Content_Type}" />
<!--input type="hidden" name="success_action_status" value="201" /-->
<!--input type="hidden" name="success_action_redirect" value="{!ForRedirect}" /-->
<h4 class="fileToUpload">Select a File to Upload in AWS</h4><br />
<div class="row">
<input type="file" size="50" name="file" id="file" />
</div>
<div id="fileName"></div>
<div id="fileSize"></div>
<div id="fileType"></div>
<div class="row">
<input type="submit" value="Upload" id="btn_submit" />
</div>
<div id="progressNumber"></div>
</form>
</div>
<script>
$(document).ready(function () {
var _requestBucket;
$("#btn_submit").click(function(event){
//alert(1);
event.preventDefault();
var _file;
_file = $("#file").val().replace(/.+[\\\/]/, "");
console.log('_file '+ _file);
$("#key").val(_file);
$("#uploadForm").submit();
});
});
</script>
Thank you!
I wish someone could have shed more light on this but sadly, this is a bug in iOS webkit browsers. It appears to have been fixed in Android and Desktop. So, if anyone stumbles on this problem, included is the bugzilla link for it
Bugzilla link for Proper handling of HTTP 204
More detail : According to the amazon s3 docs
If the value is set to 200 or 204, Amazon S3 returns an empty document with a
200 or 204 status code.
If the value is set to 201, Amazon S3 returns an XML document with a
201 status code.
If the value is not set or if it is set to an invalid value, Amazon S3
returns an empty document with a 204 status code.
According to HTTP 1.1 -
10.2.5 204 No Content
The server has fulfilled the request but does not need to return an
entity-body, and might want to return updated metainformation. The
response MAY include new or updated metainformation in the form of
entity-headers, which if present SHOULD be associated with the
requested variant.
If the client is a user agent, it SHOULD NOT change its document view
from that which caused the request to be sent. This response is
primarily intended to allow input for actions to take place without
causing a change to the user agent's active document view, although
any new or updated metainformation SHOULD be applied to the document
currently in the user agent's active view.
The 204 response MUST NOT include a message-body, and thus is always
terminated by the first empty line after the header fields.
This is not happening in either webkit browsers for iOS, currently. Sadly. I cannot find a way around this, other for Apple to fix it!

Grails 3 CSRF protection

Is it possible to configure CSRF protection in grails3 app using spring-security plugin, I can't find anything except useToken attribute for grails form and then call withForm inside controller. But this is actually not a very flexible solution. I like approach with filter like here
For csrf protection I reused org.springframework.security.web.csrf.CsrfFilter. You need to define new bean in grails resouces.groovy (See snipet below - csrfFilter bean). You can define your own accessDeniedHandler and requireCsrfProtectionMatcher. Here is the snippet from resources.groovy:
csrfFilter(CsrfFilter, new HttpSessionCsrfTokenRepository()) {
accessDeniedHandler = ref('fnAccessDeniedHandler')
requireCsrfProtectionMatcher = ref('fnRequireCsrfProtectionMatcher')
}
Now in Bootstrap.groovy add this filter into filter chain:
SpringSecurityUtils.clientRegisterFilter('csrfFilter', SecurityFilterPosition.LAST.order + 10)
Now in your main layout GSP add following tags to add csrf token on each page:
<meta name="_csrf" content="${_csrf?.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf?.headerName}"/>
So now csrf token presented on each page of your app, you can use it for each ajax request for example (snippet from application.js (I'm using grails 3)):
$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
For each jquery ajax request we are sending csrf token now.
You can implement it with <g:form> tag:
Example:
<g:form useToken="true" uri="/logout">
Documentation: http://grails.github.io/grails-doc/latest/guide/single.html#formtokens
In my case, I'm using Spring code, so, additionally, I should add manually a _csrf hidden field in the form.
<g:form useToken="true" uri="/logout">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<input type="submit">Logout</input>
</g:form>
Result:
<form action="/nacho/logout" method="post" >
<!-- this two lines are added automatically by Grails -->
<input type="hidden" name="SYNCHRONIZER_TOKEN" value="883a1037-a2c9-4997-8254-e59da6303494" id="SYNCHRONIZER_TOKEN" />
<input type="hidden" name="SYNCHRONIZER_URI" value="/nacho/userInfo" id="SYNCHRONIZER_URI" />
<!-- this line was added by myself, but, using the ${_csrf} variable -->
<input type="hidden" name="_csrf" value="0928f13c-02aa-4122-8ebe-a1239855a85b"/>
<input type="submit">Logout</input>
</form>

Expected CSRF token not found. Has your session expired? + Spring security + CSRF

I am trying to implement CSRF via spring security.
But, facing following error in browser, after clicking login button(from home.jsp)
"Expected CSRF token not found. Has your session expired?"
I have following configuration in
spring-security-config.xml:
<http auto-config="false">
<csrf/>
</http>
home.jsp:
<form action="j_spring_security_check" id="LoginForm" method="post">
<input type="text" title="Username" name="j_username" id="j_username"
class="inset-shadow defaultText" placeholder="Username" />
<input type="password" title="Password" name="j_password"
id="j_password" class="inset-shadow defaultText" placeholder="Password" />
<input type="submit" class="submit" title="LOGIN"
onsubmit="javascript:{loginSubmit();}"/>
<input type="hidden" name="_csrf" value="dc7ce2be-f73b-4086-8f90-8ef00b8f81d5"/>
</form>
Error from jboss server.log:
2015-09-14 19:56:10,221 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/].[jsp]] (http-127.0.0.1-8190-2) Servlet.service() for servlet jsp threw exception: java.lang.IllegalStateException: getAttribute: Session already invalidated
at org.apache.catalina.session.StandardSession.getAttribute(StandardSession.java:1024) [:6.1.0.Final]
at org.apache.catalina.session.StandardSessionFacade.getAttribute(StandardSessionFacade.java:110) [:6.1.0.Final]
at org.apache.jsp.WEB_002dINF.jsp.home_jsp._jspService(home_jsp.java:99)
Note : Not sure whether the error from server.log relates to CSRF
Any help on this problem is highly appreciated?
Instead of
<input type="hidden" name="_csrf" value="dc7ce2be-f73b-4086-8f90-8ef00b8f81d5"/>
did you try
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
As per the docs, the csrf and token would be computed by Spring and appended as a request parameter, with name as ${_csrf.parameterName} and value as ${_csrf.token}.
I had the same error.
I my case, the html page containing the form have a link tag to a css file into my web application.
This link to css file has been also protected by spring security and due to this, I had your error.
When I unprotect link to css file, no error occur.
This is an example how to unprotect files in Spring Security with Java Config
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**");
}

Binding a Set of domain object in Grails command object

I'm trying to bind a Set of objects to my Grails Command Objects, but currently, it doesn't work an throws errors:
Field error in object 'NewPersonCommand' on field 'addresses': rejected value [[Ljava.lang.String;#6d64b5fb]; codes [NewPersonCommand.addresses.typeMismatch.error,NewPersonCommand.addresses.typeMismatch,newPersonCommand.addresses.typeMismatch.error,newPersonCommand.addresses.typeMismatch,typeMismatch.NewPersonCommand.addresses,typeMismatch.addresses,typeMismatch.java.util.Set,typeMismatch]; arguments [addresses]; default message [Could not find matching constructor for: Address(java.lang.String)]>
I have the following command object in my Grails application:
class NewPersonCommand {
String name
Set<Address> addresses
}
And the code for the form I'm posting looks like this:
<input type="hidden" name="addresses" value="1" />
<input type="hidden" name="addresses" value="4" />
<input type="hidden" name="addresses" value="18" />
Any idea how I can fix this, so I can bind my set of addresses to the command object?

Amazon S3 Upload via Post and ASP.NET MVC

I've been trying in vain for 2 days now to do a simple upload to my Amazon S3 Bucket. Below is my rendered form:
<form action="http://s3.amazonaws.com/MYBUCKETNAME" method="post" enctype="multipart/form-data">
<input type="hidden" name="AWSAccessKeyId" value="MYACCESSKEY" />
<input type="hidden" name="acl" value="private" />
<input type="hidden" name="key" value="UserUploads/TestUser/${filename}" />
<input type="hidden" name="success_action_redirect" value="http://WWW.MYURL.COM/" />
<input type="hidden" name="policy" value="POLICY" />
<input type="hidden" name="signature" value="SIGNATURE" />
<div>
Please specify a file, or a set of files:
<input type="file" name="file" size="100" />
</div>
<input type="submit" value="Upload" />
</form>
and my policy document looks like this:
{
expiration = "2011-12-08T12:00:00.000Z",
conditions = [
["eq","bucket","MYBUCKETNAME"],
["eq","acl","private"],
["starts-with","$key","UserUploads/TestUser/"],
["eq","success_action_redirect", "HTTP://WWW.MYURL.COM/"]
]
}
I get the following error:
Code: AccessDenied,
Message: Invalid according to Policy: Policy Condition failed: ["eq", "bucket", "MYBUCKETNAME"]
Does anyone have any ideas please, I'm grabbing at straws here. Also not sure if my Bucket Policy and ACL is correct.
According to http://doc.s3.amazonaws.com/proposals/post.html#Access_Control
Matching a Particular Value
Description: There are certain fields that you want to match a
particular value, such as matching a bucket name or requiring that an
object is uploaded using the public-read access control policy. The
field value is case sensitive, but the name is not.
Syntax: There are two ways to require that the field fieldname matches
the string value. The value is case sensitive. If the value starts
with a dollar sign ($), the dollar sign must be escaped with a
backslash (\$)
[ "eq", "$fieldname", "S" ],
(Note the $ prefix).
So your policy should have $ in sets where you using "eq" format
Why not do this in C# with the S3 SDK (http://aws.amazon.com/sdkfornet/), it's a bit more secure.
Here's my blog post with more detail: http://bradoyler.com/post/3614362044/uploading-an-image-with-aws-sdk-for-net-c.
Cheers and Good luck.
I was getting the following error:
Invalid according to Policy: Policy Condition failed: [\"eq\", \"$bucket\"
After many hours, I learned that you can't have a bucket with uppercase letters. Changing the bucket to lowercase fixed it.

Resources