2008.07.22 15:46

Session_OnEnd 이벤트는 버그 라이프(?)

Session_OnEnd 이벤트에서 Response.Redirect나, Server.MapPath 등의 메쏘드는 동작하지 않습니다. 사실 Session_OnEnd 이벤트는 Request, Response,Server  객체를 지원하지 않죠. 오직 애플리케이션, 세션 객체만을 지원합니다. 따라서 Server.MapPath()를 사용해 파일 경로를 얻어와 Session_OnEnd 이벤트에서 어떤 처리를 해줄 경우에는 반드시 세션이 끝나기 전에 파일의 경로를 얻어와 애플리케이션변수에 저장해 두는 것이 좋습니다. 그렇지 않고 Session_OnEnd 에서 Server.MapPath를 통해 얻어온 파일의 경로로 작업을 한다면 그 결과를 장담할 수가 없죠.

상당히 엄청난(?) 버그인데요. Session_OnEnd 이벤트는 사실 그 이벤트가 100% 발생할 것이라는 것을 믿을 수가 없습니다. 이 버그는 아직까지 해결되지 않은 알려진 버그중에 하나로서 가능하면 Session_OnEnd에서는 중요한 처리를 하지 말기를 추천합니다.

Global.asa 사용시 주의할 점

- 웹애플리케이션의 루트에 존재해야 한다.
- Global.asa 파일 이름을 바꿔 사용할 수 없다.
- 파일 수정시에는 반드시 웹서버를 재시작 해야한다.
- ASP 내장 객체를 모두 사용할 수 있는 것은 아니다.

Global.asa 이벤트는 ASP 페이지가 요청될 경우 호출

사용자가 사이트에 접속해 htm 페이지를 요청해도 Global.asa의 Session_OnStart 이벤트는 동작하지 않습니다. 그러므로 Session_OnStart에서의 리다이렉트(Redirect)를 통한 페이지 이동은 사이트에 처음 접속할 경우 반드시 발생하는 것은 아닙니다. 사이트 내에서 처음 ASP 페이지를 호출할 경우에 발생하게 됩니다.

예를 들면, 사이트의 기본 문서가 default.htm으로 지정돼 있고, 우리가 Session_OnStart 이벤트에 리다이렉트 'main.htm'을 지정했다고 가정하죠. 사용자가 http://www.A.com으 로 여러분의 사이트를 접속할 경우 여러분은 자동으로 main.htm 페이지가 로딩될 것이라고 생각할 수 있지만, 사실은 그렇지 않습니다. 사이트에 처음 입장하더라도 htm 페이지가 요구되면 Global.asa는 동작하지 않으며 default.htm이 사용자에게 로딩됩니다. 만일, 여러분이 default.asp 페이지를 기본문서로 지정한다면 그 때에는 제대로 동작하겠죠.

Posted by 부니기
2008.06.05 00:17

서버측에서 실행되지 않도 강제로 다운로드 되도록 하는 방법을 소개한다.

Response.Expires = 0
Response.Buffer =  True
Response.Clear

On Error Resume Next

filename = Request("filename")
filepath = Server.MapPath(".") & "\Upload\" & filename

Response.ContentType =  "application/unknown"
Response.CacheControl = "public"
Response.AddHeader "Content-Disposition", "attachment;filename=" & filename

'스트림 이용
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Open
objStream.Type = 1
objStream.LoadFromFile filepath
strFile = objStream.Read
Response.BinaryWrite strFile
Response.Flush
Set objStream = Nothing

'SiteGalaxy이용
set fso = server.CreateObject("SiteGalaxyUpload.FileSystemObject")
  set file = fso.OpenBinaryFile (filepath,1,false)
  Response.BinaryWrite file.ReadAll 
set fso=nothing

'DextDownLoad 이용
Set objDownload = Server.CreateObject("DEXT.FileDownload")
objDownload.Download filepath
Set objDownload = Nothing

If Err Then
 Response.Write Err.number & "<br>" & Err.source & "<br>" & Err.description & "<br>"
 Err.Clear
