Web service on Play framework not accepting POST message - playframework-2.6

I am sending the following message from an Angular Service to a play application
public createUser(user:User):any{
console.log('contacting server at '+this.API_URL +this.ADD_USER_URL +" with user data ",user);
/*http.post returns an observable. The caller's code should subscribe to this observable */
return this.http.post(this.API_URL +this.ADD_USER_URL,user)
.map(response => { //handler if post's Observable is successful. map creates new User and returns Observable<User>.
console.log('response from backend service',response);
//return new User(response); //the constructor of User allows passing an object.
/*when the observable of http.post returns (produces) data (the response), the map function prints a message and returns that response*/
return response;
})
.catch(this.handleError); //error handler if Observable fails
}
I am getting the following warning on Play's console and forbidden response.
[warn] p.filters.CSRF - [CSRF] Check failed because application/json for request /ws/users/add
WebToBackendInterfaceService::handleError
Object { headers: {…}, status: 403, statusText: "Forbidden", url: "http://localhost:9000/ws/users/add", ok: false, name: "HttpErrorResponse", message: "Http failure response for http://localhost:9000/ws/users/add: 403 Forbidden", error: "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Unauthorized</title>\n <link rel=\"shortcut icon\" href=\"data:image/png;base64,...">\n <style>\n html, body, pre {\n margin: 0;\n padding: 0;\n font-family: Monaco, 'Lucida Console', monospace;\n background: #ECECEC;\n }\n h1 {\n margin: 0;\n background: #333;\n padding: 20px 45px;\n color: #fff;\n text-shadow: 1px 1px 1px rgba(0,0,0,.3);\n border-bottom: 1px solid #111;\n font-size: 28px;\n }\n p#detail {\n margin: 0;\n padding: 15px 45px;\n background: #888;\n border-top: 4px solid #666;\n color: #111;\n text-shadow: 1px 1px 1px rgba(255,255,255,.3);\n font-size: 14px;\n border-bottom: 1px solid #333;\n }\n </style>\n </head>\n <body>\n <h1>Unauthorized</h1>\n\n <p id=\"detail\">\n You must be authenticated to access this page.\n </p>\n\n </body>\n</html>\n" }
It seems that Play's CSRF doesn't like this message. How could I solve the issue? I read that in Play, the error happens when
Request is not GET, HEAD or Option - true in my case, I am sending a POST message
Request has cookie or Authorisation header. I am not sending them explicitly. Does Angular sends it by default?
The CORS filter is not configured to trust request's origin. - I am sending request to localhost:9000. Shouldn't Play trust localhost?
How could I make Play accept my message?

This is a workaround. Correct explanation is in Angular not sending CSRF token in header. Basically, my client should send a CSRF Header with token initially created by the play framework
I do not know if this is the correct way but I had to disable CSRF in Play. It could be done in the following two ways
In routes file, add nocsrf above the rule
+nocsrf
POST /ws/users/add controllers.UserController.addUser
Or disable csrf completely in application.conf
play.filters {
# Disabled filters remove elements from the enabled list.
disabled += play.filters.csrf.CSRFFilter
}
I disabled csrf completely

Make sure you set up your frontend HTTP client with header and cookie names "Csrf-Token". I had the same issue with my Angular+Play seed, turns out Angular's defaults cookie and header names for CSRF are XSRF-TOKEN and X-XSRF-TOKEN. So in my case I had to import another Angular module to override those values:
imports: [
BrowserModule,
ReactiveFormsModule,
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'Csrf-Token',
headerName: 'Csrf-Token',
}),
routing
]
Although disabling Play's CSRF protection(based on Manu Chadha's answer) might be a workaround but it is strongly advised against.

Related

How to add the CSRF token to the HTTP header using fetch API and VanillaJS

I am trying to send a POST-request to a server that is configured to use Spring Security. When submitting my request, I get a 403 error. This is issue is due to CSRF protection. When disabling CSRF in my Spring Security Configuration, the POST request works fine.
I am using the fetch API to send my POST-request. This allows for specifying the HTTP-header that comes with the body containing my JSON-object that I am trying to POST. I am now trying to add the CSRF token to my HTTP-header. For this purpose, I have added the following two meta-tags to the head-section of my HTML:
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
I have then added the following lines to my JavaScript:
const token = document.querySelector('meta[name="_csrf"]').content;
const header = document.querySelector('meta[name="_csrf_header"]').content;
let responsePromise = fetch(
"myEndpoint",
{ method: 'POST', headers: {'Content-Type': 'application/json', header: token}, body: JSON.stringify(myJSON) });
})
I am assuming that Spring Security fills out the content of my CSRF-token. So far the error message still persists though. I think there might be nothing inside my _csrf meta-tag. When logging the content (token and header), I just receive:
${_csrf.headerName}
${_csrf.token}
While I am expecting to see the header name "X-CSRF-Token" and the actual token. So could it be that Spring security does not automatically fill out this content?
======= Update:
My project is a Maven project. I have added the Thymeleaf-dependency to my pom.xml. I don't know much about thymeleaf, but it seemed to be the easiest way to implement the Login mechanism with Spring Security. Anyways, when replacing content with th:content in my meta-tags, the header and the token are actually found and will be logged in my console. I am still not able to POST my request though, the 403 remains.
Add 'th' before the attribute for thymeleaf processing
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>

