일일 정리

Oracle Hash, 오라클 Client 설치, PHP - 세션

mysecurity 2025. 3. 26. 23:30

목차

1. Oracle Hash

1-1 Oracle Hash 개요

1-2 Oracle Hash 종류 상세

 

2. 오라클 Client 설치

 

3. PHP - 세션

3-1 세션 개요

3-2 세션을 이용한 인증 처리

 

 

1. Oracle Hash

1-1 Oracle Hash 개요

● Oracle Hash 종류

STANDARD_HASH - VARCHAR2 타입의 데이터 처리가 가능하다.

- 다양한 알고리즘 선택이 가능하다.

- 패스워드 등 다양한 보안상의 요구를 위해 제공된다.
DBMS_CRYPTO.HASH - 여러 보안상의 요구를 위해 제공된다.

- RAW 타입 데이터를 생성한다.

- 다양한 알고리즘 선택이 가능하다.

- RAW 타입의 데이터만 처리가 가능하다.
ORA_HASH - 오라클 고유 알고리즘을 이용한다.

- 성능이 빠르다.

- 보안을 위해서 사용하는 것은 아니다.

 

- STANDARD_HASH

: 원래 존재하는 표준 함수로, 특별한 권한 없이도 사용이 가능하다.

: 텍스트 타입의 데이터를 바로 전달해 줄 수 있다.

 

- DBMS_CRYPTO.HASH

: DBMS_CRYPTO는 패키지 이름이다.

: 다양한 형태의 함수를 제공하며 해시 코드를 만들 때 내부적으로 효율적이다.

 

- ORA_HASH

: 오라클 고유 알고리즘이다. 즉, 무슨 알고리즘인지 알 수 없다.

: 데이터의 빠른 저장/찾기를 위해 사용한다.

 

RAW와 문자열

  문자열 (VARCHAR2, CHAR) RAW (RAW, LONG RAW)
정의 문자열 바이너리 데이터 (byte)
저장 UTF8과 같은 문자셋으로 인코딩된 문자열 바이너리 데이터
용도 텍스트 저장이나 출력 바이너리 데이터 처리
인식 'A' : 문자 A로 인식 0x41은 그냥 0x41로 인식
비교 문자셋 기반의 문자 단위 인식 byte 단위 인식
용도 텍스트 데이터 처리 해시, 암호와 같은 바이너리 처리
비교 비교 연산 가능 RAW 간에만 가능

 

- 바이너리 데이터는 직접 일을 수 없다.

- 해시, 암호는 모두 바이너리 처리가 되어있다.

 

1-2 Oracle Hash 종류 상세

● STANDARD_HASH

다음과 같은 다양한 알고리즘을 지원한다.

- 'MD5', 'SHA1', 'SHA256', 'SHA384', 'SHA512' 

 

해시 결과는 RAW 타입으로 저장된다.

- 저장: RAW 타입으로 저장할 수 있으나 뭔가 처리할 때 헥사코드로 처리하므로 주로  RAWTOHEX()를 이용해 HEX 문자열로 저장한다. 

- 출력 결과는 RAW,HEX 문자열이 동일해 보이나 내부 처리는 다르다.

 

※ 알고리즘에 따른 결과

  bit 길이 RAW 길이 HEX 길이
MD5 128 16 32
SHA1 160 20 40
SHA256 256 32 64
SHA384 384 48 96
SHA512 512 64 128

 

 

※ 각 알고리즘 별 컬럼 타입

  RAW 타입 VARCHAR2 타입
MD5 RAW (16) VARCHAR2 (32)
SHA1 RAW (20) VARCHAR2 (40)
SHA256 RAW (32) VARCHAR2 (64)
SHA384 RAW (48) VARCHAR2 (96)
SHA512 RAW (64) VARCHAR2 (128)

 

- RAWTOHEX()를 사용하는 경우 VARCHAR2() 타입에 저장하고 이를 사용하지 않는 경우에는 RAW() 타입의 컬럼을 사용한다.

 

- 해시 결과를 출력하거나 비교하는 일이 있다면 VARCHAR2를 권장한다. VARCHAR2는 큰 공간을 할당시켜줘도 필요한 만큼만 메모리를 차지하므로 그냥 VARCHAR2 (128)을 사용하면 된다. 

 