End If




출처 : Notebook of gogh(http://gogh914.tistory.com/62)
Posted by 부니기
2008.06.05 00:13

제목은 "~ script 보안"이라는 거창한 말을 썼지만,
기본적으로 서버 스크립트 작성시 보안 관련된 부분에 해당하는 항목들을 모아봤습니다.

1. database 연결 문자열을 web에서 분리

database connection 연결 문자열을 사용하고 있다면, 이 문자열을 별도의 file로 만들어서 관리해야 합니다. http protocol로 접근이 불가능한 위치에 놓거나 별도의 파일로 만들어 불러써야 한다.

2. 중복 login 방지

중복 login 방지를 위한 log table을 DB에 추가하여서 log table에 login된 아이디 record를 삽입, session이 종료되었을 시 session_onend 이벤트를 통해 DB에서 login된 id의 record를 삭제하는 방식.
그리고, Login ID만이 아니라 IP를 검사하여 동일 사용자 인지 판단, 막는다.

3. 소스 및 특정 주소 노출 방지

일반적으로 접근해야 하는 파일들은 모두 perma link를 노출하고 관리하되, sever측에서만 실행되는 파일들은 가능한 한 주소가 노출되지 않도록 하는 것이 좋다. 원천적인 봉쇄는 할 수 없겠지만, 최소한 주소줄에 노출되어 spammer 등의 접속을 도와주는 꼴은 막을 수 있다.
여기서 주의할 것이 html source를 막는다거나 주소표시줄의 url을 안보이게 하는 것은 불가능하고, 가능한 노출시켜주는 것이 좋으므로 perma link의 노출을 적극적으로 하는 것이 좋다. 다만 서버에서 실행되는 서버측 프로그램은 할 수 있는 한 막는 것이 더 좋겠다고 본다.

4. login 오류 횟수(submit 횟수) 제한

login 오류 횟수 제한을 위하여 user의 정보를 저장하는 table에 password 오류 횟수를 체크하는 항목 'user_pchk'을 추가, 'user_pchk' 가 5 이상이 되면 다시 로그인 시도를 할 수 없고 웹 관리자에게 연락해야 하거나 다른 페이지로 redirect되게 함. 또는 일정 시간(10초쯤)이 지난 뒤에 login할 수 있도록 제한.

5. referer check

Login 또는 '글쓰기'등 submit이 되는 모든 페이지에서는 referer의 값을 확인하여 정해진 경로이외의 접근을 막는다.
일반적으로 웹페이지의 구조상 데이터로직을 가지고 있는 페이지는 이전단계에 폼을 갖는 페이지를 거치게 된다. 따라서, 데이터로직 페이지에서는 이전페이지가 개발자에 의해 작성된 페이지인지를 체크하여 외부에서 의도하지 않은 접근이 이루어 지지 않도록 하여야 한다.
Referer를 체크해야 하는 또 하나의 단편적인 예를 보자. 회원가입 페이지에서 대부분 팝업창을 이용하여 우편번호 검색과 같은 로직을 대부분 갖고 있다. 우편번호 검색페이지와 같이 단순히 SELECT 하는 페이지를 툴을 이용하여 외부에서 끊임없이 호출하는 경우 데이터베이스가 어떤 상황에 처하게 될지 생각해 보라. 따라서, 페이지 유입경로에 대한 검증 작업이 필요하다.

7. submit

보안을 요하는 data를 hidden field에 입력되도록 하지 않는다. 또한 cookie에도 저장하지 않도록 한다. 불가피하게 저장이 필요하다면 암호화해서 저장하고 꼭 기한을 명시해 놓도록 한다.

8. submit method

submit 방식을 get방식과 post방식을 분리해서 사용한다. server측의 request는 request.form과 같이 명확한 표시를 해준다.

아래 부분은 보안이 필요한 모든 페이지에 적용하도록 해야 합니다. <%
'아래 세 줄은 client PC에 cache되지 않도록 합니다.
Response.CacheControl = "no-cache"
Response.AddHeader "Pragma", "no-cache"
Response.Expires = -1
%>

다음의 경우와 같이 변수 값을 서로 다르게 합니다.

<%
Response.Buffer=true
'client side page
<input type=text id="uid" name="uid" value="">
<input type="password" id="passwd" name="passwd" value="">

'server side page
<%
'변수를 선언합니다.
Dim strUserID, strUserPWD
strUserID = Request.Form("uid")
strUserPWD = Request.Form("passwd")

'변수에 넣은 다음 유효성 검사를 실시합니다. 유효하지 않은 문자가 있으면, 요청을 되돌려 보냅니다.
'유효성 검사는 아래 예를 참조로 정규식 표현 가이드를 활용합니다.
if strUserID = "[^\w]" then Response.Redirect("/login.asp")
if strUserPWD = "[^\w]" then Response.Redirect("/login.asp")

'값이 유효하면 database접속을 시도합니다.
strConnect = "<<-database connection string->>"
Set objCN = Server.CreateObject("ADODB.Connection")
objCN.Open strConnect
Set objRS = Server.CreateObject("ADODB.Recordset")
objRS.Open "Select * from Users where uid='" & strUserID & "'",objCN

if Not objRS.EOF then
  if StrComp(strUserPWD, Rs.Fields("memPassword").value, 1) = 0 then
    'Password is correct.
    'Set a session variable, and redirect the user to a Default.asp page
    'or the main page in your application.
    Session("UserID") = strUserID
    Response.Redirect "Default.asp"
    Response.End
  Else
    'Password is incorrect. Redirect the user to the logon page.
    Response.Redirect "Logon.asp"
    Response.End
  End if
end if
%>

9. 암호화

사용자 암호를 저장할 때에 hash나 Base64, Server.HTMLEncode, Server.URLEncode를 사용하여 암호 문자열을 incoding해서 저장한다.

이 코드 예제에는 기능으로 전송된 문자열에서 악성 문자를 제거하는 기능이 포함되어 있습니다. 위의 두 예제 모두에서 제대로 인코딩되도록 코드 페이지가 지정됩니다. 아래 예제는 Microsoft Visual Basic Scripting Edition(VBScript)으로 작성한 것입니다.

<%@ LANGUAGE="VBScript" %><%
Response.CodePage = 1252
Response.Write("Hello, " & RemoveBadCharacters(Request.Form("UserName")))
Response.Write("<br />This is why you received an error:")

Function RemoveBadCharacters(strTemp)
  Dim regEx
  Set regEx = New RegExp
  regEx.Pattern = "[^\s\w]"
  regEx.Global = True
  RemoveBadCharacters = regEx.Replace(strTemp, "")
End Function
%>

10. 첨부파일 제한 (중요)

첨부파일로 실행이 불가능한 몇개의 파일만을 업로드 할 수 있도록 해야 하며, asp, php, js 등 실행이 가능한 파일은 첨부하지 못하도록 해야 한다. fso(scripting.filesystemobject)를 이용하여 system file이 모두 노출될 수 있습니다. html의 경우 <script runat="server">코드가 들어간 html도 업로드 못하도록 해야 한다. 최근에는 flash기반의 source를 악용하는 경우도 있으므로 flash file 또한 막아야 하며, 이렇게 실행이 가능한 file을 upload하는 경우에는 zip등으로 압축하여 올리도록 해야 합니다.

11.XSS를 방지하기 위한 필터링

XSS 공격이 무엇인지 간략히 설명하자면 Cross Site Scripting의 약자로 Script 나 Tag 공격을 통해 웹 페이지가 비정상적으로 동작하게 하거나 페이지에 접근하는 유저들의 특정 데이터를 추출해가는 공격을 말한다.
특정 폼의 입력란(Input Box나 TextArea)에 태그와 자바스크립트를 통해 공격문자열을 입력하게 되면, 그 페이지를 로딩했을 때 해당 명령이 실행되게 된다. 예를 들어, <xmp> 라는 HTML 명령어를 입력하였을 때, < 와 > 로 변경하여 저장하지 않았다면, 해당 페이지는 파싱된 HTML이 실행되지 않고 <xmp>가 실행되어 <xmp> 이하의 HTML 코드들을 페이지상에 노출하게 된다. 또한, <script> 문을 작성하여 저장하게 되면 페이지가 로딩될 때마다 해당 스크립트가 실행되어 사용자의 쿠키 값을 해커가 원하는 서버로 전송시킬 수 도 있으며, 해당 페이지를 자유자재로 다른 특정 페이지로 리다이렉트 시키는 등 이외에도 여러 가지 행위를 할 수 있게 된다.
이를 방지하기 위해서는 Tag를 저장할 때, “<”와 “>” 부분을 “&lt;”와 “&gt;”로 변경하여야 한며, 입력폼의 MaxLength 제어, “<script”와 같은 단어의 입력 방지 작업들을 하여야 한다. 그리고, 페이지상에 Request.QueryString 과 같은 Value를 나타내야 하는 경우, URLEncoding과 같은 메소드를 이용하여 값을 인코딩하기 바란다.

12.SQL Injection을 방지하기 위한 필터링

너무나도 유명한 SQL Injection 이다. SQL Injection을 논할 때 마다, 예전에 방안을 마련하기 위해 수없이 테스트 했던 기억이 늘 떠오르곤 한다. SQL Injection 이 무엇인지 간략히 설명하자면, 데이터를 처리하는 과정을 침투하여 의도되지 않은 데이터 핸들링이 잃어나게 하여 데이터를 변조하거나 획득해가는 공격을 말한다. 따라서, SQL Injection 은 주요한 데이터에 대한 공격이기 때문에 절대적으로 방어해야만 하는 아주 중요한 대상이다. 실감나지 않는다면 만약 공격에 의해 테이블을 Drop 했거나 회원 Data를 획득했다고 가정해 보라.
SQL Injection 을 방지하기 위해 ADO를 통한 데이터 핸들링 시 전달되는 파라미터들에 대한 필터링 작업이 필요하게 된다. 이러한 필터링 작업만 완벽히 처리해 준다면 보안이 월등히 높아질 것이다.
파라미터 검증을 위한 함수를 작성할 시에 고려해야 할 사항에 대해 살펴보자.

  1. 데이터 타입 검증 – 데이터는 크게 INT와 Char 형식으로 나뉘게 된다. 따라서, 숫자형의 데이터를 전달해야만 하는 경우 ASP의 IsNumeric 함수를 통해 검증할 수 있다.
  2. 싱글 쿼테이션의 치환 – ADO의 RecordSet 객체를 이용하는 경우 해당 로직에 따라 싱글 퀘테이션을 치환하도록 한다. Ex) ‘ -> ‘’
  3. 문자열 체크 – 문자열이 넘어오는 경우 해당 파라미터의 사이즈에 맞도록 사이즈를 체크하여 불필요한 값이 전달되지 않도록 한다. Left 함수 등을 이용하여 전달되는 데이터의 값을 잘라내는 것도 한 방법이다.
  4. 널 체크 – 반드시 파라미터에 데이터가 전달되어야 하는 경우 널 체크를 통해 값을 인증하도록 한다.
  5. 태그의 허용 - HTML게시판과 같이 태그를 허용하는 경우를 제외하고는 &lt;와 &gt;로 치환하도록 한다.

