일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 쿠키
- php
- file upload
- csrf
- 웹개발
- 로그인페이지
- Error based sql injection
- Los
- 과제
- css
- Python
- 모의해킹
- XSS
- 로그인
- Cross Site Request Forgery
- MySQL
- 게시판 만들기
- JS
- union sql injection
- cookie 탈취
- sql injection point
- 세션
- cors
- lord of sql injection
- CTF
- blind sql injection
- sql injection
- lord of sqli
- Reflected Xss
- JWT
- Today
- Total
Almon Dev
게시판 만들기 #10 (게시글 내부에 이미지 추가) 본문
게시글 이미지 추가
게시글을 입력할 때 글 내부에 이미지를 추가할 수 있는 기능입니다.
사진 추가 UI 만들기
// 이미지 업로드
const uploadContainer = document.createElement('div');
uploadContainer.classList.add('upload-container');
const imgInput = document.createElement('input');
imgInput.classList.add('upload-btn');
imgInput.setAttribute('type', 'file');
imgInput.setAttribute('accept', '.jpg, .jpeg, .png, .gif');
imgInput.addEventListener('change', (e) => {
const file = e.target.files[0];
imgUpload(file);
imgInput.value = '';
});
const imgBtn = document.createElement('button');
imgBtn.classList.add('add-btn');
imgBtn.addEventListener('click', (e) => {
e.preventDefault();
imgInput.click();
});
imgBtn.innerHTML =
'<p class="add-btn-icon"><i class="fa-regular fa-image"></i></p><p class="add-btn-text">사진</p>';
게시판 만들기 #3에서 만들었던 게시글 목록을 확인하는 코드(post_list.js)에 검색창을 추가했습니다.
imgBtn.addEventListener('click', (e) => {
e.preventDefault();
imgInput.click();
});
버튼을 클릭할 때 이미지 추가를 위한 input 태그를 클릭해 줍니다.
imgInput.addEventListener('change', (e) => {
const file = e.target.files[0];
imgUpload(file);
imgInput.value = '';
});
input 태그에서 파일을 선택하면 파일을 가져와 imgUpload 함수로 서버에 업로드를 시킨 뒤에 input 태그의 value를 비워둡니다.
input 태그의 value를 ''으로 만드는 이유는 같은 사진 파일을 올려도 change 이벤트를 발생시키기 위함입니다.
ex) 글을 쓰다가 같은 사진을 여러 개 올릴 때
업로드 기능 (JS)
function imgUpload(file) {
const contentInput = document.querySelector('.content-input');
const formData = new FormData();
formData.append('image', file);
fetch('/forum/db/upload.php', {
method: 'POST',
body: formData,
})
// .then((res) => res.text())
// .then(console.log);
.then((res) => res.json())
.then((res) => {
if (res.result) {
// console.log(res.src);
const imgSrc = res.src;
const imgTag = document.createElement('img');
imgTag.setAttribute('src', '/forum/upload/image/' + imgSrc);
imgTag.classList.add('editor-img');
contentInput.appendChild(imgTag);
} else {
alert(res.msg);
}
});
}
서버로 요청을 보내 이미지 파일을 업로드하고 경로를 받아와서 이미지 태그를 생성합니다.
const contentInput = document.querySelector('.content-input');
const formData = new FormData();
formData.append('image', file);
FormData에 이미지 파일을 추가합니다.
fetch('/forum/db/upload.php', {
method: 'POST',
body: formData,
})
// .then((res) => res.text())
// .then(console.log);
서버에 업로드를 위해서 upload.php에 FormData를 포함해 post 요청을 보냅니다.
if (res.result) {
// console.log(res.src);
const imgSrc = res.src;
const imgTag = document.createElement('img');
imgTag.setAttribute('src', '/forum/upload/image/' + imgSrc);
imgTag.classList.add('editor-img');
contentInput.appendChild(imgTag);
업로드에 성공한 경우 서버의 응답으로 이미지의 이름을 받아와서 img 태그를 생성한 뒤 추가합니다.
이미지의 경로가 노출되기 때문에 파일 업로드 취약점으로 이용될 것 같습니다.
} else {
alert(res.msg);
}
실패 시 서버에서 받아온 실패 메세지를 출력합니다.
업로드 기능 (PHP)
<?php
if (isset($_FILES["image"]) && $_FILES['image']['error'] == 0) {
$file = $_FILES['image'];
$upload_dir = '../upload/image/';
// if (!is_dir($upload_dir)) {
// mkdir($upload_dir, 0755, true);
// exit;
// }
$file_name = basename($file['name']);
$path_info = pathinfo($file_name);
$file_extension = strtolower($path_info['extension']);
$extension = ['jpg', 'png', 'gif', 'jpeg'];
if (!in_array($file_extension, $extension)) {
echo json_encode(
[
'result' => false,
'msg' => "파일 확장자를 확인해 주세요. \n'jpg', 'jpeg', 'png', 'gif'만 사용이 가능합니다."
]
);
exit;
}
do {
$random_number = mt_rand(1, 1000000);
$upload_path = $upload_dir . $random_number . '.' . $file_extension;
} while (file_exists($upload_path));
if(move_uploaded_file($file['tmp_name'], $upload_path)) {
echo json_encode(['result' => true, 'src' => $random_number . '.' . $file_extension]);
} else {
echo json_encode(['result' => false, 'msg' => '파일 업로드에 실패했습니다.']);
}
}else {
echo json_encode(['result'=> false,'msg'=> '파일을 확인해 주세요.']);
}
PHP를 이용해서 서버에서 이미지를 업로드하는 코드입니다.
if (isset($_FILES["image"]) && $_FILES['image']['error'] == 0) {
image라는 이름으로 파일이 있고, 파일이 정상적으로 업로드된 경우만 if문을 실행합니다.
$_FILES에는 업로드된 파일의 정보가 담겨 있습니다. 이 중에서 error는 업로드의 상태를 담고 있습니다.
0 : 정상적으로 업로드
1 : php.ini 설정의 파일 사이즈 초과
2 : form의 파일 사이즈 초과
3 : 파일이 일부만 업로드에 성공
4 : 업로드가 되지 않음
.....
$file_name = basename($file['name']);
$path_info = pathinfo($file_name);
$file_extension = strtolower($path_info['extension']);
$extension = ['jpg', 'png', 'gif', 'jpeg'];
파일 이름과 확장자를 추출하고, 확장자 검사를 위한 배열을 생성합니다.
옛날 버전의 pathinfo의 extension을 활용한 확장자 추출의 경우 %00과 같은 NULL Byte를 이용한 우회에 취약했지만 지금은 패치를 통해 제대로 처리한다고 합니다.
if (!in_array($file_extension, $extension)) {
echo json_encode(
[
'result' => false,
'msg' => "파일 확장자를 확인해 주세요. \n'jpg', 'jpeg', 'png', 'gif'만 사용이 가능합니다."
]
);
exit;
}
확장자가 배열에 있지 않은 경우 실패 응답을 하고 exit로 코드를 종료합니다.
이미지 파일이라는 제한된 파일만 업로드하는 기능이기 때문에 화이트리스트 기반 필터링을 이용했습니다.
do {
$random_number = mt_rand(1, 1000000);
$upload_path = $upload_dir . $random_number . '.' . $file_extension;
} while (file_exists($upload_path));
랜덤 한 숫자로 파일의 이름을 설정하는 반복문입니다. 같은 이름의 파일이 없을 때까지 반복합니다.
파일 이름을 숫자로 변경하는 이유는 같은 이름의 이미지를 업로드할 경우 덮어씌워지기 때문입니다.
if(move_uploaded_file($file['tmp_name'], $upload_path)) {
echo json_encode(['result' => true, 'src' => $random_number . '.' . $file_extension]);
} else {
echo json_encode(['result' => false, 'msg' => '파일 업로드에 실패했습니다.']);
}
mov_uploaded_file 함수를 이용해서 임시 폴더에 존재하던 파일을 설정한 경로로 옮겨옵니다.
그 결과에 따라 성공과 실패 응답을 보냅니다.
마무리
스크린샷
고쳐야 할 점
게시글을 작성할 때 사진버튼을 이용해서 이미지를 추가하면 등록하기로 게시글을 등록하지 않아도 이미지가 업로드되는 문제가 있습니다.
'모의해킹 > 웹 개발' 카테고리의 다른 글
게시판 만들기 #9 (게시글 검색과 정렬) (0) | 2025.01.31 |
---|---|
게시판 만들기 #7 (아이디 비밀번호 찾기) (0) | 2025.01.22 |
게시판 만들기 #6 (게시글 삭제하기) (0) | 2024.11.12 |
게시판 만들기 #5 (게시글 수정하기) (0) | 2024.11.12 |
게시판 만들기 #4 (게시글 읽기) (0) | 2024.11.12 |