● STANDARD_HASH 실습

STANDARD_HASH('문자열','알고리즘') : RAW 타입

RAWTOHEX(STANDARD_HASH('문자열','알고리즘')) : HEX 문자열

 

다음과 같이 작성하여 STANDARD_HASH('asdf', 'SHA256')와 RAWTOHEX(STANDARD_HASH('asdf', 'SHA256'))의 출력값은 완전히 같은 것을 알 수 있다.

SQL> SELECT STANDARD_HASH('asdf', 'SHA256'),
 2          RAWTOHEX(STANDARD_HASH('asdf', 'SHA256'))
 3   FROM dual
 4   WHERE STANDARD_HASH('asdf', 'SHA256') =
 5         RAWTOHEX(STANDARD_HASH('asdf', 'SHA256'));

 

- STANDARD_HASH는 RAW 타입으로 저장되나, RAW 타입 데이터는 사람이 직접 읽을 수 없는 데이터이므로 자동으로 헥사 코드로 출력되어 RAWTOHEX를 이용한 것과 값이 같다.

 

- =, LIKE, IN 연산자의 경우 RAW에서 VARCHAR2로 자동 형 변환을 제공한다. 그러나 자동 형 변환을 사용하면 속도 저하와 신뢰도 문제가 있으므로 RAWTOHEX를 이용한 수동 형 변환을 권장한다.

 

 

다음과 같이 테이블을 설계하는 경우도 RAW보다 VARCHAR2를 권장한다.

SQL> CREATE TABLE ID(
 2          RAW_DATA RAW(64),
 3          HEX_DATA VARCHAR2(128)
 4 );

 

 

DBMS_CRYPTO.HASH

기본 제공되는 함수가 아니므로 DBMS_CRYPTO 패키지에 대한 실행 권한이 필요하다.

SQL> GRANT EXECUTE ON DBMS_CRYPTO TO 대상;

 

매개 인자와 리턴 값은 모두 RAW 타입이다.

- 문자열을 RAW로 변환해서 사용한다.

  (UTL_I18N.STRING_TO_RAW를 이용한다.)

 

- 인코딩은 'AL32UTF8'로 지정한다. (권장사항)

SQL> UTL_I18N.STRING_TO_RAW('입력문자열', 'AL32UTF8')

 

 

※ 오라클 권장 방식

SQL> DBMS_CRYPTO함수(UTL_I18N.STRING_TO_RAW('문자열', 'AL32UTF8'), 해시알고리즘);

 

 

※ DBMS_CRYPTO의 함수

ENCRYPT 데이터를 암호화한다. (암호화 알고리즘, 키, 패딩, 모드 설정 필요)
DECRYPT 데이터를 복호화한다.
HASH 문자열이나 RAW 데이터를 해시 처리한다. (MD5, SHA‐1, SHA‐2 등)
RANDOMBYTES 지정된 길이의 랜덤 바이트를 생성한다. (난수 생성)
. . .  . . .

 

- DBMS_CRYPTO 는 암호화 알고리즘이나 해시 알고리즘 등 함수 옵션을 상수로 표현한다.

1 MD5 해시
2 SHA‐1 해시
4 SHA‐256 해시
5 SHA‐384 해시
6 SHA‐512 해시

 

1 AES 128비트 암호화
2 AES 192비트 암호화
3 AES 256비트 암호화
4 3DES 암호화
5 DES 암호화
6 RC4 암호화

 

 

해시값 저장은 STANDARD_HASH와 동일하다.

ex) SHA‐256 알고리즘으로 해시코드를 출력한다.

SQL> SELECT RAWTOHEX(
             DBMS_CRYPTO.HASH(
               UTL_I18N.STRING_TO_RAW('hello', 'AL32UTF8'), 4
               )
             )
 2  FROM dual;

 

2. 오라클 Client 설치

다음과 같이 리눅스 서버에 오라클 클라이언트를 설치하여 오라클을 이용할 수 있다.

 

① 패키지 다운과 설치

// 패키지 다운로드
# dnf install ‐y wget 
# wget https://download.oracle.com/otn_software/linux/instantclient/1925000/oracle‐instantclient19.25‐basic‐19.25.0.0.0‐1.x86_64.rpm

