CodeIgniter

회원가입 & 비밀번호 암호화

수업소개

이번 수업에서는 Session 시간에 만든 로그인 기능의 보안성을 강화하는 방법을 알아본다. 또한 회원가입 기능을 구현해서 웹서비스를 다중 사용자 시스템으로 만드는 방법도 살펴볼 것이다. 그 과정에서 공개된 외부 라이브러리를 가져와서 프로젝트에 적용하는 방법도 배워보자. 

이번 시간에 알아볼 password_compat 라이브러리는 PHP 5.3.7부터 지원한다. 또 네이티브 API인 password_hash는 php 5.5부터 지원한다.

회원가입

회원가입을 통해서 인증된 복수의 사용자가 서비스를 사용하도록 해보자. 이를 위해서는 회원의 정보를 어딘가에 저장했다가 로그인이 시도되면 데이터베이스에 저장된 정보와 비교한다. 비교결과 인증된 사용자임이 확인되면 세션을 발급해서 로그인된 상태를 유지할 수 있도록 한다. 세션과 관련된 부분은 이미 세션 수업을 통해서 배웠다. 

회원의 로그인 정보를 저장하기 위해서는 데이터베이스 테이블을 설계해야 한다. user라는 이름의 테이블을 만들고 아래의 SQL문을 실행해서 회원정보를 저장할 테이블을 만들자. password는 암호화된 데이터가 저장되기 때문에 충분히 큰 길이를 지정했고, email은 일반적으로 50글자가 넘지 않기 때문에 50 글자 제한을 두었다. email은 모든 회원이 중복되지 않는 유일무일한 식별자가를 가져야 하기 때문에 중복을 허용하지 않는 UNIQUE 타입의 인덱스로 지정했다. 

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(50) NOT NULL,
  `password` varchar(255) NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email_idx` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

회원가입 버튼 추가

/application/views/head.php

차이점

코드

<ul class="nav pull-right">
	<?php
	if($this->session->userdata('is_login')){
	?>
		<li><a href="/index.php/auth/logout">로그아웃</a></li>
	<?php
	} else {
	?>
		<li><a href="/index.php/auth/login">로그인</a></li>
		<li><a href="/index.php/auth/register">회원가입</a></li>
	<?php
	}
	?>
</ul>	

회원정보 입력 뷰 생성

사용자로부터 회원정보를 입력받아서 서버로 전달할 양식을 만들어보자. 

/application/views/register.php

<div>  
  <div class="span4"></div>
  <div class="span4">
    <?php echo validation_errors(); ?>
    <form class="form-horizontal" action="/index.php/auth/register" method="post">
      <div class="control-group">
        <label class="control-label" for="inputEmail">이메일</label>
        <div class="controls">
          <input type="text" id="email" name="email" value="<?php echo set_value('email'); ?>" placeholder="이메일">
        </div>
      </div>
      <div class="control-group">
        <label class="control-label" for="nickname">닉네임</label>
        <div class="controls">
          <input type="text" id="nickname" name="nickname" value="<?php echo set_value('nickname'); ?>"  placeholder="닉네임">
        </div>
      </div>
      <div class="control-group">
        <label class="control-label" for="password">비밀번호</label>
        <div class="controls">
          <input type="password" id="password" name="password" value="<?php echo set_value('password'); ?>"   placeholder="비밀번호">
        </div>
      </div>      
      <div class="control-group">
        <label class="control-label" for="re_password">비밀번호 확인</label>
        <div class="controls">
          <input type="password" id="re_password" name="re_password" value="<?php echo set_value('re_password'); ?>"   placeholder="비밀번호 확인">
        </div>
      </div>
      <div class="control-group">
        <label class="control-label"></label>
        <div class="controls">
          <input type="submit" class="btn btn-primary" value="회원가입" />
        </div>
      </div>      
    </form>  
  </div>
  <div class="span4"></div>  
</div>

회원정보 모델 생성

회원정보를 저장하는 user 테이블을 제어할 모델을 만들어보자. 모델은 회원정보를 읽고, 쓰고, 수정하는 일반적인 기능이 담겨있다. 

/application/models/usre_model.php

코드

<?php
class User_model extends CI_Model {

    function __construct()
    {        
        parent::__construct();
    }

    function gets()
    {
        return $this->db->query("SELECT * FROM user")->result();
    }

    function get($option)
    {
        $result = $this->db->get_where('user', array('email'=>$option['email']))->row();
        var_dump($this->db->last_query());
        return $result;
    }

    function add($option)
    {
        $this->db->set('email', $option['email']);
        $this->db->set('password', $option['password']);
        $this->db->set('created', 'NOW()', false);
        $this->db->insert('user');
        $result = $this->db->insert_id();
        return $result;
    }
}

회원정보 컨트롤러 생성

회원정보를 입력하기 위한 뷰와 모델을 만들었기 때문에 이것들을 결합시켜줄 컨트롤러를 만들어보자. 컨트롤러 auth.php에 회원가입 페이지를 출력하는 메소드를 만들자. 아래의 URL로 접근 했을 때 페이지를 출력하고, 데이터를 입력하는 역할을 하는 페이지를 보여줄 것이다. 

http://ooo2.org/index.php/auth/register

만약 password_hash가 존재하지 않는 함수라는 오류가 발생하면 본 토픽의 말미에 있는 외부 라이브러리 사용이라는 내용을 참조하자.

/application/controllers/auth.php

차이점

코드

function register(){
    $this->_head();

    $this->load->library('form_validation');

    $this->form_validation->set_rules('email', '이메일 주소', 'required|valid_email|is_unique[user.email]');
    $this->form_validation->set_rules('nickname', '닉네임', 'required|min_length[5]|max_length[20]');
    $this->form_validation->set_rules('password', '비밀번호', 'required|min_length[6]|max_length[30]|matches[re_password]');
    $this->form_validation->set_rules('re_password', '비밀번호 확인', 'required');

    if($this->form_validation->run() === false){
        $this->load->view('register');    
    } else {
        if(!function_exists('password_hash')){
            $this->load->helper('password');
        }
        $hash = password_hash($this->input->post('password'), PASSWORD_BCRYPT);

        $this->load->model('user_model');
        $this->user_model->add(array(
            'email'=>$this->input->post('email'),
            'password'=>$hash,
            'nickname'=>$this->input->post('nickname')
        ));

        $this->session->set_flashdata('message', '회원가입에 성공했습니다.');
        $this->load->helper('url');
        redirect('/');
    }

    
    $this->_footer();   
}

인증방법

사용자의 비밀번호는 시스템에서 가장 귀하고 위험한 정보다. 일반적인 사용자들은 모든 서비스에서 동일한 비밀번호를 사용하기 때문에 사용자의 비밀번호가 유출되면 그 사용자가 사용하는 모든 서비스까지 연쇄적으로 위험에 처하게 된다. 비밀번호가 유출될 수 있는 방법은 많지만 가장 대표적인 것이 회원정보가 담겨있는 데이터베이스에 침입자가 접속하는 경우다. 이런 경우 사용자의 비밀번호가 암호화되어 있다면 침입자는 의미없는 데이터만을 획득할 수 있기 때문에 이 정보를 가지고 제2의 공격을 시도하기 어렵다. 이에 대한 자세한 내용은 NHN의 개발자 블로그인 Hello world, 안전한 패스워드에 대한 내용을 꼼꼼하게 정독하자.

단방향 hash

단방향 해쉬는 쉽게 이야기해서 비밀번호를 풀 수 없는 방법으로 암호화하는 것이다. 이를테면 사용자의 비밀번호가 111111 이라고 할 때 이것을 단방향 해쉬하면 아래와 같은 정보로 변환된다. 이것을 저장하고 사용자의 비밀번호 111111은 기록하지 않는다. 

$10$k9lKqq1mxgQTI8fR1tf/GexMDd6wcU52gx931t4/5J/qZeah/6acm

사용자가 로그인을 시도할 때는 사용자가 입력한 비밀번호를 해쉬한 결과값과 데이터베이스에 저장된 결과값을 비교해서 일치하면 인증된 사용자임을 식별할 수 있다. 

PHP의 단방향 해쉬

PHP는 5.5 버전부터 password_hash라는 비밀번호용 해쉬를 쉽게 만들 수 있는 API를 제공한다. 사용하고 있는 PHP의 버전이 5.5 하위 버전이라면 github의 password_compat를 사용한다. 이 함수를 이용해서 해쉬를 생성할 때는 아래와 같은 구문을 사용한다. 이것은 $password의 값을 BCRYPT 방식으로 암호화한다. BCRYPT 방식은 현재로서는 충분히 안전한 암호화 방식으로 알려졌고, 현재까지 PHP에서 제공하는 가장 강력한 암호화 방식이기도 하다. 아래 구문을 통해서 획득한 $hash의 값을 데이터베이스에 저장한다.

$hash = password_hash($password, PASSWORD_BCRYPT);

사용자가 입력한 값과 데이터베이스에 저장된 값이 일치하는지를 확인하기 위해서는 password_verify를 사용한다. 이 함수의 리턴값이 true라면 비밀번호가 일치하는 것이다. 

if (password_verify($password, $hash)) {
    /* Valid */
} else {
    /* Invalid */
}

로그인

로그인 로직을 변경해보자. Session 수업에서는 config.php 파일에 기록한 아이디와 비밀번호를 이용해서 인증을 했는데, 이제는 데이터베이스의 정보를 사용해서 인증을 하도록 변경해보자. 또한 저장된 사용자 정보가 BCRYPT 방식이기 때문에 인증도 BCRYPT 방식으로 진행해야 한다. 

Application/controllers/auth.php 

차이점

코드

function authentication(){
    $this->load->model('user_model');
    $user = $this->user_model->getByEmail(array('email'=>$this->input->post('email')));
    if(!function_exists('password_hash')){
        $this->load->helper('password');
    }
	if(
		$this->input->post('email') == $user->email && 
        password_verify($this->input->post('password'), $user->password)
	) {
		$this->session->set_userdata('is_login', true);
		$this->load->helper('url');
		redirect("/");
	} else {
		echo "불일치";
		$this->session->set_flashdata('message', '로그인에 실패 했습니다.');
		$this->load->helper('url');
		redirect('/auth/login');
	}
}

로그인 페이지에서 서버로 전송하는 id를 email로 변경하자.

/application/views/login.php

차이점

코드

<div class="control-group">
  <label class="control-label" for="inputEmail">아이디</label>
  <div class="controls">
    <input type="text" id="email" name="email" placeholder="이메일">
  </div>
</div>
<div class="control-group">
  <label class="control-label" for="inputPassword">비밀번호</label>
  <div class="controls">
    <input type="password" id="password" name="password"  placeholder="비밀번호">
  </div>
</div>      

외부 라이브러리 사용 

외부의 라이브러리를 사용할 때는 그것이 클래스 기반인지, 함수 기반인지를 먼저 살펴봐야 한다. 함수 기반이라면 helper의 체계로 사용하고, 클래스 기반이라면 Library로 사용하면 된다. 

예제에서 password_hash라는 PHP의 API를 사용했는데, 이 API는 PHP 5.5부터 지원된다. 해서 이 API와 호환되는 라이브러리를 찾아봤더니 password_compat라는 것이 있었다. 이 함수는 후에 사용 중인 PHP의 버전이 password_hash를 지원하면 자동으로 해당 라이브러리를 사용하도록 구현되어 있다. 

아래의 절차로 진행한다. 

  1. 파일을 다운로드 한다.
    https://github.com/ircmaxell/password_compat
  2. /lib/password.php 파일을 다운로드 받아서 /application/helpers/password_helper.php 로 저장한다. 
  3. password_* 명령을 사용하기 전에 아래의 구문을 이용해서 password_helper를 로드한다.
    $this->load->helper('password');

/application/helpers/password_helper.php

<?php
/**
 * A Compatibility library with PHP 5.5's simplified password hashing API.
 *
 * @author Anthony Ferrara <ircmaxell@php.net>
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 * @copyright 2012 The Authors
 */

if (!defined('PASSWORD_BCRYPT')) {

    define('PASSWORD_BCRYPT', 1);
	define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);

	/**
	 * Hash the password using the specified algorithm
	 *
	 * @param string $password The password to hash
	 * @param int    $algo     The algorithm to use (Defined by PASSWORD_* constants)
	 * @param array  $options  The options for the algorithm to use
	 *
	 * @return string|false The hashed password, or false on error.
	 */
	function password_hash($password, $algo, array $options = array()) {
		if (!function_exists('crypt')) {
			trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
			return null;
		}
		if (!is_string($password)) {
			trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
			return null;
		}
		if (!is_int($algo)) {
			trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
			return null;
		}
		switch ($algo) {
			case PASSWORD_BCRYPT:
				// Note that this is a C constant, but not exposed to PHP, so we don't define it here.
				$cost = 10;
				if (isset($options['cost'])) {
					$cost = $options['cost'];
					if ($cost < 4 || $cost > 31) {
						trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
						return null;
					}
				}
				$required_salt_len = 22;
				$hash_format = sprintf("$2y$d$", $cost);
				break;
			default:
				trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
				return null;
		}
		if (isset($options['salt'])) {
			switch (gettype($options['salt'])) {
				case 'NULL':
				case 'boolean':
				case 'integer':
				case 'double':
				case 'string':
					$salt = (string) $options['salt'];
					break;
				case 'object':
					if (method_exists($options['salt'], '__tostring')) {
						$salt = (string) $options['salt'];
						break;
					}
				case 'array':
				case 'resource':
				default:
					trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
					return null;
			}
			if (strlen($salt) < $required_salt_len) {
				trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
				return null;
			} elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
				$salt = str_replace('+', '.', base64_encode($salt));
			}
		} else {
			$buffer = '';
			$raw_length = (int) ($required_salt_len * 3 / 4 + 1);
			$buffer_valid = false;
			if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
				$buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
				if ($buffer) {
					$buffer_valid = true;
				}
			}
			if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
				$buffer = openssl_random_pseudo_bytes($raw_length);
				if ($buffer) {
					$buffer_valid = true;
				}
			}
			if (!$buffer_valid && is_readable('/dev/urandom')) {
				$f = fopen('/dev/urandom', 'r');
				$read = strlen($buffer);
				while ($read < $raw_length) {
					$buffer .= fread($f, $raw_length - $read);
					$read = strlen($buffer);
				}
				fclose($f);
				if ($read >= $raw_length) {
					$buffer_valid = true;
				}
			}
			if (!$buffer_valid || strlen($buffer) < $raw_length) {
				$bl = strlen($buffer);
				for ($i = 0; $i < $raw_length; $i++) {
					if ($i < $bl) {
						$buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
					} else {
						$buffer .= chr(mt_rand(0, 255));
					}
				}
			}
			$salt = str_replace('+', '.', base64_encode($buffer));

		}
		$salt = substr($salt, 0, $required_salt_len);

		$hash = $hash_format . $salt;

		$ret = crypt($password, $hash);

		if (!is_string($ret) || strlen($ret) <= 13) {
			return false;
		}

		return $ret;
	}

	/**
	 * Get information about the password hash. Returns an array of the information
	 * that was used to generate the password hash.
	 *
	 * array(
	 *    'algo' => 1,
	 *    'algoName' => 'bcrypt',
	 *    'options' => array(
	 *        'cost' => 10,
	 *    ),
	 * )
	 *
	 * @param string $hash The password hash to extract info from
	 *
	 * @return array The array of information about the hash.
	 */
	function password_get_info($hash) {
		$return = array(
			'algo' => 0,
			'algoName' => 'unknown',
			'options' => array(),
		);
		if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
			$return['algo'] = PASSWORD_BCRYPT;
			$return['algoName'] = 'bcrypt';
			list($cost) = sscanf($hash, "$2y$%d$");
			$return['options']['cost'] = $cost;
		}
		return $return;
	}

	/**
	 * Determine if the password hash needs to be rehashed according to the options provided
	 *
	 * If the answer is true, after validating the password using password_verify, rehash it.
	 *
	 * @param string $hash    The hash to test
	 * @param int    $algo    The algorithm used for new password hashes
	 * @param array  $options The options array passed to password_hash
	 *
	 * @return boolean True if the password needs to be rehashed.
	 */
	function password_needs_rehash($hash, $algo, array $options = array()) {
		$info = password_get_info($hash);
		if ($info['algo'] != $algo) {
			return true;
		}
		switch ($algo) {
			case PASSWORD_BCRYPT:
				$cost = isset($options['cost']) ? $options['cost'] : 10;
				if ($cost != $info['options']['cost']) {
					return true;
				}
				break;
		}
		return false;
	}

	/**
	 * Verify a password against a hash using a timing attack resistant approach
	 *
	 * @param string $password The password to verify
	 * @param string $hash     The hash to verify against
	 *
	 * @return boolean If the password matches the hash
	 */
    function password_verify($password, $hash) {
		if (!function_exists('crypt')) {
			trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
			return false;
		}
		$ret = crypt($password, $hash);
		if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
			return false;
		}

		$status = 0;
		for ($i = 0; $i < strlen($ret); $i++) {
			$status |= (ord($ret[$i]) ^ ord($hash[$i]));
		}

		return $status === 0;
	}
}

태그

태그명 : register 

태그주소https://github.com/egoing/codeigniter_codeingeverbody/tree/register

댓글

댓글 본문
  1. 존레논아부지
    완!!!
  2. hanjaelee
    2024.03.08 수강완료 감사합니다!
  3. jeisyoon
    2021.09.26 Ci 4 에서 성공

    Validation Ci 4 Manual 로 암호화 작업은 PHP Document로 자습 후 완성함

    Ci 4 Validation : http://ci4doc.cikorea.net......tml
    PHP 암호화 작업 : https://www.php.net......php
  4. jeisyoon
    2021.08.24 회원가입 & 비밀번호 암호하 - OK
  5. 저거슨
    auth/authentication여기에 getbyemail이 없다는거...깃헙엔있네요
  6. Deuklyoung Ko
    질문을 더 구체적으로 하셔야 할 듯 하네요.

    클라이언트에서 암호화 코드 노출 안시키고 어떻게 암호화 하신다는 건지?

    그리고 왜 더 안전하다고 생각하시는 건지도.
    대화보기
    • 에그에그
      실수로 빠진것같습니다.
      현업에서는 DB에 추가해야합니다 ㅎㅎ
      대화보기
      • 김세창
        공부하다가 이상한 점을 발견에서 질문드립니다.
        sql문에.. nickname을 받는 곳이 없는것같은데..

        CREATE TABLE `user` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `email` varchar(50) NOT NULL,
        `password` varchar(255) NOT NULL,
        `created` datetime NOT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `email_idx` (`email`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

        왜 없는지 알수있을까요?? 깜빡하고 빼먹으신건지..
        초보로써는 잘 모르겠습니다.
      • Leos
        강의 감사합니다. ^^ 열심히 해보고 있습니다.

        회원가입이 완료되고 데이터 베이스에 값이 넘어오긴 하는데

        hash 저장이 database 에서 0으로 저장이 되네요?

        PHP Version 5.4.8 버전인데 해결방안이 있나요?
      • 초보자
        controller에서 배열을 보내서 model에서 바로 값을 비교하면 안되는건가요??
        result해서 $user로 받지않고 $result=$this->db->get_where('user',array('id'=>$option['email']))->row();
        해서 바로 $result->email같은 식으로요.. 일단 $result->email로 받는건 불가능한것 같은데 model에서 바로 작업하는 방법이 있으면 알려주세요~
      • song
        클라이언트의 비밀번호를 서버에서 암호화하는 것보다 클라이언트에서 미리 암호화해서 서버로 전송하는 것이 보안상 더 좋지 않나요?
      • 신입1
        와 감사합니다. codeigniter는 간단한 구문으로도 많은걸 할 수 있네요
      • JustStudy
        고맙습니다
      • password_verify할때... php 5.3이면, 다시 로드 안하면..
        helper로드 안하면 에러 납니다. 참고하세요^^;;
      • 개발자
        회사에서 php를 이용하여 개발하는 개발자인데, 소기업이다보니깐 그냥 하드코딩을 이용하고있는데,
        프레임워크를 사용하는 다른회사들에 뒤쳐지지않기 위해 가장 많이 쓴다고 CI를 study 중인데
        강좌가 개념에 쏙쏙들어와서 어느정도 CI를 이용해서 기본적인 홈페이지는 만들 수 있을 것 같습니다
        정말 전문 강사보다도 잘 설명해주시는 것 같습니다 너무 감사드립니다^^
      • chocyber
        코드이그나이트 공부중입니다..
        생활코딩 덕분에 빠른 시간에 개념을 잡는데 많은 도움이 되었습니다.
        정말 감사를 드리며 앞으로도 계속해서 생활코딩이 발전하길 바라며
        주위 사람들에게 전파하도록 하겠습니다..
      • 정동원
        PHP 5.5.29 버전인데 저도 비밀번호 관련해서 같은 문제가 생기는 것 같네요.
        문제가... 김승갑님이 올려두신 링크에 있는 방법이 제 파일 구성이랑 달라서 거기에 막혀 있는 ㅠ...
        계속 들이대면 뭔가 보이리라 믿고 계속 찾고 있습니다. ㅎ
      • will
        password_helpr 오류 관련
        전 apmsetup 쓰고있습니다. php구버젼이라 오류떴는데, 승갑님 말씀대로 php 업그레이드하고 하니까 잘 됩니다.

        승갑님이 올려놓으신 php 5.4.30 링크가 없어서
        http://windows.php.net......zip
        전 이걸로 했습니다.
        password_heler 다 잘 작동되고 데이터베이스에 해시로 암호화된 데이터입력도 확인했습니다

        근데 php버젼업 하니 페이지속도가 느려지네요



        //의견
        로그인시 아이디 틀렸을떄 오류안뜨게하는건
        if( $user == true &&
        $this->input->post('email') ==$user->email &&
        password_verify($this->input->post('password'),$user->password)
        )
        이렇게하면 될꺼같구

        ~님 환영합니다 뜨게하기 위해선
        $this->session->set_userdata('is_login', true); 이구문을 배열로
        $this->session->set_userdata( array('is_login' => true, 'nickname' => $user->nickname));
        이렇게 바꿔준후
        head부분에 <?php echo $this->session->userdata('nickname') ?>님 환영합니다
        이런식으로 바꿔주면 될꺼같습니다
        대화보기
        • Letz
          강의 완~전 잘 보고 있습니다 ^^
          jsp로만 개발하다가 php공부해보고 싶어서 검색으로 왔다가 신세계를 만났네요..

          쉽고 꼼꼼하게 설명해주셔서 정말 감사합니다!
          그럼 계속 수고해주세요! 응원합니다.
        • 샤핀
          감사합니다. 잘 봤습니다. ^^
        • egoing
          고맙습니다 ^^
          대화보기
          • 김승갑
            APM셋업 쓰시는분들 안될텐데요.. 저도 한참해매서 조금이나마 도움을 드리기위해 글을 남기네요ㅎ
            버전업하셔야합니다. 5.2.7일텐데 5.5버전이상은 APM에서 구동이 안되는거 같아요
            그래서 5.4로 설치하셔야해요 링크남길테니 참고하세요

            그리고 한가지 더 왜그런지는 모르겠는데 egoing님께서 올려주신 password_helper.php를 복사해서 해봤는데 이걸로는 버전업을 해도 안되더라구요
            꼭 gitup가셔서 동영상에서 보여주신대로 namespace가 들어간 소스로 하셔야 잘될겁니다. 이것때문에 4시간정도 해맸네요. 다른분들이 이글 보고 도움받았으면 좋겠어요 ㅎ
            egoing님 매번 강의 감사합니다.

            PHP 업그래이드 방법 http://doodoori2.tistory.com......ade
            PHP 5.4.30 다운로드 http://windows.php.net......zip
            gitup 주소 https://raw.githubusercontent.com......php
          • green5940
            감사합니다 ^^
            대화보기
            • egoing
              반드시 하셔야 합니다. :)
              대화보기
              • egoing
                비밀번호는 꼭 하시길요
                대화보기
                • green5940
                  개인적으로 만든 서비스를 하려고 하는데 유저의 모든 정보를 다 암호화처리해야하나요??
                • 다커스
                  컨트롤러 auth.php에 topic.php처럼
                  생성자에 $this->load->database(); 안넣어줘도 디비접속이 가능하던데
                  왜인지 좀 알려주세욤~
                • 초보개발자
                  mysql에서 제공하는 password함수를 쓰지 않고 php에서 제공하는 password_hash를 쓰시는 이유가 있나요?
                  일반적으로 select과정부터 email = 이메일 and password = passworld("패스워드") 이렇게 verify를 하는 과정을 거쳤었는데.. 그렇게 하지 않고 이렇게 복잡하게 하시는게 어떠한 이점이 있어서 그런 것인지 궁금합니다.

                  mysql 인젝션만 주의한다면 DB상에 mysql 로 암호화된 패스워드를 저장하고 암호화된 패스워드끼리 비교해서 확인하는것도 보안에 문제가 없지 않나요?

                  위 과정은 암호화를 php에서 하는것 말고는 별 차이점이 없어 보이는데 과정은 크게 복잡해 지는것 같아서 여쭤봅니다.
                • 별모모
                  [ 고맙습니다. ] 지금까지는 포기하지 않고 있습니다. 1회독 중인데요.
                  <li><a href="/index.php/auth/register">회원가입</a></li>
                  수업 중에 register.html페이지가 없어도 값이 뜨는 것은 CodeIgniter 프레임워크이기 때문에 가능한 것이군요.
                • 레온
                  Fatal error: Call to undefined function password_hash(); helper가 작동을 안하는건가요?
                  이거 어찌 수정해야하나요? php 5.5.23사용중인대 .......
                • Foo는도대체왜쓰나요
                  정확히 하자면 이런 에러입니다
                  Severity: 4096
                  Message: Object of class stdClass could not be converted to string
                  Filename: controllers/auth.php
                  대화보기
                  • Foo는도대체왜쓰나요
                    깔끔하고 자세한 강의 잘 보고 열심히 공부하고 있습니다
                    그런데 열심히 따라하다가 보니 문제가 생겨서 댓글 남깁니다
                    로그인을 처리하는 과정에서, 일단 사용자 email과 password(해쉬)가 일치하면 제대로 로그인이 됩니다. 그런데 문제는 사용자 email이나 pw가 잘못된 경우입니다.
                    1. email과 pw 모두 틀렸을 경우에는 php에러를 냅니다. object 접근할수 없다면서
                    $user_input['email'] == $user->email && 이 라인에서 오류를 냅니다.
                    2. 더 큰 문제는 email이 일치하고 pw가 틀린 경우에 한해서만 다시 로그인 페이지로 리다이렉션을 시킨다는 겁니다.
                    아직 지식이 얕아서 혼자 해결할 능력이 안되는 상황에, 이 에러는 대충 짐작하기에도 보안에 취약할 것 같다는 생각이 들어서 질문해봅니다.
                    다시한번 강의 감사합니다.
                  • 소원아빠
                    깔끔한 설명!! 정말 매번 감사 합니다.
                  • egoing
                    일단 아래에서 set_flashdata 부분을 읽어보시고 거기서 이해가 안되는 부분을 물어보시면 좀 더 구체적으로 설명드리겠습니다 :) http://codeigniter-kr.org/user...
                    2013/6/18 Disqus <notifications@disqus.net></notifications@disqus.net>
                    대화보기
                    • kanasii
                      $this->session->set_flashdata('message', '로그인에 실패 했습니다.');
                      위 set_flashdata 의 기능이 궁금합니다.
                    • Guest
                      버전이 문제인건지 확실치는 모르겠습니다.
                      실습을 위해 md5로 전환을 하여 로그인은 가능합니다.
                      단, 디비에 없는 아이디와 패스워드를 입력시 오류를 뱉어냅니다.
                      //------------------------------------------------------------------------//if($this->input->post('email') == $user->email && password_verify($this->input->post('password'), $user->password)) {
                      $hash = MD5($this->input->post('password'));if($this->input->post('email') == $user->email && $hash == $user->password) { $this->session->set_userdata('is_login', true); $this->load->helper('url'); redirect("/");} else { $this->session->set_flashdata('message', '로그인에 실패 했습니다.'); $this->load->helper('url'); redirect('/auth/login');}//------------------------------------------------------------------------A PHP Error was encounteredSeverity: NoticeMessage: Trying to get property of non-objectFilename: controllers/auth.php//------------------------------------------------------------------------
                      디비 결과물이 공백이 되어 들어오는거 같아if문에 공백검사까지 넣어봤으나 같은 결과입니다.에러라인은 if문입니다.
                    • kanasii
                      5.2.9p 버전입니다.
                      대화보기
                      • egoing
                        php의 버전을 한번 체크해보셔요. 너무 낮은 버전은 그런 현상이 발생합니다.
                        2013년 6월 17일 월요일에 Disqus님이 작성:
                        대화보기
                        • kanasii
                          초보 질문 올립니다.
                          php버전때문에 말씀하신 password_helper.php 를 작성하고
                          password_hash 구간을
                          $hash = password_hash($password, PASSWORD_BCRYPT);$this->load->model('user_model');$this->user_model->add(array('email'=>$this->input->post('email'),'password'=>$hash,'nickname'=>$this->input->post('nickname')));
                          $this->session->set_flashdata('message', '회원가입에 성공했습니다.');$this->load->helper('url');redirect('/');
                          $hash가 0이 되어 디비에 인서트가 됩니다.password_hash 함수가 잘못된건가요?
                          // 확인해보니 cafe24가 php 5.2.7이네요password_compat 도 사용이 안되는건가요 ㅠ_ㅠ
                          대체방법 좀 부탁드립니다....
                        • egoing
                          나중에 차차로 아시게 될겁니다. ^^
                          2013/4/16 Disqus <notifications@disqus.net></notifications@disqus.net>
                          대화보기
                          • usens
                            답변감사합니다. 제가 초보라서 링크 주신 것을 봐도 도통 사용법을 모르겟네요 ㅠㅠ 친절히 답해주시는데,,,, 넘 어려운거 같아요 ㅠㅠ
                          • usens
                            답변감사합니다. 제가 초보라서 링크 주신 것을 봐도 도통 사용법을 모르겟네요 ㅠㅠ 친절히 답해주시는데,,,, 넘 어려운거 같아요 ㅠㅠ
                          • usens
                            답변감사합니다. 제가 초보라서 링크 주신 것을 봐도 도통 사용법을 모르겟네요 ㅠㅠ 친절히 답해주시는데,,,, 넘 어려운거 같아요 ㅠㅠ
                          • usens
                            답변감사합니다. 제가 초보라서 링크 주신 것을 봐도 도통 사용법을 모르겟네요 ㅠㅠ 친절히 답해주시는데,,,, 넘 어려운거 같아요 ㅠㅠ
                            대화보기
                            • usens
                              답변감사합니다. 제가 초보라서 링크 주신 것을 봐도 도통 사용법을 모르겟네요 ㅠㅠ 친절히 답해주시는데,,,, 넘 어려운거 같아요 ㅠㅠ
                              대화보기
                              • egoing
                                아래 방법도 한번 참고해보셔요~
                                http://stackoverflow.com/a/633...
                                2013/4/16 Disqus <notifications@disqus.net></notifications@disqus.net>
                                대화보기
                                • usens
                                  답변감사합니다! 제가 사용하고 있는 5.2.7 에서는 사용이 불가능 하군요,, 순서대로 보고 따라하는 입장이다 보니,, 이렇게 버전 차이가 앞을 막을 줄 몰랐습니다 ㅠㅠ
                                  대화보기
                                  • egoing
                                    PHP 5.3.7 이상에서 사용 가능합니다. PHP 버전을 한번 확인해보셔용~
                                    대화보기
                                    • usens
                                      잘보고 따라 할려고 노력중입니다. 좋은 자료 인거 같습니다. helper 로드할때 php 5.5 버전 에서 사용하는것인거 같은데 하위 버전에서도 사용 가능한지요? 입력을 하니깐 값이 계속 0 으로 들어가는거 같습니다.
                                    버전 관리
                                    egoing
                                    현재 버전
                                    선택 버전
                                    graphittie 자세히 보기