이렇게 5가지가 파라미터 검증을 위한 가장 핵심적인 부분이다. 이외에도 해당 로직에 따라 추가적인 파라미터 검증작업이 필요하므로 구현시 보안로직에 대해 잘 고민해야만 한다.
여기서 주의해야 할 사항이 있다. 이렇게 ASP에서 파라미터 검증을 한다고 하여 SQL Injection 이 완벽히 막아지는 건 아니라는 사실이다. 이 내용은 ASP 웹 소스상에서 최대한 할 수 있는 방어 기법이라는 것이다. 이를 완벽이 막기 위해서는 데이터베이스 차원에서의 방어까지 이루어 져야 한다. 위 내용은 웹 소스상에서 기본적으로 반드시 해줘야 할 작업들에 내한 내용이다.
위에서 설명한 내용들을 모두 함수로 작성하고, 개발표준안에 추가하여 모든 스크립트가 항상 보안 로직을 통해 작성되도록 해야 한다.

13.파라미터 이름의 규칙 정의

많은 웹사이트들에서 쿼리스트링으로 전달되는 값들을 보면 UserCode 혹은 MemberCode, table 등 해당 값이 무엇인지 유추할 수 있게 제작되어 있다. 이러한 값들은 URL 확인 만으로도 값을 확인할 수 있게 된다. 누군가 공격하려 할 때 웹의 구조를 쉽게 파악할수 있다는 것이다. 따라서, 퍼머링크로 사용하지 않고 보안이 필요한 페이지에는 사용자가 쉽게 알아내지 못하도록 해당 파라미터들의 이름을 약어로 사용하거나 암호화 하는 작업이 중요하다.