# wget https://download.oracle.com/otn_software/linux/instantclient/1925000/oracle‐instantclient19.25‐devel‐19.25.0.0.0‐1.x86_64.rpm

# wget https://download.oracle.com/otn_software/linux/instantclient/1925000/oracle‐instantclient19.25‐sqlplus‐19.25.0.0.0‐1.x86_64.rpm


// 패키지 설치
# dnf install ‐y oracle‐instantclient19.25‐basic‐19.25.0.0.0‐1.x86_64.rpm

# dnf install ‐y oracle‐instantclient19.25‐devel‐19.25.0.0.0‐1.x86_64.rpm

# dnf install ‐y oracle‐instantclient19.25‐sqlplus‐19.25.0.0.0‐1.x86_64.rpm

 

② sqlnet.ora 파일과 tnsnames.ora 파일 생성

# mkdir ‐p /usr/lib/oracle/network/admin/ 
# echo 'NAMES.DIRECTORY_PATH= (TNSNAMES)' > /usr/lib/oracle/network/admin/sqlnet.ora
# cat <<EOF > /usr/lib/oracle/network/admin/tnsnames.ora
 oracle = (DESCRIPTION =
 (ADDRESS = (PROTOCOL = tcp) (HOST = 서버_IP) (port = 1521))
 (CONNECT_DATA = (SID = DB19))
 )
EOF

 

③ 접속 환경 설정

# echo 'export TNS_ADMIN=/usr/lib/oracle/network/admin' >> /etc/profile
# echo 'export LD_LIBRARY_PATH=/usr/lib/oracle/19.25/client64/lib' >> /etc/profile
# echo 'export NLS_LANG=AMERICAN_AMERICA.AL32UTF8' >> /etc/profile

 

④ 접속 테스트

sqlplus 계정/패스워드@oracle

 

 

3. PHP - 세션

3-1 세션 개요

세션은 한 페이지에서 변수에 저장한 값을 여러 페이지를 방문하는 동안 계속 유지하는 방법으로, 네이버에 로그인 한 상태를 유지하며 내 계정의 블로그나 카페를 방문할 때 세션이 쓰인다.

 

세션을 이용하여 다음 a.php의 변수값을 b.php에서도 저장하여 이용할 수 있다.

 

※ a.php

 <?
   session_start();
   
   $_SESSION["name"] = '홍길동';
   $_SESSION["passwd"] = '암호';
   
   $name=$_SESSION["name"];
   $passwd= $_SESSION["passwd"];
   
   echo(" name = $name <br>");
   echo(" passwd = $passwd <br>");
   echo("<a href='./b.php'>b.php</a>");
 ?>

 

※ b.php

 <?
   session_start();
   
   $name = $_SESSION["name"];
   $passwd = $_SESSION["passwd"];
   
   echo(" name = $name <br>");
   echo(" passwd = $passwd <br>");
   
   session_destroy();
  ?>

 

session_start() 를 이용해 세션을 시작한다. 세션은 하드드라이브에 만들어지며, 항상 파일의 첫 머리에 나와야 한다.

 

a.php의 $_SESSION["name"] 변수와 $_SESSION["passwd"] 변수에 세션 값을 저장하여 b.php의 변수에 동일한 세션 값을 저장할 수 있다.

이 세션 값들은 session _destroy() 함수나 session_unset() 함수를 호출하여 세션을 종료하기 전까지 계속 유지된다.

 

● 세션 관련 함수

session_start () - 세션을 시작한다.

- 매개 변수 없이 쓰인다.

- session.auto_start=1로 설정하면 함수를 자동으로 실행한 것과 동일하다.

- $_SESSION 배열을 이용해서 세션 변수를 등록한다.
session_destroy() - 세션을 종료한다.

- 매개 변수 없이 쓰인다.

- 이후 세션을 이용하려면 새로운 세션을 시작해야 한다.

 

 

3-2 세션을 이용한 인증 처리

다음과 같이 id.html, id.php를 이용해 계정을 등록하고 check.html, check.php를 이용해 로그인하는 프로그램을 작성할 수 있다.

 

인증을 위한 테이블 추가 - 오라클에서 테이블 생성

