jQuery Ajax 호출 및 HTML.위조 방지토큰()
인터넷상의 블로그 투고에서 읽은 정보에 따라 CSRF 공격의 완화를 앱에 실장했습니다.특히, 이 투고는, 제 도입의 원동력이 되고 있습니다.
- ASP의 베스트 프랙티스ASP로부터의 NET MVCNET 및 웹 툴 개발자 콘텐츠 팀
- Phil Hack 블로그의 사이트 간 요청 위조 공격 구조
- 위조 방지ASP 토큰NET MVC 프레임워크 - Html.위조 방지토큰 및 검증위조 방지David Hayden 블로그의 토큰 속성
기본적으로 CSRF 공격을 방지하기 위해서는 다음 코드를 구현해야 한다고 기술되어 있습니다.
, 그럼 여기에다가 더해져요.
[ValidateAntiForgeryToken]
Http Http를[Http Post] [검증]위조 방지토큰] 퍼블릭액션 결과 SomeAction(SomeModel 모델) { }
, 그럼 여기에다가 더해져요.
<%= Html.AntiForgeryToken() %>
to .는 helper inside forms.
어쨌든, 앱의 일부에서는, jQuery로 서버에 Ajax POST를 전혀 폼을 만들지 않고 실행하고 있습니다.예를 들어 사용자가 특정 작업을 수행하기 위해 이미지를 클릭할 수 있도록 하는 경우 등이 있습니다.
활동 목록이 있는 테이블이 있다고 가정해 봅시다.테이블의 컬럼에 "Mark activity as completed"라고 표시된 이미지가 있으며 사용자가 해당 액티비티를 클릭하면 다음 예시와 같이 Ajax POST를 수행합니다.
$("a.markAsDone").click(function (event) {
event.preventDefault();
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: {},
success: function (response) {
// ....
}
});
});
어떻게 하면<%= Html.AntiForgeryToken() %>
? ?도우미 콜을 Ajax 콜의 데이터 파라미터에 포함시켜야 합니까?
투고가 길어서 죄송합니다.도와주셔서 감사합니다.
편집:
jayrdub 답변에 따라 다음과 같이 사용하였습니다.
$("a.markAsDone").click(function (event) {
event.preventDefault();
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: {
AddAntiForgeryToken({}),
id: parseInt($(this).attr("title"))
},
success: function (response) {
// ....
}
});
});
이렇게 간단한 js 함수를 사용합니다.
AddAntiForgeryToken = function(data) {
data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
return data;
};
페이지 상의 모든 폼은 토큰의 값이 같기 때문에 맨 위 마스터 페이지에 이와 같은 것을 넣습니다.
<%-- used for ajax in AddAntiForgeryToken() --%>
<form id="__AjaxAntiForgeryForm" action="#" method="post"><%= Html.AntiForgeryToken()%></form>
다음으로 ajax call do (두 번째 예시와 일치하도록 편집)
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
success: function (response) {
// ....
}
});
360 Airwalk에서 제공하는 솔루션은 마음에 들지만 조금 개선될 수 있습니다.
은 ''를 때$.post()
데이터일 jQuery를 .Content-Type
header 이 경우 ASP.header('ASP')를합니다.NET MVC는 NET MVC를 사용합니다.따라서 헤더가 항상 존재하는지 확인해야 합니다.
또 다른 개선사항은 POST, PUT, DELETE 등의 콘텐츠를 가진 모든 HTTP 동사를 지원하는 것입니다.어플리케이션에서는 POST만 사용할 수 있지만 범용 솔루션을 사용하여 수신하는 모든 데이터에 위조 방지 토큰이 있는지 확인하는 것이 좋습니다.
$(document).ready(function () {
var securityToken = $('[name=__RequestVerificationToken]').val();
$(document).ajaxSend(function (event, request, opt) {
if (opt.hasContent && securityToken) { // handle all verbs with content
var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam;
// ensure Content-Type header is present!
if (opt.contentType !== false || event.contentType) {
request.setRequestHeader( "Content-Type", opt.contentType);
}
}
});
});
그 밖에도 많은 답변이 있다는 것을 알지만, 이 기사는 훌륭하고 간결하며, 일부뿐 아니라 모든 Http Post를 확인해야 합니다.
http://richiban.wordpress.com/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/
폼 컬렉션을 수정하는 대신 HTTP 헤더를 사용합니다.
서버
//make sure to add this to your global action filters
[AttributeUsage(AttributeTargets.Class)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
public override void OnAuthorization( AuthorizationContext filterContext )
{
var request = filterContext.HttpContext.Request;
// Only validate POSTs
if (request.HttpMethod == WebRequestMethods.Http.Post)
{
// Ajax POSTs and normal form posts have to be treated differently when it comes
// to validating the AntiForgeryToken
if (request.IsAjaxRequest())
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null
? antiForgeryCookie.Value
: null;
AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
}
else
{
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
}
}
}
}
고객
var token = $('[name=__RequestVerificationToken]').val();
var headers = {};
headers["__RequestVerificationToken"] = token;
$.ajax({
type: 'POST',
url: '/Home/Ajax',
cache: false,
headers: headers,
contentType: 'application/json; charset=utf-8',
data: { title: "This is my title", contents: "These are my contents" },
success: function () {
...
},
error: function () {
...
}
});
여기서는 고급 네크로맨서처럼 느껴지지만, 이것은 4년 후에도 MVC5에서 여전히 이슈가 되고 있습니다.
Ajax 요구를 올바르게 처리하려면 Ajax 콜 시 위조 방지 토큰을 서버에 전달해야 합니다.포스트 데이터나 모델에 통합하는 것은 복잡하고 불필요합니다.토큰을 커스텀헤더로 추가하는 것은 깔끔하고 재사용할 수 있습니다.또한 매번 토큰을 추가하는 것을 잊지 않도록 설정할 수 있습니다.
예외는 있습니다.Ajax 콜에 대해 방해받지 않는Ajax는 특별한 처리가 필요하지 않습니다.토큰은 일반 숨김 입력 필드에서 정상적으로 전달됩니다.통상의 POST와 똑같습니다.
_Layout.cshtml
_layout.cshtml에는 이 JavaScript 블록이 있습니다.토큰을 DOM에 쓰는 것이 아니라 jQuery를 사용하여 MVC Helper가 생성하는 숨겨진 입력 리터럴에서 추출합니다.헤더 이름인 Magic 문자열은 속성 클래스에서 상수로 정의됩니다.
<script type="text/javascript">
$(document).ready(function () {
var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
//http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative
$.ajaxSetup({
beforeSend: function (xhr) {
if (!isAbsoluteURI.test(this.url)) {
//only add header to relative URLs
xhr.setRequestHeader(
'@.ValidateAntiForgeryTokenOnAllPosts.HTTP_HEADER_NAME',
$('@Html.AntiForgeryToken()').val()
);
}
}
});
});
</script>
beforeSend 함수의 작은 따옴표 사용에 유의하십시오.- 렌더링되는 입력 요소는 JavaScript 리터럴을 깨는 큰 따옴표를 사용합니다.
클라이언트 JavaScript
이렇게 하면 위의 beforeSend 함수가 호출되고 AntiForgy가 실행됩니다.토큰은 요청 헤더에 자동으로 추가됩니다.
$.ajax({
type: "POST",
url: "CSRFProtectedMethod",
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
//victory
}
});
서버 라이브러리
비표준 토큰을 처리하려면 사용자 지정 특성이 필요합니다.이것은 @viggity의 솔루션을 기반으로 하지만 방해받지 않는 아약스를 올바르게 처리합니다.이 코드는 공통 라이브러리에 저장할 수 있습니다.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
public const string HTTP_HEADER_NAME = "x-RequestVerificationToken";
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
// Only validate POSTs
if (request.HttpMethod == WebRequestMethods.Http.Post)
{
var headerTokenValue = request.Headers[HTTP_HEADER_NAME];
// Ajax POSTs using jquery have a header set that defines the token.
// However using unobtrusive ajax the token is still submitted normally in the form.
// if the header is present then use it, else fall back to processing the form like normal
if (headerTokenValue != null)
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null
? antiForgeryCookie.Value
: null;
AntiForgery.Validate(cookieValue, headerTokenValue);
}
else
{
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
}
}
}
}
서버/컨트롤러
이제 Atribute를 Action에 적용하기만 하면 됩니다.컨트롤러에 Atribute를 적용하면 모든 요구가 검증됩니다.
[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
return Json(true, JsonRequestBehavior.DenyGet);
}
HTML을 사용하지 마십시오.위조 방지토큰 대신 위조 방지 기능을 사용하십시오.겟토큰과 위조방지.ASP에서의 CSRF(Cross-Site Request Formature) 공격 방지 설명에 따라 Web API에서 검증합니다.NET MVC 어플리케이션
당신이 해야 할 일은 '_RequestVerification'이Token" 입력이 POST 요청에 포함됩니다.나머지 절반의 정보(사용자 쿠키의 토큰)는 이미 AJAX POST 요청과 함께 자동으로 전송됩니다.
예.,
$("a.markAsDone").click(function (event) {
event.preventDefault();
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: {
"__RequestVerificationToken":
$("input[name=__RequestVerificationToken]").val()
},
success: function (response) {
// ....
}
});
});
저는 지금 하고 있는 프로젝트에서 이 실제 문제를 구현하고 있었습니다.인증된 사용자를 필요로 하는 모든 ajax-POST에 대해 실행했습니다.
우선, 나는 너무 자주 같은 말을 반복하지 않기 위해 jquery ajax 전화를 걸기로 결정했다.이 javascript 스니펫은 모든 Ajax(포스트) 콜이 내 요청 검증 토큰을 요청에 추가하도록 보장합니다.주의: 이름 __RequestVerification토큰은 에 의해 사용됩니다.다음과 같이 표준 Anti-CSRF 기능을 활용할 수 있는 네트워크 프레임워크입니다.
$(document).ready(function () {
var securityToken = $('[name=__RequestVerificationToken]').val();
$('body').bind('ajaxSend', function (elm, xhr, s) {
if (s.type == 'POST' && typeof securityToken != 'undefined') {
if (s.data.length > 0) {
s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
else {
s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
}
});
});
위의 javascript에서 토큰을 사용할 수 있어야 하는 보기에서 공통 HTML-Helper를 사용하십시오.기본적으로 이 코드를 원하는 위치에 추가할 수 있습니다.나는 그것을 if(요청) 안에 두었다.IsAuthenticated) 스테이트먼트:
@Html.AntiForgeryToken() // you can provide a string as salt when needed which needs to match the one on the controller
컨트롤러에서는 표준 ASP를 사용하기만 하면 됩니다.Net MVC Anti-CSRF 메커니즘(실제로 Salt를 사용했지만) 이렇게 했습니다.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public JsonResult SomeMethod(string param)
{
// do something
return Json(true);
}
Firebug 등의 툴을 사용하면 POST 요구에 __RequestVerification이 어떻게 설정되어 있는지 쉽게 알 수 있습니다.토큰 파라미터가 추가되었습니다.
이것도 할 수 있습니다.
$("a.markAsDone").click(function (event) {
event.preventDefault();
$.ajax({
type: "post",
dataType: "html",
url: $(this).attr("rel"),
data: $('<form>@Html.AntiForgeryToken()</form>').serialize(),
success: function (response) {
// ....
}
});
});
는 '어느 정도 쓸 수 있다'를 쓰는 예요.Razor
,를 사용하고 WebForms
은 " " "와 할 수 있습니다.<%= %>
표시
그 과정에서 저를 도와준 @JBall의 답변에 대한 저의 코멘트와 더불어, 이것이 저에게 맞는 최종 답변입니다.MVC와 Razor를 사용하고 있으며, jQuery AJAX를 사용하여 폼을 제출하고 있습니다.새로운 결과로 부분 뷰를 갱신할 수 있도록 하기 위해 완전한 포스트백(및 페이지 플리커)을 하고 싶지 않았습니다.
, 그럼 여기에다가 더해져요.@Html.AntiForgeryToken()
평소처럼 폼 안에.
AJAX 송신 버튼 코드(온클릭 이벤트)는 다음과 같습니다.
//User clicks the SUBMIT button
$("#btnSubmit").click(function (event) {
//prevent this button submitting the form as we will do that via AJAX
event.preventDefault();
//Validate the form first
if (!$('#searchForm').validate().form()) {
alert("Please correct the errors");
return false;
}
//Get the entire form's data - including the antiforgerytoken
var allFormData = $("#searchForm").serialize();
// The actual POST can now take place with a validated form
$.ajax({
type: "POST",
async: false,
url: "/Home/SearchAjax",
data: allFormData,
dataType: "html",
success: function (data) {
$('#gridView').html(data);
$('#TestGrid').jqGrid('setGridParam', { url: '@Url.Action("GetDetails", "Home", Model)', datatype: "json", page: 1 }).trigger('reloadGrid');
}
});
MvcJqGrid를 포함하는 부분 보기가 어떻게 업데이트되고 있고 어떻게 새로고침되는지 보여 주는 "성공" 액션은 에 남겨두었습니다(매우 강력한 jqGrid 그리드이며, 이는 이를 위한 훌륭한 MVC 래퍼입니다.
컨트롤러 방식은 다음과 같습니다.
//Ajax SUBMIT method
[ValidateAntiForgeryToken]
public ActionResult SearchAjax(EstateOutlet_D model)
{
return View("_Grid", model);
}
모든 폼의 데이터를 모델로서 POST 하는 것을 좋아하지 않는 것은 인정합니다만, 필요한 경우는, 이것도 하나의 방법입니다.MVC는 데이터 바인딩을 너무 쉽게 하기 때문에 16개의 개별 값(또는 약한 타입의 Form Collection)을 제출하는 것이 아니라 이 방법은 괜찮다고 생각합니다.만약 당신이 더 잘 알고 있다면, 저는 강력한 MVC C# 코드를 제작하고 싶기 때문에 알려주시기 바랍니다.
https://gist.github.com/scottrippey/3428114에서 $.199 콜마다 요청을 수정하고 토큰을 추가할 수 있는 매우 현명한 아이디어를 발견했습니다.
// Setup CSRF safety for AJAX:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
if (options.type.toUpperCase() === "POST") {
// We need to add the verificationToken to all POSTs
var token = $("input[name^=__RequestVerificationToken]").first();
if (!token.length) return;
var tokenName = token.attr("name");
// If the data is JSON, then we need to put the token in the QueryString:
if (options.contentType.indexOf('application/json') === 0) {
// Add the token to the URL, because we can't add it to the JSON data:
options.url += ((options.url.indexOf("?") === -1) ? "?" : "&") + token.serialize();
} else if (typeof options.data === 'string' && options.data.indexOf(tokenName) === -1) {
// Append to the data string:
options.data += (options.data ? "&" : "") + token.serialize();
}
}
});
1. 서버에서 토큰을 가져오는 기능의 정의
@function
{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
2. 서버로 전송하기 전에 토큰을 가져와 헤더를 설정합니다.
var token = '@TokenHeaderValue()';
$http({
method: "POST",
url: './MainBackend/MessageDelete',
data: dataSend,
headers: {
'RequestVerificationToken': token
}
}).success(function (data) {
alert(data)
});
3. Post/get 처리 메서드의 HttpRequestBase 온서버 검증
string cookieToken = "";
string formToken = "";
string[] tokens = Request.Headers["RequestVerificationToken"].Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
AntiForgery.Validate(cookieToken, formToken);
먼저 @Html을 사용합니다.위조 방지html의 토큰()
$.ajax({
url: "@Url.Action("SomeMethod", "SomeController")",
type: 'POST',
data: JSON.stringify(jsonObject),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
async: false,
beforeSend: function (request) {
request.setRequestHeader("RequestVerificationToken", $("[name='__RequestVerificationToken']").val());
},
success: function (msg) {
alert(msg);
}
이 질문이 올라온 지 꽤 지났지만 위조 방지 사용에 대해 설명하는 유용한 자료를 찾았습니다.토큰을 사용하여 번거로움을 덜 수 있습니다.또한 AJAX 호출에 위조 방지 토큰을 쉽게 포함하기 위한 jquery 플러그인도 제공합니다.
ASP용 위조 방지 요청 레시피NET MVC 및 AJAX
내가 기여하는 건 별로 없지만, 누군가 유용하다고 생각할지도 몰라.
여기 내가 본 것 중 가장 쉬운 방법이 있다.주의: "@Html"이 있는지 확인합니다.위조 방지[ Token ( ) ]를 표시합니다.
$("a.markAsDone").click(function (event) {
event.preventDefault();
var sToken = document.getElementsByName("__RequestVerificationToken")[0].value;
$.ajax({
url: $(this).attr("rel"),
type: "POST",
contentType: "application/x-www-form-urlencoded",
data: { '__RequestVerificationToken': sToken, 'id': parseInt($(this).attr("title")) }
})
.done(function (data) {
//Process MVC Data here
})
.fail(function (jqXHR, textStatus, errorThrown) {
//Process Failure here
});
});
360Airwalk 솔루션의 약간의 개선.이것은 javascript 함수에 위조방지 토큰을 포함하기 때문에 @Html.위조 방지Token()은 모든 뷰에 포함할 필요가 없습니다.
$(document).ready(function () {
var securityToken = $('@Html.AntiForgeryToken()').attr('value');
$('body').bind('ajaxSend', function (elm, xhr, s) {
if (s.type == 'POST' && typeof securityToken != 'undefined') {
if (s.data.length > 0) {
s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
else {
s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
}
}
});
});
function DeletePersonel(id) {
var data = new FormData();
data.append("__RequestVerificationToken", "@HtmlHelper.GetAntiForgeryToken()");
$.ajax({
type: 'POST',
url: '/Personel/Delete/' + id,
data: data,
cache: false,
processData: false,
contentType: false,
success: function (result) {
}
});
}
public static class HtmlHelper {
public static string GetAntiForgeryToken() {
System.Text.RegularExpressions.Match value =
System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(),
"(?:value=\")(.*)(?:\")");
if (value.Success) {
return value.Groups[1].Value;
}
return "";
}
}
삭제 메서드를 실행하기 위해 ajax 포스트를 사용하고 있습니다(visjs 타임라인에서 가져온 것이지만 그것은 관련이 없습니다).이건 내 여동생이야
이것은 Index.cshtml 입니다.
@Scripts.Render("~/bundles/schedule")
@Styles.Render("~/bundles/visjs")
@Html.AntiForgeryToken()
<!-- div to attach schedule to -->
<div id='schedule'></div>
<!-- div to attach popups to -->
<div id='dialog-popup'></div>
여기에 덧붙인 건@Html.AntiForgeryToken()
토큰을 페이지에 표시시키다
그리고 나서 나는 아약스 포스트에서 다음과 같이 썼다.
$.ajax(
{
type: 'POST',
url: '/ScheduleWorks/Delete/' + item.id,
data: {
'__RequestVerificationToken':
$("input[name='__RequestVerificationToken']").val()
}
}
);
그러면 페이지에서 삭제한 토큰 값이 게시된 필드에 추가됩니다.
이전에 헤더에 값을 입력하려고 했지만 동일한 오류가 발생했습니다.
개선 사항을 자유롭게 게시하십시오.이것은 확실히 내가 이해할 수 있는 간단한 접근법인 것 같다.
네, 여기 많은 게시물이 있습니다.그들 중 어느 것도 도움이 되지 않았습니다.구글을 며칠이고 몇 일이고 사용할 수 없습니다.그리고 앱 전체를 처음부터 다시 작성해야 할 지경에 이르렀습니다.그리고 웹에 작은 덩어리가 있는 것을 알게 되었습니다.confg
<httpCookies requireSSL="false" domain="*.localLookup.net"/>
왜 추가했는지 이유를 알 수 없습니다.실가동 모드가 아닌 디버깅 모드에서는 무시됩니다(IE Installed to IIS Something).
솔루션에는 2가지 옵션 중 하나가 있었습니다.왜 추가했는지 기억이 나지 않기 때문에 다른 것에 의존하지 않는지 확신할 수 없습니다.두 번째로 도메인 이름은 모두 소문자여야 하며 *.localLookup.net에서 했던 것과 달리 TLD여야 합니다.
도움이 안 될 수도 있어요그게 누군가에게 도움이 됐으면 좋겠다.
제가 찾은 해결책은 ASPX가 아니라 Razor용이지만 상당히 이해하기 쉬운 문제입니다.
요청에 AntiForgy를 추가하여 해결했습니다.HTML 도우미는 콜을 사용하여 HTML ID를 생성하지 않습니다.
@Html.AntiForgeryToken()
포스트 리퀘스트에 토큰을 추가하기 위해 숨겨진 필드에 jquery를 사용하여 AntiForgy ID를 추가했습니다.
$("input[name*='__RequestVerificationToken']").attr('id', '__AjaxAntiForgeryForm');
이로 인해 컨트롤러는 [Validate]를 사용하여 요청을 수락했습니다.위조 방지토큰]아트리뷰트
위조 방지토큰은 여전히 골칫거리입니다. 위의 예들 중 어느 것도 제게는 한마디로 통하지 않습니다.너무 많아.그래서 다 합쳐봤어요.@Html이 필요합니다.위조 방지Iirc에 걸려있는 형태의 토큰
다음과 같이 해결:
function Forgizzle(eggs) {
eggs.__RequestVerificationToken = $($("input[name=__RequestVerificationToken]")[0]).val();
return eggs;
}
$.ajax({
url: url,
type: 'post',
data: Forgizzle({ id: id, sweets: milkway }),
});
확실하지 않은 경우 $ 기호를 추가합니다.
언급URL : https://stackoverflow.com/questions/4074199/jquery-ajax-calls-and-the-html-antiforgerytoken
'programing' 카테고리의 다른 글
React의 useState 훅을 사용할 때 nullable 상태를 입력하는 올바른 방법 (0) | 2023.03.31 |
---|---|
Spring Boot - Bean Definition Override예외:잘못된 콩 정의 (0) | 2023.03.31 |
Python Flask, TypeError: 'dict' 개체를 호출할 수 없습니다. (0) | 2023.03.31 |
Woocommerce 업데이트 카트 버튼 작업 후 실행 중인 후크 (0) | 2023.03.26 |
woocommerce 사이트의 제품 목록에 있는 빠른 편집 옵션에 사용자 지정 제품 필드 추가 (0) | 2023.03.26 |