14.GET 방식의 지양

회원코드, 주민번호와 같이 보안이 필요한 데이터를 전송시에 GET 방식을 지양하고 POST방식을 활용토록 하며, POST로 값을 전송할 시에 해당 데이터를 암호화하여 전달하도록 한다. GET 방식을 사용하는 경우 해당 데이터들이 너무나 쉽게 노출되는 경향이 있기 때문이다. 다만 검색엔진 등에 노출되어야 할 게시판 리스트, 게시물 내용보기 등은 GET방식으로 표시하여야 매개변수가 노출되어 직접 링크(perma link)가 가능하므로 무조건 POST쓰는 것은 좋지 않을 수도 있다.

15.인증정보의 암호화

XSS 공격을 통해 페이지에 접근하는 유저들의 정보를 쉽게 획득할 수 있다고 하였다. 유저들이 로그인후 갖게 되는 인증정보는 일반적으로 세션 혹은 쿠키를 이용해 저장되게 된다. 따라서, 세션, 쿠키 값을 생성할 시에 암호화를 하여 저장한다면 해당 데이터를 획득했다 할지라도 복호화해야 하는 작업이 필요하게 된다. 따라서, 유저들의 인증정보를 암호화 하는 작업은 상당히 유용한 작업이다.

16.naming

웹 폴더 및 페이지 이름에 대해서도 네이밍룰에 대한 권장사항이 있다. 보안 점검 툴을 실행하게 되면 찾는 취약점 중 하나가 웹 폴더 및 페이지의 이름 구조이다. Password.asp 같이 너무나 쉽게 탐지할 수 있는 페이지 구조는 취약점으로 판단하게 된다.
introduce.asp, product.asp, information.asp 등 검색 등에서 노출이 되어야 하는 파일 이름은 알기 쉽도록 사용하며 보안이 필요한 파일이름은 구조적으로 알기 힘든 이름을 사용하는 것이 좋다. "password.asp" 보다는 "validate.asp"가 좀 더 낫다.

