I am trying to make a post request with jquery to a spring rest controller I have setup. This works perfectly fine with the $.get request, however the post request is giving me a 403 error in the console. Is there any better way to handle this, or get it working at all?
$("#testBtn").click(() => {
$.post("/test").done((data) => {
console.log(data);
})
});
My Controller:
#RestController
public class LiveValidationController {
#PostMapping("/test")
public String checkEmail() {
return "hello";
}
}
403 status = forbidden.
You receive this error because you have CSRF protection enabled.
Solution 1: send csrf token in your post from js
Solution 2: disable csrf in the spring security configuration.
The csrf protection is usually enabled by default if you have a dependency to spring-security module
Related
I am using https://github.com/sahat/hackathon-starter#recommended-design-resources and trying to add post method and send a response as json. I am getting Error: CSRF token missing.
I want to use the Lusca and just wondering if there is a way to just add csrf token without doing whitelist/backlist url or using a form. Please take a look at app.js https://github.com/sahat/hackathon-starter/blob/master/app.js
exports.postTest = (req, res) => {
//tried the following.
//res.local = {_crf:"_crf"} no success
res.send("hello");
};
There must be a way to deal with this kind of issues.
I am working on an app that will submit data to a REST API and have some questions.
How does jQuery know if my post request was successful or not? Is it only looking at the HTTP status?
Is there a convention on what to return from a POST request to a REST API?
JavaScript
$.post( '/API/removeUser', { Eid: id }, function(data) { row.remove(); } );
PHP SLIM Framework
$app->POST('/API/removeUser', function () use ($app) {
// Get the ID from the jQuery post
$Eid = trim(stripslashes(htmlspecialchars($_POST['Eid'])));
echo json_encode(removeFunction($Eid));
});
Your backend should always return the appropriate HTTP status code along with the actual data. 404 for resources that were not found, 403 for unauthorized requests, 200 for successful requests etc. Most AJAX libraries (including jQuery) will rely on those for determining the result of the operation.
If you need more fine-grained error reporting, you could always include a field like "errorCode" in your response that contains an application-level error code that you define yourself and react to accordingly in your frontend code.
I am having a controller.js
ListNewsCtrl.$inject = ['$http', '$scope', 'datacontext'];
function ListNewsCtrl( $http, $scope, datacontext) {
$scope.names = [];
$http.get("http://www.w3schools.com/website/Customers_JSON.php")
.success(function (response) {$scope.names = response;console.log($scope.names)});
};
I get the data that I want. But when I change to a different site I get the followinf msg :
XMLHttpRequest cannot load https://URL. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3424' is therefore not allowed access. The response had HTTP status code 404.
The information I am trying to access are not requiring access token ?
The solution to my answer would be this :
http://blog.novanet.no/angularjs-with-jsonp-and-how-i-get-to-work-on-time/#2
However,I get this error : Uncaught SyntaxError: Unexpected token : I get small syntax issues . But at least I can see my data
CORS is enabled server-side. The domain you're requesting does not allow CORS requests, and that is not something you can edit or configure on the client end.
If the domain does allow CORS, then whatever you're using to host your local web server on localhost is not allowing it.
If cross-site requests are allowed, try
$http.jsonp("http://www.w3schools.com/website/Customers_JSON.php")
.success(function(data){
console.log(data);
});
I would not say its a perfect approach but better workaround for cors.
The Yahoo! Query Language is an expressive SQL-like language that lets you query, filter, and join data across Web services. Great thing about Yahoo YQL is that it is CORS-enabled :)
Client -> YQL -> API Server
Run Sample Here
$.getJSON("http://query.yahooapis.com/v1/public/yql",
{
q: "select * from json where url=\"https://erikberg.com/mlb/standings.json\"",
format: "json"
},
function (data) {
if (data.query.results) {
alert(data.query.results.json.standing);
} else {
alert('no such code: ' + code);
}
}
);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Here is a cool Tutorial
This will at least solve your cors problem in different ways.
Happy Helping!
I'm implementing a website in Angular.js, which is hitting an ASP.NET WebAPI backend.
Angular.js has some in-built features to help with anti-csrf protection. On each http request, it will look for a cookie called "XSRF-TOKEN" and submit it as a header called "X-XSRF-TOKEN" .
This relies on the webserver being able to set the XSRF-TOKEN cookie after authenticating the user, and then checking the X-XSRF-TOKEN header for incoming requests.
The Angular documentation states:
To take advantage of this, your server needs to set a token in a JavaScript readable session cookie called XSRF-TOKEN on first HTTP GET request. On subsequent non-GET requests the server can verify that the cookie matches X-XSRF-TOKEN HTTP header, and therefore be sure that only JavaScript running on your domain could have read the token. The token must be unique for each user and must be verifiable by the server (to prevent the JavaScript making up its own tokens). We recommend that the token is a digest of your site's authentication cookie with salt for added security.
I couldn't find any good examples of this for ASP.NET WebAPI, so I've rolled my own with help from various sources. My question is - can anyone see anything wrong with the code?
First I defined a simple helper class:
public class CsrfTokenHelper
{
const string ConstantSalt = "<ARandomString>";
public string GenerateCsrfTokenFromAuthToken(string authToken)
{
return GenerateCookieFriendlyHash(authToken);
}
public bool DoesCsrfTokenMatchAuthToken(string csrfToken, string authToken)
{
return csrfToken == GenerateCookieFriendlyHash(authToken);
}
private static string GenerateCookieFriendlyHash(string authToken)
{
using (var sha = SHA256.Create())
{
var computedHash = sha.ComputeHash(Encoding.Unicode.GetBytes(authToken + ConstantSalt));
var cookieFriendlyHash = HttpServerUtility.UrlTokenEncode(computedHash);
return cookieFriendlyHash;
}
}
}
Then I have the following method in my authorisation controller, and I call it after I call FormsAuthentication.SetAuthCookie():
// http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks
// http://docs.angularjs.org/api/ng.$http
private void SetCsrfCookie()
{
var authCookie = HttpContext.Current.Response.Cookies.Get(".ASPXAUTH");
Debug.Assert(authCookie != null, "authCookie != null");
var csrfToken = new CsrfTokenHelper().GenerateCsrfTokenFromAuthToken(authCookie.Value);
var csrfCookie = new HttpCookie("XSRF-TOKEN", csrfToken) {HttpOnly = false};
HttpContext.Current.Response.Cookies.Add(csrfCookie);
}
Then I have a custom attribute which I can add to controllers to make them check the csrf header:
public class CheckCsrfHeaderAttribute : AuthorizeAttribute
{
// http://stackoverflow.com/questions/11725988/problems-implementing-validatingantiforgerytoken-attribute-for-web-api-with-mvc
protected override bool IsAuthorized(HttpActionContext context)
{
// get auth token from cookie
var authCookie = HttpContext.Current.Request.Cookies[".ASPXAUTH"];
if (authCookie == null) return false;
var authToken = authCookie.Value;
// get csrf token from header
var csrfToken = context.Request.Headers.GetValues("X-XSRF-TOKEN").FirstOrDefault();
if (String.IsNullOrEmpty(csrfToken)) return false;
// Verify that csrf token was generated from auth token
// Since the csrf token should have gone out as a cookie, only our site should have been able to get it (via javascript) and return it in a header.
// This proves that our site made the request.
return new CsrfTokenHelper().DoesCsrfTokenMatchAuthToken(csrfToken, authToken);
}
}
Lastly, I clear the Csrf token when the user logs out:
HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");
Can anyone spot any obvious (or not-so-obvious) problems with that approach?
Your code seems to be fine. The only thing is, you don't need most of the code you have as web.api runs "on top" of asp.net mvc, and latter has built in support for anti-forgery tokens.
In comments dbrunning and ccorrin express concerns that you only able to use build in AntiForgery tokens only when you are using MVC html helpers. It is not true. Helpers can just expose session based pair of tokens that you can validate against each other. See below for details.
UPDATE:
There is two methods you can use from AntiForgery:
AntiForgery.GetTokens uses two out parameters to return cookie token and form token
AntiForgery.Validate(cookieToken, formToken) validates if pair of tokens is valid
You totally can repurpose those two methods and use formToken as headerToken and cookieToken as actual cookieToken. Then just call validate on both within attribute.
Another solution is to use JWT (check eg MembershipReboot implementation)
This link shows how to use built in anti-forgery tokens with ajax:
<script>
#functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$.ajax("api/values", {
type: "post",
contentType: "application/json",
data: { }, // JSON data goes here
dataType: "json",
headers: {
'RequestVerificationToken': '#TokenHeaderValue()'
}
});
</script>
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
Also take a look at this question AngularJS can't find XSRF-TOKEN cookie
This solution isn't secure since CSRF attacks are still possible as long as the Auth cookie is valid. Both the auth and the xsrf cookie will be sent to the server when an attacker makes you perform a request via another site, and therefore you are still vulnerable until the user does a "hard" logout.
Each request or session should have its own unique token to truly prevent CRSF attacks. But probably the best solution is to not use cookie based authentication but token based authentication such as OAuth. This prevents other websites from using your cookies to perform unwanted requests, since the tokens are used in http headers instead of cookies. And http headers are not automatically send.
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
AngularJS Token Authentication using ASP.NET Web API 2, Owin, and Identity
These excellent blog posts contain information of how to implement OAuth for WebAPI. The blog posts also contains great information of how to integrate it with AngularJS.
Another solution might be to disable CORS and only accept incoming requests from whitelisted domains. However this won't work for non-website applications, such as mobile and/or desktop clients. Next to that once your website is vulnerable to a XSS attack the attacker will still be able to forge requests on your behalve.
I think your code is flawed. The whole idea around prevent CSRF is to prevent a unique token on each REQUEST, not each session. If the anti-forgery token is a session persisted value, the ability to perform CSRF still remains. You need to provide a unique token on each request...
Haven't had any problems pointed out with the code, so I consider the question answered.
I have some url secured with spring (configured through xml). It works. However when I try to hit that endpoint with an ajax request I get a 302 (found) response. This redirects my ajax call to the login page (so I GET the html). However I'd like to get a 401 (unauthorized) response with the url of the login page available to the client application, so I can redirect the user there with javascript. This question seems to be the closest to what I want, but there's no example and it suggests changing the controller again. Is there no configuration in spring-security that will give me a 401 and a url (or some other sensible error message and the url of the login page)?
You can extend LoginUrlAuthenticationEntryPoint. Here is my one:
package hu.progos.springutils;
// imports omitted
public class AjaxAwareLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
} else {
super.commence(request, response, authException);
}
}
}
Then configure spring to use your implementation:
<beans:bean id="authEntryPoint" class="hu.progos.springutils.AjaxAwareLoginUrlAuthenticationEntryPoint" scope="singleton>
<beans:property name="loginFormUrl" value="/login.html" />
</beans:bean>
<http entry-point-ref="authEntryPoint">
<!-- your settings here -->
</http>
There are a million ways to do this of course. But the short solution to your problem is this configuration snippet:
<bean id="customAuthEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/your-custom-login" />
</bean>
I also take a step further and turn off the security auto-config so I can map the above entry point like so:
<security:http auto-config="false" entry-point-ref="customAuthEntryPoint">
...
...
</security:http>
I also override a bunch of spring security classes to get the security model to do exactly what I want. It's a slippery slope, but it's nice having the control once it works the way you want it to.