λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
μ΄μŠˆπŸ…/ν•΄κ²°μ™„λ£Œ

POST 403 (Forbidden)

by @ENFJ 2024. 3. 20.

πŸ‘‰POST 403 (Forbidden)

403 Forbidden 였λ₯˜λŠ” μš”μ²­μ΄ μ„œλ²„μ— μ˜ν•΄ κ±°λΆ€λ˜μ—ˆμŒμ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€. 이것은 주둜 κΆŒν•œμ΄ μ—†λŠ” 경우 λ°œμƒν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ κ²½μš°κ°€ λ°œμƒν•˜λŠ” μ΄μœ λŠ” μ—¬λŸ¬ 가지가 μžˆμ„ 수 μžˆμ§€λ§Œ, 주둜 λ‹€μŒκ³Ό 같은 μ΄μœ κ°€ μžˆμŠ΅λ‹ˆλ‹€:

  1. CSRF(Cross-Site Request Forgery) 토큰이 λˆ„λ½λ˜μ—ˆκ±°λ‚˜ 잘λͺ»λœ 경우: μ„œλ²„μ—μ„œ CSRF 보호λ₯Ό μœ„ν•΄ μš”μ²­ 헀더에 CSRF 토큰을 포함해야 ν•˜λŠ” κ²½μš°κ°€ μžˆμŠ΅λ‹ˆλ‹€. AJAX μš”μ²­μ„ 보낼 λ•Œ 이 토큰을 ν•¨κ»˜ λ³΄λ‚΄λŠ” 것을 ν™•μΈν•˜μ„Έμš”.
  2. μ‚¬μš©μžμ˜ κΆŒν•œμ΄ μ—†λŠ” 경우: μ„œλ²„κ°€ μš”μ²­μ„ μ²˜λ¦¬ν•  κΆŒν•œμ΄ μ—†λŠ” 경우 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. 둜그인이 ν•„μš”ν•œ μ„œλΉ„μŠ€μΈ 경우, μ‚¬μš©μžκ°€ μ˜¬λ°”λ₯΄κ²Œ μΈμ¦λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”.
  3. μ„œλ²„μ—μ„œ μš”μ²­μ„ κ±°λΆ€ν•˜λŠ” κ·œμΉ™ λ˜λŠ” 정책이 μžˆλŠ” 경우: μ„œλ²„ μΈ‘μ—μ„œ μš”μ²­μ„ κ±°λΆ€ν•˜λŠ” κ·œμΉ™ λ˜λŠ” 정책이 μžˆλŠ” 경우 403 Forbidden 였λ₯˜κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이 경우 μ„œλ²„ 둜그 λ˜λŠ” API λ¬Έμ„œλ₯Ό ν™•μΈν•˜μ—¬ 더 μžμ„Έν•œ 정보λ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

403 Forbidden 였λ₯˜κ°€ λ°œμƒν•œ 경우, λ¨Όμ € μ„œλ²„ 츑의 섀정을 ν™•μΈν•˜κ³  문제λ₯Ό ν•΄κ²°ν•΄μ•Ό ν•©λ‹ˆλ‹€. μš”μ²­μ΄ μ˜¬λ°”λ₯΄κ²Œ κ΅¬μ„±λ˜μ—ˆλŠ”μ§€, ν•„μš”ν•œ 인증이 μ œκ³΅λ˜μ—ˆλŠ”μ§€, μ„œλ²„ 츑의 λ³΄μ•ˆ 정책을 μ€€μˆ˜ν•˜λŠ”μ§€ 등을 확인해야 ν•©λ‹ˆλ‹€.

 


βœ…ν•΄κ²° 방법

<aside> πŸ’‘ API κ΅¬ν˜„ ν•  λ•Œλ§ˆλ‹€ Spring Securityλ₯Ό μ œλŒ€λ‘œ ν•˜μ§€ μ•Šκ³  CRUD κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λ‹€ λ³΄λ‹ˆ 계속 CSRF 였λ₯˜κ°€ λ°œμƒν•˜μ˜€λ‹€.

μ²˜μŒμ—λŠ” csrf 문제 인건 μ•Œκ² λŠ”λ° μ–΄λ–»κ²Œ ν•΄κ²°ν•΄μ•Ό 할지 λͺ°λΌμ„œ ν”„λ‘œμ νŠΈ 진행 속도에 영ν–₯을 끼쳀닀.