CREATE TABLE id(
 id  VARCHAR2(50),
 passwd VARCHAR2(128),
 CONSTRAINT id_pk_id PRIMARY KEY (id)
 );

 

 

● 계정 등록 프로그램

// id.html //

<html><head>
    <meta http-equiv="content-type" content="text/html; charset=euc-kr">
    <title> 계정등록 </title>
    </head>
    <body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">
        회원가입
    <form name="id" action="id.php" method="POST">
    <center>
    <table  border="0" width="250">
      <tr>
         <td width="50"><p align="center">ID</p></td>
         <td>&nbsp;&nbsp;<input type="text" name="id"></td>
      </tr> 
      <tr>
         <td width="50"><p align="center">passwd</p></td>
         <td>&nbsp;&nbsp;<input type="password" name="passwd"></td>
      </tr> 
      <tr>
         <td colspan="2">
             <p align="center"><input type="submit" name="확인" value="확인"></p>
         </td>
      </tr> 
    </table>
    </center>
    </form>
    <br><hr><br>
    <?  show_source(__FILE__); ?>
    </body></html>

 

// id.php //

<html>
    <head>
        <title>id.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>
<?  
  require('conn.php');  
  
  $id = $_POST["id"];  
  $passwd = $_POST["passwd"];
  $sql="insert into id (id, passwd) 
        values ('$id', RAWTOHEX(STANDARD_HASH('$passwd', 'SHA256')))";
                       
  $result=oci_parse($conn,$sql);
  $re=@oci_execute($result);                  // 입력된 값을 테이블에 저장한다.
  oci_free_statement($result);
  oci_close($conn);
  
 if(!$re)
    { echo(" <script>
             window.alert('계정 등록 장애입니다.');
              history.go(-1);
              </script>
           ");
      exit;
    }            // st_vi.php를 호출할 때 $id나 $passwd는 세션변수에
 else            // 등록되었으므로 전달할 필요가 없다. 
     echo(" <meta http-equiv='Refresh' content = '0; URL=check.html'>");
 ?>
</form>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

 

● 로그인 프로그램

// check.php //

<html><head>
<meta http-equiv="content-type" content="text/html; charset=euc-kr">
<title> login </title>
</head>
<body bgcolor="white" text="black" link="blue" vlink="purple" alink="red">
    로그인
<form name="login" action="check.php" method="POST">
<center>
<table  border="0" width="250">
    <tr>
        <td width="50"><p align="center">ID</p></td>
        <td>&nbsp;&nbsp;<input type="text" name="id"></td>
    </tr> 
    <tr>
        <td width="50"><p align="center">passwd</p></td>
        <td>&nbsp;&nbsp;<input type="password" name="passwd"></td>
    </tr> 
    <tr>
        <td colspan="2">
            <p align="center"><input type="submit" name="확인" value="확인"></p>
        </td>
    </tr> 
</table>
</center>
</form>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body></html>

 

// check.php //

<html>
    <head>
        <title>check.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>
<?  
  session_start();
  require('conn.php');  
  
  $id = $_POST["id"];  
  $passwd = $_POST["passwd"];
  $sql="select * from id 
      where id = '$id'
      and passwd = RAWTOHEX(STANDARD_HASH('$passwd', 'SHA256'))";
  $result=oci_parse($conn,$sql);
  $re=oci_execute($result);
  $row_num=oci_fetch_all($result,$row); 
  oci_free_statement($result);
  oci_close($conn);
  
  if($row_num == 1){
     $_SESSION["id"] = $id;
     $_SESSION["passwd"] = $passwd;
     
     echo(" <meta http-equiv='Refresh' content = '0; URL=st_vi.php'>");
  }    
  else{
       echo(" <script>
               window.alert('잘못된 계정 정보입니다.');
               history.go(-1);
              </script>
            ");
      exit;
     }
?>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

 

● 로그인에 따른 화면 변화

앞서 작성한 check.html과 check.php에서는 등록한 아이디와 패스워드를 입력했을 때 st_vi.php를 호출하도록 로그인 프로그램을 작성하였으나 로그인이라는 것은 로그인 전과 후의 화면이 차이가 있어야 의미가 있다.

 

다음과 같이 로그인 여부에 따라 출력 화면을 달리 하여 로그인이 의미를 갖도록 st_vi.php를 개선할 수 있다.

// st_vi.php 로그인 여부에 따른 화면 출력 개선 //

<html>
    <head>
        <title>st_vi.php</title>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
<body>
<?
  session_start();


  if(empty($_GET["logout"])){}
  else session_unset();    // session_distroy 사용과 비교한다.

  require('conn.php');

  $id = $_SESSION["id"];
  $passwd = $_SESSION["passwd"];

  $sql="select * from id
        where id = '$id'
        and passwd = RAWTOHEX(STANDARD_HASH('$passwd', 'SHA256'))";
  $result=oci_parse($conn,$sql);
  $re=oci_execute($result);
  $row_num=oci_fetch_all($result,$row);

  if($row_num == 1){                  // 로그인 상태
   echo("<a href=./st_in.html>자료 입력하기</a>
         <a href=./st_vi.php?logout=$row_num>logout</a><hr>
         ");
   }
   else{                          // 비로그인 상태
   echo(" <a href=./id.html>계정등록</a>
          <a href=./check.html>login</a><hr>");
   }
?>

<?
  echo("<a href=./st_in.html>자료 입력하기</a><hr>");
  require('conn.php');
// =========================== 삭제
$del=$_GET['del'];
if (empty($del)){}
else {
      $sql="delete from student where sno='$del'";
      $result=oci_parse($conn,$sql);
      oci_execute($result);
      oci_free_statement($result);
      }
// ===========================
// =========================== 검색
$search = $_GET['search'];
if (empty($search))
   {
    $sql="select sno,sname,sex,major,syear,to_char(avr,'0.00') avr
          from student order by sno";
   }
else
   {
    $sql="select sno,sname,sex,major,syear,to_char(avr,'0.00') avr
          from student
          where sname like '%{$search}%' order by sno";
   }
// ===========================

  $result=oci_parse($conn,$sql);
  oci_execute($result);

  $row_num=oci_fetch_all($result, $row);  // $row_num : 전제 행의 수
  oci_free_statement($result);
  oci_close($conn);
  echo("Row의 개수는 $row_num 입니다.<br><hr>");

  $scale=5;                           // 화면에 출력할 행의 개수
  $start = $_GET['start'];
  if (empty($start)){ $start=0;}    // 첫 화면은 0에서 시작, 배열의 키값은 0에서 시작
  echo("<table border='0'>");
  for ($i=$start; $i<($start+$scale); $i++) {
    if($i<$row_num){
      echo("
      <tr>
         <td width='50'><p align='center'>{$row['SNO'][$i]}</p></td>
         <td width='80'><p align='center'>{$row['SNAME'][$i]}</p></td>
         <td width='20'><p align='center'>{$row['SEX'][$i]}</p></td>
         <td width='20'><p align='center'>{$row['SYEAR'][$i]}</p></td>
         <td width='50'><p align='center'>{$row['MAJOR'][$i]}</p></td>
         <td width='30'><p align='center'>{$row['AVR'][$i]}</p></td>
         <td width='30'><a href=./st_vi.php?del={$row['SNO'][$i]}>del</a></td>
      </tr>
     ");
}
}
echo("</table><hr>");
$p=$start-$scale;     // 이전 화면의 시작 위치
$n=$start+$scale;     // 다음 화면의 시작 위치
if($p>=0)
echo("<a href=./st_vi.php?start=$p&search=$search>[이전페이지]</a>&nbsp;&nbsp;");
else
echo("이전페이지&nbsp;&nbsp;");

if($n<$row_num)
echo("<a href=./st_vi.php?start=$n&search=$search>[다음페이지]</a>");
else
echo("다음페이지");

?>



<form name='search' method='get' action='st_vi.php'>
검색창 &nbsp;<input type='text' name='search'>
             <input type='submit' name='확인' value='확인'>
</form>
<br><hr><br>
<?  show_source(__FILE__); ?>
</body>
</html>

 

7 ~ 35행 프로그래밍 작성

: 기존 st_vi.php 파일에 7 ~ 35행 프로그래밍을 추가로 작성하여 로그인 여부에 따라 다른 버튼이 출력되도록 한다.

- 로그인 시: '자료입력', 'logout' 버튼 출력

- 비로그인 시: '계정등록', 'login' 버튼 출력