HTTP Status 403 - Expected CSRF token not found.Has session expired? - spring-mvc

I have a Spring MVC application, view layer is jsp based.At times I get this error message and this message is true, session really got expired.If I login once again then it is all fine.
I am using following mechanism to send CSRF token:
In head section 2 meta tags are added:
<meta name="_csrf" content="${_csrf.token}" />
<meta name="_csrf_header" content="${_csrf.headerName}" />
In every Ajax call,token and header are retrieved:
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
Then with XMLHttpRequest this header & token are sent:
$.ajax({
type : "GET",
url : xxx,
beforeSend : function(xhr) {
xhr.setRequestHeader(header, token);
},
complete : function() {
},
success : function(response) {
}
});
This is how it is in most jsp pages.I have tried to capture the token in an alert message & it works. I want to redirect the user to the login page if the session has expired via a page which will show why the user is getting redirected. How to go about this?
In the server side Spring we have used xml based configuraton:
<http auto-config="true" use-expressions="true">
...
<csrf/>
</http>

I added following to application-security.xml which solved the problem for this particular scenario, though I am facing CSRF exception in some other scenario :
<http auto-config="true" use-expressions="true">
...
<session-management invalid-session-url="/login">
<concurrency-control expired-url="/login" />
</session-management>
</http>

Related

Cors issues using sharepoint

i'm trying to query using REST API (sharepoint) and get some results.
this is where i've started
https://www.slideshare.net/CoreyRoth/fives-ways-to-query-share-point-2013-search-sharepoint-summit-toronto-2013, slide 31
this is what i have
document.getElementById("restApi")
.addEventListener("click", restApi, false);
function restApi() {
var queryUrl = "https://myAppDomain.org/_api/search/query?querytext='" + $("#restSearch").val() + "'";
$.ajax({
type: "GET",
url: queryUrl,
xhrFields: {
withCredentials: true
},
headers: {
"Accept": "application/json; odata=verbose"
},
success: function (data) {
var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
},
error: function (error) {
}
});
}
this is the problem.
XMLHttpRequest cannot load "https://myAppDomain.org/_api/search/query?querytext=x" No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:63734' is therefore not allowed
access.
and another way i've tried providing password and username
headers: {
"Accept": "application/json; odata=verbose",
"password": "my password",
"username": "my account name"
},
for which i get
XMLHttpRequest cannot load
https://myAppDomain.org/_api/search/query?querytext=x. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:63734' is therefore not allowed
access. The response had HTTP status code 401.
I'm using .net webApi, for which i've installed the cors packages, configuration
// Enabled cors
config.EnableCors(new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true });
The app are internal and i'm using windows credentials pass and account name.
So why am i getting this error, what do i do wrong?, i've searched and tried other solutions but always get the same problem related to
No 'Access-Control-Allow-Origin' header is present on the requested
Open IIS
Go to Sites and find your Sharepoint site
Right-click -> Explore
Open web.config
Add the following code inside system.webServer node
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Save and exit
I've managed to solve my issue (partially, because i would have wanted to use CSOM rather then REST API on client side) by allowing cross domain calls from my application, directly to the application(sharepoint app) for which i made the request.
So in the requested app i've added the following file :
web.config
in section :
system.webserver/httpprotocol/customheaders
content :
<add name="Access-Control-Allow-Origin" value="https://myApplicationDomain.zzz" />
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Credentials" value="true" />
If the server is being developed by you
Have you put the "Access-Control-Allow-Origin: localhost" in your .htaccess or in your webserver configuration?
You can, also, add that header in your response from the server.

User authentication failure when using Dojo and Spring Security due to failure to verify CSRF token