참고 : ASP보안 (http://technet2.microsoft.com/WindowsServer/ko/Library/45746741-e2b4-4f83-8ddd-d3e57aad9ee91042.mspx?mfr=true)


Posted by 부니기
2008.05.24 03:24
숫자 세자리마다 콤마를 표현하는 방법에 대해 알아보겠습니다.

:: ASP Script 에서 표현할 경우
     response.write(formatnumber( value, 0, -1 ))

:: PHP Script 에서 표현할 경우
     echo number_format($value);

:: Perl Script 에서 표현할 경우
     print cm($value);
    sub cm { 
          my $num = shift;
          my ($n) = $num =~ /(\d+)/;
          1 while $n =~ s/(\d+)(\d{3})/$1,$2/;
          $num =~ s/(\d+)/$n/;
          $num; 
     } 

:: JSP 에서 표현할 경우 
     new DecimalFormat("###,###,###,###,###").format(value);
     보통은 Bean으로 만들어서 표현을 합니다. 

:: MS-SQL Query에 의해 표현할 경우
     SELECT 
    REPLACE(CONVERT(VARCHAR,CONVERT(MONEY,1234567890),1),'.00','')
     결과값          
     1,234,567,890
     (1개 행 적용됨)
Posted by 부니기
2008.05.24 01:35
윈도우 2003에서 ASP의 CDONTS.NewMail을 이용한 메일 보내기

윈도우 2000에서 CDONTS.NewMail을 이용해 메일을 보내던 기능이 윈도우 2003으로 버전업 하고나서 오류를 발생하고는 작동하지 않는다.

윈도우 2003은 CDONTS.NewMail대신 CDO.Message를 이용해서 메일 발송이 가능하다.
CDO.Message를 이용해 메일을 발송하려면 일단 SMTP서비스를 설치해서 기동되어 있어야 한다.
그후 코딩은 아래를 참고하면 된다.


Const cdoSendUsingPort = 2 '1일 경우 로컬(SMTP), 2일 경우 외부(SMTP)로 메일전송
Const strSmartHost = "xxx.xxx.xxx.xxx" '보낼 메일 Host Server 설정

Set objMail = Server.CreateObject("CDO.Message") '메일 객체생성 
Set objConf = objMail.Configuration 'Configuration 설정값을 저장할 objConf 객채 생성

With objConf.Fields
   .item("sendusing") = cdoSendUsingPort ' 서버 접근방법을 설정합니다
   .item("smtpserver") = strSmartHost ' 서버 주소를 설정합니다
   .Item("smtpserverport") = 25  ' 접근할 포트번호를 설정합니다
   .Item("smtpconnectiontimeout") = 30 ' 접속시도할 제한시간을 설정합니다
   .Item("smtpauthenticate") = 1  ' SMTP 접속 인증방법을 설정합니다
   .item("sendusername") = "아이디" ' SMTP 서버에 인증할 ID를 입력합니다
   .item("sendpassword") = "암호"  ' SMTP 서버에 인증할 암호를 입력합니다
   .Update
End With

  objMail.from = 메일주소  '보내는이 메일
  objMail.To= 메일주소  '받는사람 Email

  objMail.subject = "제목 들어가는곳"

  objMail.HTMLBody = "HTML형식의 Body 들어가는곳. HTMLBody나 TextBody 둘중 하나를 선택"
  objMail.TextBody = "TEXT형식의 Body 들어가는곳. HTMLBody나 TextBody 둘중 하나를 선택"
혹은
  objMail.Body = "내용"
  objMail.BodyFormat = 0 ' CdoBodyFormatHTML(0)는 HTML, CdoBodyFormatText(1)는 Text
 

  objMail.BodyPart.Charset="ks_c_5601-1987"     ' 한글을 위해선 꼭 넣어 주어야 합니다.
  objMail.HTMLBodyPart.Charset="ks_c_5601-1987" ' 한글을 위해선 꼭 넣어 주어야 합니다.

  objMail.Importance = 2 ' 우선순위 CdoLow(0)은 낮게, CdoNomal(1)은 중간, CdoHigh(2)은 높게
  objMail.Attachfile "파일패스", "파일명", 엔코딩방식 ' 첨부파일 엔코딩방식 : CdoEncodingUUencode(0)은 Ascii파일, CdoEncodingBase64(1)은 Binary파일

  objMail.send  ' 메일을 보냅니다. 
 Set objConf = Nothing ' 설정값 객체를 닫습니다.
 Set objMail = Nothing ' 메일 객체를 닫습니다.



하지만 소스를 수정하지 않고 CDONTS.NewMail을 이용한 기존의 소스를 그대로 쓸수도 있다.
아래의 첨부된 cdonts.dll 파일을 다운받아서
C:\WINDOWS\system32 경로에 cdonts.dll을 복사한 후에 아래의 명령어로 레지스트리에 등록한다

cdonts.dll

regsvr32.exe cdonts.dll

Posted by 부니기

티스토리 툴바