Swagger에 광주은행 OAuth 2.0 API 문서 업데이트 및 XSS 방지 처리 추가
This commit is contained in:
@@ -415,18 +415,4 @@ var jsonlint = (function(){
|
|||||||
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
|
||||||
exports.parser = jsonlint;
|
exports.parser = jsonlint;
|
||||||
exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); }
|
exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); }
|
||||||
exports.main = function commonjsMain(args) {
|
|
||||||
if (!args[1])
|
|
||||||
throw new Error('Usage: '+args[0]+' FILE');
|
|
||||||
if (typeof process !== 'undefined') {
|
|
||||||
var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
|
|
||||||
} else {
|
|
||||||
var cwd = require("file").path(require("file").cwd());
|
|
||||||
var source = cwd.join(args[1]).read({charset: "utf-8"});
|
|
||||||
}
|
|
||||||
return exports.parser.parse(source);
|
|
||||||
}
|
|
||||||
if (typeof module !== 'undefined' && require.main === module) {
|
|
||||||
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,48 @@
|
|||||||
const customPopups = {
|
const customPopups = {
|
||||||
|
/**
|
||||||
|
* HTML 새니타이저 - 허용된 태그만 통과시키고 나머지는 제거
|
||||||
|
* XSS 방지를 위해 script, event handler 등 위험 요소를 제거합니다.
|
||||||
|
* @param {string} input - 새니타이즈할 문자열
|
||||||
|
* @returns {string} 새니타이즈된 HTML 문자열
|
||||||
|
*/
|
||||||
|
_sanitizeHtml: function (input) {
|
||||||
|
if (!input) return '';
|
||||||
|
var allowedTags = ['br', 'strong', 'b', 'em', 'i', 'span', 'p'];
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.innerHTML = input;
|
||||||
|
|
||||||
|
// script, style, iframe, object, embed, form 등 위험 요소 제거
|
||||||
|
var dangerousTags = div.querySelectorAll('script, style, iframe, object, embed, form, link, meta, base');
|
||||||
|
for (var i = dangerousTags.length - 1; i >= 0; i--) {
|
||||||
|
dangerousTags[i].parentNode.removeChild(dangerousTags[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 허용되지 않은 태그는 텍스트 노드로 대체
|
||||||
|
var allElements = div.querySelectorAll('*');
|
||||||
|
for (var j = allElements.length - 1; j >= 0; j--) {
|
||||||
|
var el = allElements[j];
|
||||||
|
if (allowedTags.indexOf(el.tagName.toLowerCase()) === -1) {
|
||||||
|
var textNode = document.createTextNode(el.textContent);
|
||||||
|
el.parentNode.replaceChild(textNode, el);
|
||||||
|
} else {
|
||||||
|
// 허용된 태그라도 모든 속성 제거 (on* 이벤트 핸들러 등 방지)
|
||||||
|
while (el.attributes.length > 0) {
|
||||||
|
el.removeAttribute(el.attributes[0].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return div.innerHTML;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 알림 팝업 표시
|
* 알림 팝업 표시
|
||||||
* @param {string} message - 알림 메시지
|
* @param {string} message - 알림 메시지
|
||||||
* @param {Function} callback - 확인 버튼 클릭 시 호출되는 콜백 (선택사항)
|
* @param {Function} callback - 확인 버튼 클릭 시 호출되는 콜백 (선택사항)
|
||||||
*/
|
*/
|
||||||
showAlert: function (message, callback) {
|
showAlert: function (message, callback) {
|
||||||
// 메시지 설정
|
// 메시지 설정 (XSS 방지를 위해 새니타이즈)
|
||||||
$('#customAlertMessage').html(message);
|
$('#customAlertMessage').html(customPopups._sanitizeHtml(message));
|
||||||
|
|
||||||
// 팝업 표시 (modal 구조 사용)
|
// 팝업 표시 (modal 구조 사용)
|
||||||
$('#customAlert').show();
|
$('#customAlert').show();
|
||||||
@@ -100,9 +136,9 @@ const customPopups = {
|
|||||||
const onConfirm = options.onConfirm;
|
const onConfirm = options.onConfirm;
|
||||||
const onCancel = options.onCancel;
|
const onCancel = options.onCancel;
|
||||||
|
|
||||||
// 팝업 내용 설정
|
// 팝업 내용 설정 (XSS 방지를 위해 새니타이즈)
|
||||||
$('#passwordPopupTitle').text(title);
|
$('#passwordPopupTitle').text(title);
|
||||||
$('#passwordPopupMessage').html(message);
|
$('#passwordPopupMessage').html(customPopups._sanitizeHtml(message));
|
||||||
|
|
||||||
// 입력 필드 및 에러 초기화
|
// 입력 필드 및 에러 초기화
|
||||||
$('#passwordPopupInput').val('').removeClass('error');
|
$('#passwordPopupInput').val('').removeClass('error');
|
||||||
@@ -215,8 +251,8 @@ const customPopups = {
|
|||||||
* @param {Function} callback - 버튼 클릭 시 호출되는 콜백 (파라미터: boolean - true는 확인, false는 취소)
|
* @param {Function} callback - 버튼 클릭 시 호출되는 콜백 (파라미터: boolean - true는 확인, false는 취소)
|
||||||
*/
|
*/
|
||||||
showConfirm: function (message, callback) {
|
showConfirm: function (message, callback) {
|
||||||
// 메시지 설정
|
// 메시지 설정 (XSS 방지를 위해 새니타이즈)
|
||||||
$('#customConfirmMessage').html(message);
|
$('#customConfirmMessage').html(customPopups._sanitizeHtml(message));
|
||||||
|
|
||||||
// 팝업 표시 (modal 구조 사용)
|
// 팝업 표시 (modal 구조 사용)
|
||||||
$('#customConfirm').show();
|
$('#customConfirm').show();
|
||||||
@@ -302,7 +338,7 @@ const customPopups = {
|
|||||||
$(document).off('keydown.customConfirmEsc');
|
$(document).off('keydown.customConfirmEsc');
|
||||||
},
|
},
|
||||||
showEmailValidationPopup: function (message, onConvertCallback, onChangeEmailCallback) {
|
showEmailValidationPopup: function (message, onConvertCallback, onChangeEmailCallback) {
|
||||||
$('#emailValidationPopupMessage').html(message);
|
$('#emailValidationPopupMessage').html(customPopups._sanitizeHtml(message));
|
||||||
$('#emailValidationPopup').css('display', 'flex');
|
$('#emailValidationPopup').css('display', 'flex');
|
||||||
|
|
||||||
$('#emailConvertButton').one('click', function () {
|
$('#emailConvertButton').one('click', function () {
|
||||||
@@ -320,7 +356,7 @@ const customPopups = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
showEmailResendPopup: function (message, loginId) {
|
showEmailResendPopup: function (message, loginId) {
|
||||||
$('#emailResendMessage').html(message);
|
$('#emailResendMessage').html(customPopups._sanitizeHtml(message));
|
||||||
$('#userLoginId').val(loginId);
|
$('#userLoginId').val(loginId);
|
||||||
$('#emailResend').show();
|
$('#emailResend').show();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,15 +1,40 @@
|
|||||||
{
|
{
|
||||||
"openapi": "3.0.0",
|
"openapi": "3.0.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "케이뱅크 OAuth 2.0 토큰 발급",
|
"title": "광주은행 OAuth 2.0 토큰 발급",
|
||||||
"description": "OAuth 2.0 토큰 발급을 위한 API 문서입니다.",
|
"description": "OAuth 2.0 토큰 발급을 위한 API 문서입니다.",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
},
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"clientCredentials": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": {
|
||||||
|
"securitySchemes": {
|
||||||
|
"clientCredentials": {
|
||||||
|
"type": "oauth2",
|
||||||
|
"flows": {
|
||||||
|
"clientCredentials": {
|
||||||
|
"tokenUrl": "/api/v1/oauth/token",
|
||||||
|
"scopes": {
|
||||||
|
"api": "API 접근 권한"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"paths": {
|
"paths": {
|
||||||
"/api/v1/oauth/token": {
|
"/api/v1/oauth/token": {
|
||||||
"post": {
|
"post": {
|
||||||
"summary": "케이뱅크 액세스 토큰 발급",
|
"summary": "광주은행 액세스 토큰 발급",
|
||||||
"description": "client_credentials grant type을 사용하여 액세스 토큰을 발급받습니다.",
|
"description": "client_credentials grant type을 사용하여 액세스 토큰을 발급받습니다.",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"clientCredentials": ["api"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
"required": true,
|
"required": true,
|
||||||
"content": {
|
"content": {
|
||||||
@@ -26,12 +51,12 @@
|
|||||||
"client_id": {
|
"client_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"maxLength": 256,
|
"maxLength": 256,
|
||||||
"description": "케이뱅크에서 발급한 client id"
|
"description": "광주은행에서 발급한 client id"
|
||||||
},
|
},
|
||||||
"client_secret": {
|
"client_secret": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"maxLength": 512,
|
"maxLength": 512,
|
||||||
"description": "케이뱅크에서 발급한 client secret"
|
"description": "광주은행에서 발급한 client secret"
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -183,6 +183,8 @@
|
|||||||
|
|
||||||
// 테스트 환경 안내 메시지 표시
|
// 테스트 환경 안내 메시지 표시
|
||||||
function displayTestAuthNotice(authNumber) {
|
function displayTestAuthNotice(authNumber) {
|
||||||
|
// XSS 방지를 위해 authNumber를 이스케이프 처리
|
||||||
|
const safeAuthNumber = String(authNumber).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||||
const noticeHtml = `
|
const noticeHtml = `
|
||||||
<div class="test-env-notice" style="margin-top: 12px; padding: 12px 16px; background-color: #FFF4E6; border: 1px solid #FFB84D; border-radius: 4px;">
|
<div class="test-env-notice" style="margin-top: 12px; padding: 12px 16px; background-color: #FFF4E6; border: 1px solid #FFB84D; border-radius: 4px;">
|
||||||
<div class="test-env-notice__content" style="display: flex; align-items: flex-start; gap: 8px;">
|
<div class="test-env-notice__content" style="display: flex; align-items: flex-start; gap: 8px;">
|
||||||
@@ -193,7 +195,7 @@
|
|||||||
<p style="margin: 0; font-size: 14px; color: #E65100; font-weight: 500;">테스트 환경 안내</p>
|
<p style="margin: 0; font-size: 14px; color: #E65100; font-weight: 500;">테스트 환경 안내</p>
|
||||||
<p style="margin: 4px 0 0 0; font-size: 13px; color: #5D4037; line-height: 1.5;">
|
<p style="margin: 4px 0 0 0; font-size: 13px; color: #5D4037; line-height: 1.5;">
|
||||||
현재 테스트 환경입니다. 실제로 SMS가 발송되지 않으며, 인증번호는
|
현재 테스트 환경입니다. 실제로 SMS가 발송되지 않으며, 인증번호는
|
||||||
<strong style="color: #E65100; font-family: monospace; font-size: 14px; font-weight: bold;">${authNumber}</strong>
|
<strong style="color: #E65100; font-family: monospace; font-size: 14px; font-weight: bold;">${safeAuthNumber}</strong>
|
||||||
입니다.
|
입니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user