how Play sends CSRF token?

When my application retrieves index.html and other js and css files from the server, I do not see an csrf token in headers or cookies. How does play sends csrf token?
My UI is an Angular application being served from play. From the documents, I read about csrf token that
This token gets placed either in the query string or body of every form submitted, and also gets placed in the user’s session
The documentation also says that
To ensure that a CSRF token is available to be rendered in forms, and sent back to the client, the global filter will generate a new token for allGETrequests that accept HTML, if a token isn’t already available in the incoming request. - But I don't see this token in response to my initial GET request.
As my UI (and thus form) is not a play UI, I cannot use play's annotation to put csrf token in the form. I would like that when the homepage is delivered, play sends the csrf token which Angular application can store and use later.
Following are the headers I see on browser's developer console.
Response headers
Content-Length 1421
Content-Type text/html; charset=UTF-8
Date Sun, 11 Mar 2018 21:23:52 GMT
Referrer-Policy origin-when-cross-origin, strict-origin-when-cross-origin
X-Content-Type-Options nosniff
X-Frame-Options DENY
X-Permitted-Cross-Domain-Policies master-only
X-XSS-Protection 1; mode=block
Request headers (600 B)
Accept text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Cookie PLAY_SESSION=eyJhbGciOiJIUzI1N…AR2uh5KwKBhqKxQQT1wWPWC2yPyCM
Host localhost:9000
Upgrade-Insecure-Requests 1
User-Agent Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/58.0
The Action in play which servers the homepage is
def index =
Action { implicit request =>
val Token(name, value) = CSRF.getToken.get
println(s"Token name ${name}, value ${value}")
Ok(views.html.index("Your new application is ready."))
}
I can see (print) the token name and value but I am not sure if it is being sent in the Ok response.
This is a partial answer. The 3 csrf configurations of interest in play are token, cookie and header names
if none of the token, cookie and header of csrf properties are configured then the default values are csrfToken for token name), nothing gets configured for cookie and Csrf-Token for header
When token name is configured then play seem to send a PLAY_SESSION cookie. Eg token.name = "CJCsrfToken". In this case, the name of the token is CJCsrfToken instead of csrfToken. However, I couldn't find how csrfToken gets sent and how to retrieve it in the client. I have an Angular5 client and I couldn't get it to pass csrf when only token.name was configured in play.
If cookie name is configured, Play will store the csrf token in a cookie with the given name, instead of in the session. I suppose we should configure either token.name or cookie.name. Eg cookie.name = "CJCsrfCookie" means you should see a cookie with name CJCsrfCookie
Now if only cookie.name is configured but no header name is configured then Play expects that requests from client will contain the csrf token in header Csrf-Token (the default header name)
The code in Angular to accept the cookie and return header was
HttpClientXsrfModule.withOptions({ cookieName: 'CJCsrfCookie', headerName: 'Csrf-Token' }),
If you do not want to use default header name, configure the new name in header.name. This would be the name of the header to accept CSRF tokens from.
eg header.name = "CJCsrfHeader"
The code in Angular to accept the cookie and return header was
HttpClientXsrfModule.withOptions({ cookieName: 'CJCsrfCookie', headerName: 'CJCsrfHeader' }),
Note that for the Angular part, the url has to be relative. See this angular4 httpclient csrf does not send x-xsrf-token has

Change User-Agent for AJAX calls from Delphi TWebBrowser