Below is a fragment of my Dojo code verifying the username and password of the user.
var deferred = dojo.xhrPost({
url : "../../j_spring_security_check",
load : function(response, ioArgs) {
return response;
},
handleAs : "json-comment-filtered",
timeout : 180000,
content : {
"j_username" : usrName,
"j_password" : password
},
error : function(response, ioArgs) {
return response;
}
});
On the server side Spring configuration is:
<form-login login-page="/service/authenticate" username-parameter="j_username" password-parameter="j_password" login-processing-url="/j_spring_security_check" default-target-url="/service/authenticate" always-use-default-target="true" authentication-failure-url="/service/authenticate" />
On attempting to authenticate the user. I keep getting:
Failed to load resource: the server responded with a status of 403 (Could not verify the provided CSRF token because your session was not found.)
on the xxx/j_spring_security_check endpoint.
I have enabled:
<headers><frame-options policy="SAMEORIGIN"/></headers>
The goal is to enable Cross Site Request Forgery (CSRF) protection. Any ideas on how I could solve this.
Two changes had to be made both on the server and client code base.
Server
This was clearly explained in the link shared. spring security reference
<http ...>
<csrf token-repository-ref="tokenRepository" />
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository">
<b:property name="cookieHttpOnly" value="false"/>
</b:bean>
Client
Intercepted xhr request and added the X-XSRF-TOKEN header by obtaining the XSRF-TOKEN from the cookie. This works but from a security perspective better alternatives than storing the token in cookie can be used.
require(
["dojo/request/notify", "dojo/cookie"],function(notify,Cookie ){
notify("send", function(response, cancel) {
response.xhr.setRequestHeader('X-XSRF-TOKEN', Cookie("XSRF-TOKEN"));
});}

Server application has enabled CORS yet client gets "Access-Control-Allow-Origin" error

I have an RESTful API and I've added this to allow Cross-origin request
public static void Register(HttpConfiguration config)
{
...
// Allow Cross-origin request
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors();
...
}
My client is an AngularJS application calling the server like this
var json = JSON.stringify({
Request: 'INIT'
}),
req = {
method: 'GET',
url: 'http://localhost:51615/api/Call/2/' + json
}
$http(req)
.then(function(response){
console.info('response', response)
})
.catch(function(error){
console.error(error);
})
Even thou Cross-origin should be allowed I still get this error
XMLHttpRequest cannot load
http://localhost:51615/api/Call/2/%7B%22Request%22:%22INIT%22%7D. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:86' is therefore not allowed
access. The response had HTTP status code 400.
How can this be?
Big thanks to #shaunhusain for pointing out that I should take a look at the network response in chrome. Here I saw that the problem was with unsafe data in my url string (the json part). Changing the request from get to post along with json as data and not part of the url fixed my problem
try with putting this two appsettings in web.config of your web api project
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<appSettings>
</configuration>

how to post to soap web service from angularjs?

I am moved to a soap Web service ver 3.5. The post request are working fine for .net 4.5v. But while post data to service i am getting errors:
At browser
Error: OPTIONS http://localhost/webserv/WebService1.asmx/HelloWorld
XMLHttpRequest cannot load http://localhost/webserv/WebService1.asmx/HelloWorld. Invalid HTTP status code 500
When i saw the details it is showing this
System.InvalidOperationException: Missing parameter: obj.
at System.Web.Services.Protocols.ValueCollectionParameterReader.Read(NameValueCollection collection)
at System.Web.Services.Protocols.HttpServerProtocol.ReadParameters()
at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
My implementation is as:
$http({
method: 'POST',
url: 'http://localhost/webserv/WebService1.asmx/HelloWorld',
datatype: 'json',
data:JSON.stringify({obj:details})
contentType: "application/json; charset=utf-8"
}).success(function(data,status, headers, config){
if(status == '200'){
alert(data.d);
}
}).error(function(data,status, headers, config){});
And at service
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string HelloWorld(Details obj)
{
return JsonConvert.SerializeObject((new Helper()).GetDetails(obj.Id);
}
inside web config:
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
How could i be able to call post request and get result in json format?
From this
Error: OPTIONS http://localhost/webserv/WebService1.asmx/HelloWorld I can assume you are facing CORS issue.
There are two ways to solve these issue:
On server side you need to add CORS headers. See this link for more information.
Use Chrome browser:
Open chrome browser using following command and browse to your page from where this service is getting called.
chrome.exe --disable-web-security

How to make a session enabled ASP.NET web service work with an ajax client in a different domain?

Me and my work mate have spent lots of time finding this answer, so I thought I should share this self answered question.
I have a web service (ASP.NET 4.5) with a method that needs to use the session state. In order to archive this, I have decorated the method with EnableSession=true. This is what MS suggests to do in this article (See "Code Block A" at the end).
In order for the web service to be able to re-use the session, it needs to find a cookie with the session ID. This cookie is created and set, on the server side, by the web server itself the first time the method is called. In order to re-send this cookie with every call, MS suggests to use a "cookie container" that gets assigned to the web service proxy, saved between calls and reused with every call. (See "Code Block B" at the end).
This works great, as long as you plan to call the web service from the server side. I need to call it from the client side using a jQuery ajax JSON Post call (See "Code Block C" at the end). This code works great. In fact, the cookie with the session ID is passed to the web service automatically. No need of the cookie jar...as long as the client and the web service are on the same domain.
If the client is in a different domain (domaina.com) than the web service (domainb.com), the cookie with the session id is not passed along at all. As a result, every call to the web service is considered the first one.
I have the following headers defined on the web config to allow cross domain calls.
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
So, what is missing?
Code Block A: Web Service with decorated method
public class Util: WebService {
[ WebMethod(EnableSession=true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public int SessionHitCounter() {
if (Session["HitCounter"] == null) {
Session["HitCounter"] = 1;
}
else {
Session["HitCounter"] = ((int) Session["HitCounter"]) + 1;
}
return ((int) Session["HitCounter"]);
}
}
Code Block B: Server Side Cookie Jar Solution
<script runat="server">
void EnterBtn_Click(Object Src, EventArgs E)
{
// Create a new instance of a proxy class for your XML Web service.
ServerUsage su = new ServerUsage();
CookieContainer cookieJar;
// Check to see if the cookies have already been saved for this session.
if (Session["CookieJar"] == null)
cookieJar= new CookieContainer();
else
cookieJar = (CookieContainer) Session["CookieJar"];
// Assign the CookieContainer to the proxy class.
su.CookieContainer = cookieJar;
// Invoke an XML Web service method that uses session state and thus cookies.
int count = su.PerSessionServiceUsage();
// Store the cookies received in the session state for future retrieval by this session.
Session["CookieJar"] = cookieJar;
// Populate the text box with the results from the call to the XML Web service method.
SessionCount.Text = count.ToString();
}
</script>
Code Block C: Ajax Call
$.ajax({
type: "POST",
url: web_services_url + "/SessionHitCounter",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
alert(msg.d);
}
});
After hours of thinking and Google searching my mate found that we were missing
An extra header ("Access-Control-Allow-Credentials")
Changing the "*" on "Access-Control-Allow-Origin" to the specific domain that is calling the web service
Adding an extra parameter on the ajax call (withCredentials)
These are the headers that are required to enable CORS
<add name="Access-Control-Allow-Origin" value="http://localhost:53710" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Credentials" value="true" />
This is the ajax call with the extra parameter
$.ajax({
type: "POST",
xhrFields: { withCredentials: true },
url: web_services_url + "/SessionHitCounter",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
alert(msg.d);
}
});

Resources