웹 API 및 ValidateAntiForgeryToken
웹 페이지에서 AJAX 스타일이라고하는 기존 MVC 웹 서비스가 있습니다. 이러한 서비스는 ValidateAntiForgeryToken 특성을 사용하여 요청 위조를 방지합니다.
이러한 서비스를 Web API로 마이그레이션하려고하지만 이에 상응하는 위조 방지 기능이없는 것 같습니다.
내가 뭔가를 놓치고 있습니까? Web API를 사용하여 위조 요청을 처리하는 다른 방법이 있습니까?
이러한 권한 부여 속성을 구현할 수 있습니다.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
try
{
AntiForgery.Validate();
}
catch
{
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;
}
}
그런 다음 API 작업을 장식합니다.
[ValidateAntiForgeryToken]
public HttpResponseMessage Post()
{
// some work
return Request.CreateResponse(HttpStatusCode.Accepted);
}
위 코드 FilterAttribute 보완
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
try
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (actionContext.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);
}
catch (System.Web.Mvc.HttpAntiForgeryException e)
{
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;
}
Razor를 사용하는 HTML 함수
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
Angular 사용
return $http({
method: 'POST',
url: '@Url.Content("~/api/invite/")',
data: {},
headers: {
'RequestVerificationToken': '@TokenHeaderValue()'
}
});
이 링크가 도움 이 되었으므로 면도기보기에서 위조 방지 토큰을 검색하고 토큰을 헤더로 전달할 수 있습니다.
var csrfToken = $("input[name='__RequestVerificationToken']").val();
$.ajax({
headers: { __RequestVerificationToken: csrfToken },
type: "POST",
dataType: "json",
contentType: 'application/json; charset=utf-8',
url: "/api/products",
data: JSON.stringify({ name: "Milk", price: 2.33 }),
statusCode: {
200: function () {
alert("Success!");
}
}
});
Oswaldo의 답변이지만 AuthorizeAttribute로 구현 됨
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ApiValidateAntiForgeryToken : AuthorizeAttribute
{
public static string GenerateAntiForgeryTokenForHeader() {
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
protected override bool IsAuthorized(HttpActionContext actionContext) {
var headers = actionContext.Request.Headers;
// we pass both the cookie and the form token into a single header field
string headerToken = headers.Contains("__RequestVerificationToken") ? headers.GetValues("__RequestVerificationToken").FirstOrDefault() : null;
if (headerToken == null) {
return false;
}
string[] tokens = headerToken.Split(':');
if (tokens.Length != 2) {
return false;
}
string cookieToken = tokens[0].Trim();
string formToken = tokens[1].Trim();
try {
AntiForgery.Validate(cookieToken, formToken);
}
catch {
return false;
}
return base.IsAuthorized(actionContext);
}
}
[ApiValidateAntiForgeryToken]을 사용하여 컨트롤러 또는 메서드를 장식 한 다음 RequestVerificationToken : "@ ApiValidateAntiForgeryToken.GenerateAntiForgeryTokenForHeader ()"를 면도기 자바 스크립트 코드의 메서드에 대한 헤더로 전달할 수 있습니다.
이것에 대해 좀 더 생각한 후, 위조 방지 토큰의 전체 목적을 무너 뜨리기 때문에 쿠키와 폼 토큰을 혼합하는 것은 좋지 않습니다. 양식 부분을 인증 헤더로 이동하는 동안 쿠키 부분을 쿠키로 유지하는 것이 더 낫습니다. 따라서이 새로운 답변 (다시 AuthorizeAttribute)이 있습니다.
using System;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Helpers;
using System.Web.Http;
using System.Web.Http.Controllers;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ApiValidateAntiForgeryToken : AuthorizeAttribute {
public const string HeaderName = "X-RequestVerificationToken";
private static string CookieName => AntiForgeryConfig.CookieName;
public static string GenerateAntiForgeryTokenForHeader(HttpContext httpContext) {
if (httpContext == null) {
throw new ArgumentNullException(nameof(httpContext));
}
// check that if the cookie is set to require ssl then we must be using it
if (AntiForgeryConfig.RequireSsl && !httpContext.Request.IsSecureConnection) {
throw new InvalidOperationException("Cannot generate an Anti Forgery Token for a non secure context");
}
// try to find the old cookie token
string oldCookieToken = null;
try {
var token = httpContext.Request.Cookies[CookieName];
if (!string.IsNullOrEmpty(token?.Value)) {
oldCookieToken = token.Value;
}
}
catch {
// do nothing
}
string cookieToken, formToken;
AntiForgery.GetTokens(oldCookieToken, out cookieToken, out formToken);
// set the cookie on the response if we got a new one
if (cookieToken != null) {
var cookie = new HttpCookie(CookieName, cookieToken) {
HttpOnly = true,
};
// note: don't set it directly since the default value is automatically populated from the <httpCookies> config element
if (AntiForgeryConfig.RequireSsl) {
cookie.Secure = AntiForgeryConfig.RequireSsl;
}
httpContext.Response.Cookies.Set(cookie);
}
return formToken;
}
protected override bool IsAuthorized(HttpActionContext actionContext) {
if (HttpContext.Current == null) {
// we need a context to be able to use AntiForgery
return false;
}
var headers = actionContext.Request.Headers;
var cookies = headers.GetCookies();
// check that if the cookie is set to require ssl then we must honor it
if (AntiForgeryConfig.RequireSsl && !HttpContext.Current.Request.IsSecureConnection) {
return false;
}
try {
string cookieToken = cookies.Select(c => c[CookieName]).FirstOrDefault()?.Value?.Trim(); // this throws if the cookie does not exist
string formToken = headers.GetValues(HeaderName).FirstOrDefault()?.Trim();
if (string.IsNullOrEmpty(cookieToken) || string.IsNullOrEmpty(formToken)) {
return false;
}
AntiForgery.Validate(cookieToken, formToken);
return base.IsAuthorized(actionContext);
}
catch {
return false;
}
}
}
그런 다음 [ApiValidateAntiForgeryToken]을 사용하여 컨트롤러 또는 메서드를 장식합니다.
그리고 이것을 razor 파일에 추가하여 자바 스크립트에 대한 토큰을 생성하십시오.
<script>
var antiForgeryToken = '@ApiValidateAntiForgeryToken.GenerateAntiForgeryTokenForHeader(HttpContext.Current)';
// your code here that uses such token, basically setting it as a 'X-RequestVerificationToken' header for any AJAX calls
</script>
참고 URL : https://stackoverflow.com/questions/11476883/web-api-and-validateantiforgerytoken
'programing tip' 카테고리의 다른 글
자바에서 'instanceof'피하기 (0) | 2020.11.25 |
---|---|
python argh / argparse : 목록을 명령 줄 인수로 전달하려면 어떻게해야합니까? (0) | 2020.11.25 |
HTML5가 코드 요소를 pre 안에 넣을 것을 권장하는 이유는 무엇입니까? (0) | 2020.11.25 |
.travis.yml에서 Travis의 빌드 작업 디렉토리를 어떻게 얻습니까? (0) | 2020.11.25 |
Bootstrap, Twitter Bootstrap 및 Bootstrap 3의 차이점은 무엇입니까? (0) | 2020.11.25 |