I have a Delphi application that loads the Google Maps JavaScript API in an embedded web browser. The page it loads looks like this:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<style>
#map {
height: 400px;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {});
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3.29&key=~APIKEY~&callback=initMap">
</script>
</body>
</html>
I'm displaying the page like this in a TWebBrowser:
str := StringReplace(htmlBase, '~APIKEY~', cMapsAPIKey, []);
if not Assigned(WebBrowser.Document) then
WebBrowser.Navigate('about:blank', '1', '', '', 'User-Agent: Mozilla/5.0');
doc := WebBrowser.Document;
doc.Clear;
doc.Write(str);
doc.Close;
TWebBrowser.Navigate() will use the user agent string I've provided for the main page, but it uses this to load the scripts:
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3)
Starting with 3.29, the Google Maps JavaScript API seems to be checking the browser's user agent and displaying an error message: "You are using a browser that is not supported". This isn't an issue with 3.28 or below. The browser is supported (it's using IE 11), it's just sending the wrong user agent string.
On the JavaScript end, how can I override the user agent check without disabling warnings completely? And on the Delphi end, is there a way to change the user agent for AJAX calls?
Edit: Overriding TWebBrowser.Invoke() lets me change the user agent for all HTTP requests, but it looks like navigator.userAgent isn't being changed.
You appear to be using the VCL's TWebBrowser. Per Changing the UA (User Agent) of a TWebBrowser component, you can derive a new class from TWebBrowser to override its Invoke() method to return the desired UserAgent string when the DISPID_AMBIENT_USERAGENT property is requested. Then query the browser for its IOleControl interface and call its OnAmbientPropertyChange() method to signal to the browser that the DISPID_AMBIENT_USERAGENT property value has changed. The article has full code.
For good measure, in FMX's TWebBrowser, per Change User Agent for FireMonkey TWebBrowser, on Android you can use a helper class and RTTI trickery to access the browser's internal Java WebView object and call its WebSettings.setUserAgentString() method. Not sure about Windows, but on iOS you don't customize the user agent via the web browser itself (unless you hack the FMX framework to customize the requests it sends), you have to create a dictionary containing an item named UserAgent and register it with the global standardUserDefaults dictionary using its registerDefaults() method. How you do that in Delphi, I have no clue.
The problem why your Google Maps web page isn't loading fine in your TWebBrowser component is because it is working in Compatibility Mode. That is also the reason for mentioned User Agent string.
So why is this happening. Well TWebBrowser is just a wrapper for Internet Explorer browser API. And based on Microsoft decision any application that is using such API would be by default showing web pages in Compatibility mode.
You can disable this by using instructions here:
https://stackoverflow.com/a/25843958/3636228

Token mismatch exception even after adding protected $except array

Well, this is the third that this token mismatch is making me crazy. I tried adding the tokens as suggested by others on Stack Overflow but that did not solve the problem. Then I tried removing protection from certain routes, that is not working even. I don't know what's going on.
This is how I am removing csrf protection
protected $except = ['/donate-money', 'donate-money-main', '/donate-money-main'];
I have this in the head
<meta name="csrf-token" content="{{ csrf_token() }}">
and following code before my AJAX request
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
You don't have to pass the token in the header, but in the data you sent with the ajax request.
data: {_token: token, ......}
Removing the csrf protection is bad practice and you should avoid it.

AFNetworking Self Signed Certificate iOS 5.1 vs 6.1

I'm using AFNetworking with an iPad app to connect to a server using https with a self-signed certificate.
On my device running iOS 6.1 it all works fine but when we tested on the iOS 5.1 simulator and a 5.1 device I get a 403 response.
We've tried:
#define _AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_
and:
[operation setAuthenticationAgainstProtectionSpaceBlock:^BOOL(NSURLConnection *connection, NSURLProtectionSpace *protectionSpace) {
return YES;
}];
as was suggested elsewhere but these still gave the same error. This is the nslog of the error sent to the AFHTTPRequestOperation's failure block:
Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in (200-299), got 403" UserInfo=0x10337330 {NSLocalizedRecoverySuggestion=<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>The page cannot be displayed</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
<STYLE type="text/css">
BODY { font: 8pt/12pt verdana }
H1 { font: 13pt/15pt verdana }
H2 { font: 8pt/12pt verdana }
A:link { color: red }
A:visited { color: maroon }
</STYLE>
</HEAD><BODY><TABLE width=500 border=0 cellspacing=10><TR><TD>
<h1>The page cannot be displayed</h1>
You have attempted to execute a CGI, ISAPI, or other executable program from a directory that does not allow programs to be executed.
<hr>
<p>Please try the following:</p>
<ul>
<li>Contact the Web site administrator if you believe this directory should allow execute access.</li>
</ul>
<h2>HTTP Error 403.1 - Forbidden: Execute access is denied.<br>Internet Information Services (IIS)</h2>
<hr>
<p>Technical Information (for support personnel)</p>
<ul>
<li>Go to Microsoft Product Support Services and perform a title search for the words <b>HTTP</b> and <b>403</b>.</li>
<li>Open <b>IIS Help</b>, which is accessible in IIS Manager (inetmgr),
and search for topics titled <b>Configuring ISAPI Extensions</b>, <b>Configuring CGI Applications</b>, <b>Securing Your Site with Web Site Permissions</b>, and <b>About Custom Error Messages</b>.</li>
<li>In the IIS Software Development Kit (SDK) or at the MSDN Online Library, search for topics titled <b>Developing ISAPI Extensions</b>, <b>ISAPI and CGI</b>, and <b>Debugging ISAPI Extensions and Filters</b>.</li>
</ul>
</TD></TR></TABLE></BODY></HTML>
, AFNetworkingOperationFailingURLResponseErrorKey=<NSHTTPURLResponse: 0x875be10>, NSErrorFailingURLKey=..., NSLocalizedDescription=Expected status code in (200-299), got 403, AFNetworkingOperationFailingURLRequestErrorKey=<NSMutableURLRequest ...>}
I've replaced the URL with "..."
Any suggestions?
Fixed the problem, when setting the HTTP method of my NSMutableURLRequest
[request setHTTPMethod:#"POST"];
I wasn't capitalising "POST" ... apparently that's a problem on 5.x but not 6.x

Resources