www.example1.com
api.example2.com
I want to restrict the access to WebApi. I've tried to implement the Anti-Forgery Token:
When I create the GET request to WebApi with Anti-forgery token then I get an exception because the request doesn't contains this token.
In method called ValidateRequestHeader
is variable cookie = null
.
How can I fix following code? Is this correct solution?
MVC project (front-end) - for development is localhost:33635
:
Index.cshtml
<div class="container">
<div class="row">
<div class="col-md-12">
<input id="get-request-button" type="button" class="btn btn-info" value="Create request to API Server" />
<br />
<div id="result"></div>
</div>
</div>
</div>
@section scripts
{
<script type="text/javascript">
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$(function () {
$("#get-request-button").click(function () {
$.ajax("http://localhost:33887/api/values", {
type: "GET",
contentType: "application/json",
data: {},
dataType: "json",
headers: {
'RequestVerificationToken': '@TokenHeaderValue()'
}
}).done(function (data) {
$("#result").html(data);
});
return false;
});
});
</script>
}
WebApi project - for development is localhost:33887
:
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.EnableCors(new EnableCorsAttribute("http://localhost:33635", "*", "*"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
ValidateHttpAntiForgeryTokenAttribute.cs:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class ValidateHttpAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var request = actionContext.Request;
try
{
if (IsAjaxRequest(request))
{
ValidateRequestHeader(request);
}
else
{
AntiForgery.Validate();
}
}
catch (Exception)
{
actionContext.Response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.Forbidden,
RequestMessage = actionContext.ControllerContext.Request
};
return FromResult(actionContext.Response);
}
return continuation();
}
private Task<HttpResponseMessage> FromResult(HttpResponseMessage result)
{
var source = new TaskCompletionSource<HttpResponseMessage>();
source.SetResult(result);
return source.Task;
}
private bool IsAjaxRequest(HttpRequestMessage request)
{
IEnumerable<string> xRequestedWithHeaders;
if (!request.Headers.TryGetValues("X-Requested-With", out xRequestedWithHeaders)) return false;
var headerValue = xRequestedWithHeaders.FirstOrDefault();
return !String.IsNullOrEmpty(headerValue) && String.Equals(headerValue, "XMLHttpRequest", StringComparison.OrdinalIgnoreCase);
}
private void ValidateRequestHeader(HttpRequestMessage request)
{
var headers = request.Headers;
var cookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
IEnumerable<string> xXsrfHeaders;
if (headers.TryGetValues("RequestVerificationToken", out xXsrfHeaders))
{
var rvt = xXsrfHeaders.FirstOrDefault();
if (cookie == null)
{
throw new InvalidOperationException($"Missing {AntiForgeryConfig.CookieName} cookie");
}
AntiForgery.Validate(cookie.Value, rvt);
}
else
{
var headerBuilder = new StringBuilder();
headerBuilder.AppendLine("Missing X-XSRF-Token HTTP header:");
foreach (var header in headers)
{
headerBuilder.AppendFormat("- [{0}] = {1}", header.Key, header.Value);
headerBuilder.AppendLine();
}
throw new InvalidOperationException(headerBuilder.ToString());
}
}
}
ValuesController:
public class ValuesController : ApiController
{
// GET: api/Values
[ValidateHttpAntiForgeryToken]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET: api/Values/5
public string Get(int id)
{
return "value";
}
// POST: api/Values
public void Post([FromBody]string value)
{
}
// PUT: api/Values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Values/5
public void Delete(int id)
{
}
}
ASP.NET Core uses a hidden field to store the anti-forgery token and uses the ValidateAntiForgeryToken attribute to validate the token. As the token is sent to the browser in a hidden field, it is also stored in an HttpOnly cookie.
Cross-site request forgery (CSRF) is an attack that tricks an end user into executing undesirable actions while logged into a web application. Taking advantage of the authenticated user's permissions, a CSRF attack dupes the victim into performing specific actions that benefit the attacker.
This is not the way to restrict the access by another service. The ForgeryToken helps to prevent CSRF attacks, ASP.NET MVC uses anti-forgery tokens, also called request verification tokens. The client requests an HTML page that contains a form. The server includes two tokens in the response. One token is sent as a cookie. When you submit the form, those will match on the same server.
I believe, What you need is the trust from example1.com
to api.example2.com
. An example would be stackauth which is a domain for centralized services across the entire Stack Exchange network. Then you can implement the authorization as you need in your project.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With