λͺ‡ 번 λ˜‘κ°™μ€ 였λ₯˜λ₯Ό 계속 κ²½ν—˜ν•˜λ‹€ λ³΄λ‹ˆ μ •ν™•ν•œ μ›λ¦¬λŠ” 아직 잘 λͺ¨λ₯΄μ§€λ§Œ μ–΄λ–»κ²Œ ν•΄μ•Ό ν•΄κ²° ν•  수 μžˆμ„μ§€λŠ” μ•Œκ²Œ λ˜μ—ˆλ‹€.

</aside>

μš°μ„  λ‚˜λŠ” html → js (ajax 톡신 _ jqueryλ₯Ό μ‚¬μš©) → controller νλ¦„μœΌλ‘œ κ°œλ°œμ„ μ§„ν–‰ν•˜μ˜€λ‹€.

1. μš°μ„  JS νŒŒμΌμ— CSRF 토큰을 κ°€μ Έμ˜¨λ‹€.

(이건 κ°œλ°œμžλ§ˆλ‹€ λ‹€λ₯΄μ§€λ§Œ λ‚˜λŠ” μ–΄μ°¨ν”Ό html μ—μ„œ js 그리고 css 연결을 지어 놓은 μƒνƒœμ΄κΈ° λ•Œλ¬Έμ— jsμ—μ„œ λΆ€λ₯΄κΈ°λ‘œ κ²°μ •ν–ˆλ‹€. * htmlμ—μ„œλ„ λΆ€λ₯Ό 수 μžˆλ‹€.)

// CSRF 토큰 κ°€μ Έμ˜€κΈ°
function getCsrfToken() {    return ***document***.querySelector('meta[name="_csrf"]').getAttribute('content');}

2. CSRF 토큰을 κ°€μ Έμ˜¨λ‹€.

var csrfToken = getCsrfToken(); // CSRF 토큰 κ°€μ Έμ˜€κΈ°

그리고 κ°€μ Έμ˜¨ 토큰을 …

3.csrf 토큰을 헀더 μ„€μ • - 보내기

beforeSend: function(xhr) { // CSRF 토큰을 헀더에 μ„€μ • xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken); },

λ₯Ό μΆ”κ°€ν•œλ‹€. μ΄λŠ” ajax λ₯Ό ν†΅ν•˜μ—¬ μ„œλ²„λ‘œ 데이터λ₯Ό μ „μ†‘ν•˜λŠ”λ° κ·Έλ•Œ csrf 토큰을 헀더에 λ„£μ–΄μ„œ 같이 보내버린닀.

κ·Έλ ‡κ²Œ ν•˜λ©΄ csrf κ΄€λ ¨ 403 였λ₯˜λ₯Ό ν•΄κ²° ν•  수 μžˆλ‹€.

 $.ajax({
        beforeSend: function(xhr) {
            // CSRF 토큰을 헀더에 μ„€μ •
            xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken);
        });
       

βœ… 전체 μ½”λ“œ.js

// CSRF 토큰 κ°€μ Έμ˜€κΈ°
function getCsrfToken() {
    return document.querySelector('meta[name="_csrf"]').getAttribute('content');
}

function deletePost(postId) {
    var csrfToken = getCsrfToken(); // CSRF 토큰 κ°€μ Έμ˜€κΈ°

$.ajax({
        type: 'DELETE',
        url: '/posts/' + postId, // μ„œλ²„μ˜ μ—”λ“œν¬μΈνŠΈ 경둜
        beforeSend: function(xhr) {
            xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken);
        },
        success: function(response) {
            alert("κ²Œμ‹œλ¬Όμ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
            // μš”μ²­μ΄ μ„±κ³΅ν•˜λ©΄ λͺ©λ‘ νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•©λ‹ˆλ‹€.
            window.location.href = "/list-posts";
        },
        error: function(error) {
            console.error('Error deleting post:', error);
            alert("κ²Œμ‹œλ¬Ό μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.");
            // ν•„μš”ν•œ 경우 였λ₯˜ 처리 λ™μž‘ μˆ˜ν–‰
        }
    });