How to prevent CSRF attack in angular7 when I use XMLHttpRequest
let xhr = new XMLHttpRequest();
var inputData = {};
xhr.open('POST', this.hostUrlConstants["baseUrl"] + this.pathUrlConstants["xxxx"] + this.pathUrlConstants["yyy"] + "/" + scenarioId, true);
Fortunately, this request will not be executed by modern web browsers thanks to same-origin policy restrictions. This restriction is enabled by default unless the target web site explicitly opens up cross-origin requests from the attacker's (or everyone's) origin by using CORS with the following header:
Access-Control-Allow-Origin: *
Related
I have a Rails API with a React client side. I have had everything in the app setup for a long time now and today while I was working on it I suddenly started getting the error:
Access to XMLHttpRequest at 'http://localhost:3000/api/v1/user/authed'
from origin 'http://localhost:8000' has been blocked by CORS policy:
The value of the 'Access-Control-Allow-Origin' header in the response
must not be the wildcard '*' when the request's credentials mode is
'include'. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
Now none of the requests in my application work at all.
The request does go through from the React app to the Rails API and the Rails API responds properly as well (I can see this in the terminal) but nothing actually happens on the Client side because I am assuming it gets blocked for the CORS reason.
Is there something I can do to fix this? Could it be that some package is somehow updated on my system and different from the project so now it breaks?
URL to make request to:
const ENDPOINT = '/api/v1',
PORT = 3000,
URL = window.location.protocol + '//' + window.location.hostname + ':' + PORT + ENDPOINT;
The request
$.ajax({
url: URL + '/' + resource,
type: verb,
data: params,
xhrFields: { withCredentials: true }
})
.done(callback)
.fail(errcallback);
Request functions have the format:
static get(resource, params, callback, errcallback) {
API.send('GET', resource, params, callback, errcallback);
}
If your API doesn't require credentials you should remove withCredentials: true.
More about withCredentials:
The XMLHttpRequest.withCredentials property is a Boolean that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
I tried to send a bearer token to an Auth0 API using Postman and it works perfectly.
I then tried the same using RestSharp (in c#) but it doesn't work at all.
Below is my code. I've tried many different formats but none of them work.. Is there any other way I can try to make it work?
var client = new RestClient("http://domain.auth0.com/api/v2/users");
RestRequest request = new RestRequest(Method.GET);
//request.AddHeader("authorization", "Bearer eyJhbGcJ9.eyJhdWQiOiJ6VU4hVWUE2.token");
//request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
//request.AddHeader("Accept", "application/json");
//RestClient client = new RestClient("http://domain.auth0.com");
//RestRequest request = new RestRequest("api/v2/users", Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Accept", "application/json");
request.AddParameter("Authorization",
string.Format("Bearer " + "eyJhbGciOI1NiIsI9.eyJhdWQiOiWmVhTWpD2VycyI6eyJhY.token"),
ParameterType.HttpHeader);
//request.AddParameter("Authorization",
// String.Format("Bearer {0}", token),
//ParameterType.HttpHeader);
var response = client.Execute(request);
PS: the token was changed.
The problem is that you're using an HTTP URL. When you issue the first request the token is included, but you receive a redirect response informing that you should be calling the HTTPS endpoint.
Since RestSharp will not include the token in the second request performed automatically due to the first redirect response you get an unauthorized response.
You need to update the URL to be HTTPS which will prevent the redirect and as a consequence solve your problem. If you want to make multiple authenticated request using the same client you also change your code to be:
using RestSharp;
using RestSharp.Authenticators;
class Program
{
static void Main(string[] args)
{
// Use the HTTPS scheme
var client = new RestClient("https://[domain].auth0.com/api/v2/users");
client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(
"eyJhbGciJIUz.eyJhdWQi4QW5OXhCNTNlNDdjIn0.vnzGPiWA", // Update the token
"Bearer");
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
Console.WriteLine("{0}", response.StatusCode);
}
}
If you really need to handle redirects and still send the token, check: https://github.com/restsharp/RestSharp/issues/414
I have a use-case where I need to spoof a white-listed Redirect URL locally when performing OAuth 2 authentication.
I'm running a very basic web-server coupled with a hosts file entry for the domain I'm spoofing. I'm able to correctly negotiate my tokens and return them to Paw, but Paw isn't picking up my access_token or refresh_token, it simply displays the raw response:
Here's my server code (with placeholders for sensitive data):
var http = require('http'),
request = require('request');
var PORT = 6109;
var server = http.createServer(function(req, res) {
var code = req.url.split('?')[1].split('=')[2];
request({
url: 'https://<access token URL>/oauth2/token?code=' + code,
method: 'POST',
form: {
'client_id': <client_id>,
'client_secret': <client_secret>,
'grant_type': 'authorization_code',
'redirect_uri': <spoofed redirect URL>
}
}, function(err, response, data) {
data = JSON.parse(data);
res.writeHead(200, {'Content-Type': 'application/json'});
res.write(JSON.stringify(data.result));
// I also tried this with the same end-result
// res.writeHead(200);
// res.write('access_token=' + data.result.access_token + '&token_type=' + data.result.token_type + '&refresh_token=' + data.result.refresh_token);
res.end();
});
});
server.listen(PORT, function() {
console.log('Server listening on port %d', PORT);
});
What am I missing? Why isn't Paw finding my tokens?
Here's my configuration for reference:
Some other noteworthy points:
The OAuth provider is non-standard and flubs quite a few things from the spec (my proxy exists in part to patch up the non-standard bits)
The domain for the Redirect URL is real, but the URL does not resolve (this is a part of the reason for the local hosts entry)
I'm not showing this part of the flow, but I am correctly completing the authorization step prior to being given the code value
I think you're probably confused between the Authorization URL and Access Token URL. When you're in Authorization Code grant type for OAuth 2, you're expected to have a user confirmation step in a web page (the Authorization URL).
Which makes me guess that instead, you're expecting instead to use the Password Grant or Client Credentials? Otherwise, if you want to use Authorization URL, you'll need to specify a webpage at the Authorization URL.
Note: I've tried your Node.js script in Paw using the two last grants I mentioned (Password Grant & Client Credentials), and it works nicely.
Update: Following the comments below, I understand more what you are doing. The Authorization Request should (if successful) return a 302 redirect response to the Redirect URL page, and append a code URL query param to it. It seems like you're returning a JSON response with the code instead, so Paw isn't catching it.
According to the OAuth 2.0 spec (RFC 6749), section *4.1.2. Authorization Response*, if granted, the code should be passed as a URL query param (i.e. a ?key=value param in the URL) to the Redirect URL when doing the redirection.
If the resource owner grants the access request, the authorization
server issues an authorization code and delivers it to the client by
adding the following parameters to the query component of the
redirection URI using the "application/x-www-form-urlencoded" format
Quoting the example from the spec, here's how the response of the Authorization Request should look like if it's a success (code is granted):
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
I saw that the Redirect URL contains "my Spoofed Uri".
When we need to use authorization code flow, we provide the authorization code and redirect Uri.
When the URI you are providing does not match the URI saved for the client in Identity server, you will not be able to get the token as the URI does not match with the client authorization code.
For example : Consider client identity in the Identity server be:
Auth Code: "xyx"
Redirect Uri: "www.mylocalhost.com\xyz"
And in your example the combination you are providing is:
Auth Code: "xyx"
Redirect Uri: "<my spoofed uri>"
As these 2 wont match there will be no token received.
I believe if you use the correct URI that is registered with the client in the Identity server, you will be able to receive the token.
In my hapijs app, given a Request object, how can I find the original, unparsed, unmodified URL?
function getRequestUrl (request) {
return ...; // What goes here?
}
I've found that I can piece it together somewhat from Request.info.host, Request.path, and Request.query, but it lacks the scheme (ie, http vs https), and is a bit of a kludge. Isn't the plain URL available somewhere?
The full URL isn't stored somewhere you can get it. You need to build it yourself from the parts:
const url = request.connection.info.protocol
+ '://'
+ request.info.host
+ request.url.path;
Even though it might seem kludgey, it makes sense if you think about it because there is no original, unparsed, unmodified URL. The HTTP request that goes over the wire doesn't contain the URL as typed into the browser address bar for instance:
GET /hello?a=1&b=2 HTTP/1.1 // request.url.path
Host: localhost:4000 // request.info.host
Connection: keep-alive
Accept-Encoding: gzip, deflate, sdch
...
And you only know the protocol based on whether the hapi server connection is in TLS mode or not (request.connection.info.protocol).
Things to be aware of
If you check either:
request.connection.info.uri or request.server.info.uri
the reported hostname will be the hostname of the actual machine that the server is running on (the output of hostname on *nix). If you want the actual host the person typed in the browser (which might be different) you need to check request.info.host which is parsed from the HTTP request's Host header)
Proxies and X-Forwarded-Proto header
If your request got passed through a proxy(ies)/load balancers/HTTPS terminators, it's possible somewhere along the line HTTPS traffic got terminated and was sent to your server on an HTTP connection, in this case you'll want use the value of the x-forwarded-proto header if it's there:
const url = (request.headers['x-forwarded-proto'] || request.connection.info.protocol)
+ '://'
+ request.info.host
+ request.url.path;
With template strings:
const url = `${request.headers['x-forwarded-proto'] || request.connection.info.protocol}://${request.info.host}${request.url.path}`;
hapi-url solves this exact problem. It is prepared to work with X-Forwarded headers to run behind a proxy. There is also an option to override the automatic resolution if the library is not able to resolve the URL correctly.
I use the following syntax now (using coffee script):
server.on 'response', (data) ->
raw = data.raw.req
url = "#{data.connection.info.protocol}://#{raw.headers.host}#{raw.url}"
console.log "Access to #{url}"
Or as javascript:
​server.on('response', function(data) {
var raw = data.raw.req;
var url = data.connection.info.protocol + "://" +
raw.headers.host + raw.url;
console.log("Access to " + url);
});
That gives you the exact URL like the user requested it.
You can't get the URL. You have to generate it. I'm using this one:
const url = request.headers['x-forwarded-proto'] + '://' +
request.headers.host +
request.url.path;
nowadays there is simply request.url:
https://hapi.dev/api?v=20.2.0#-requesturl
I have three ASP.NET WebAPI endpoints:
Identity server, which generates bearer tokens (serverA.com);
SignalR server with hub (serverB.com);
Some endpoint with a simple ASP.NET MVC view and SignalR JS client script (serverC.com).
All three servers use OAuth2 middleware as Auth mechanism. Microsoft.Owin.Cors is configured as well.
Servers use only HTTPS requests.
SignalR v2.2.0 is installed on serverB.com.
I can successfully make a cross-domain request from serverC.com to serverA.com to get bearer token, but, actually, I don't know how to pass auth token while connecting to serverB.com
There are two ways I found so far:
Pass auth token as a query string (not secure);
Apply this setting to the jQuery.ajax
$.ajaxSetup({
beforeSend: function (xhr) {
xhr.setRequestHeader('tokenKey', 'tokenValue');
}});
but it forces SignalR to use long polling only.
Is there any other way to send auth token (not in query string) so it can be consumed and validated by OAauth BearerAuthorizationProvider? Maybe, cookies, headers or any other way?
Update
CORS middleware set up for both environments to allow all data and accept credentials.
Here is my OWIN middleware:
var requestUri = context.Request.Uri.AbsolutePath;
if (string.Equals(requestUri, authRoute, StringComparison.OrdinalIgnoreCase))
{
if (!context.Request.Headers.ContainsKey("Authorization") || string.IsNullOrEmpty(context.Request.Headers["Authorization"]))
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
context.Response.Cookies.Append("BearerToken", context.Request.Headers["Authorization"]);
}
}
else
{
await Next.Invoke(context);
}
Then I do first Ajax request:
$.ajax({
url: self.communicationHubUrl + '/authenticate',
type: 'post',
cache: false,
crossDomain: true,
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', self.accessToken);
});
middleware sets auth token from header to the response cookies.
Then I call hub.Start so SignalR begins to send ajax requests.
But for some reasons I don't quite understand, cookies are present in request only if I enable xhr.withCredentials = true for ALL ajax requests via $.ajaxSetup
$.ajaxSetup({
xhrFields: {
withCredentials: true
}
});
Without this setting request doesn't include cookies. On the other hand, I guess it's not a good decision to force all ajax requests to enable such settings.
Furthermore, I've faced strange behavior in Oauth middleware: ValidateIdentity method is not invoked when request from signalR comes so instead of 401 Unauthorized, I'm getting deafult principal.
I think that putting the auth token insied a cookie will be your best bet. Unlike the ajaxSetup option, cookies are sent with EventSource and WebSocket requests.
You might need to add some middleware to the SignalR server (serverB.com) that sets the appropriate cookie when you POST the auth token before starting the SignalR connection.