728x90

PHP Connect to MySQL

- MySQL (procedural) : MySQL DB에서만 동작하며, PHP 7.0 이상에선 동작안됨. PHP 5.5에서 deprecated

- MySQLi (procedural) : MySQL DB에서만 동작한다.

- MySQLi (object-oriented) : MySQL DB에서만 동작한다.
- PDO(PHP Data Objects) :
Oracle, PostgreSQL, SQLite, MySQL 등 12가지의 DB에서 동작한다.


MySQLi 기반 Prepared Statements PHP 연동 방식이 SQL 인젝션에 강하다고 되어 있다.

그러면 절차지향 방식의 MySQLi 방식에서는 어떻게 보강하면 될까?

$userid = mysqli_real_escape_string($db, $userid);

와 같이 mysqli_real_escape_string 를 써주면 된다.

해킹 대비를 위해서는 INPUT 해킹 시도(SQL Injection), 출력 해킹 시도(Cross Site Scripting) 둘다 신경써야 한다.


기존 사용하던 MySQLi (procedural) 대신에 MySQLi (object-oriented) 로 연습을 해봤다.


https://www.androidhive.info/2012/01/android-login-and-registration-with-php-mysql-and-sqlite 에서 파일을 다운로드하여 좀 수정했다.


테스트 환경

- Windows10 기반 Autoset9 (PHP 5.6.0, MySQL 5.6.20) : 정상

- Windows10 기반 Autoset10(Apache 2.4.17, PHP 7.0, MariaDB 10.1.9 ) : 정상


리눅스 기반 PHP 5.6.30, MySQL 5.6.30 에서 테스트를 하다보니 일부 함수가 문제가 발생하는 걸 알았다.

mysqlnd 가 설치되지 않아서였더라.

phpize로 추가 설치하는 방법을 검색해봤는데 아직 해결책을 못찾았다.

정 안되면 APM(Apache + PHP + MySQL) 소스 설치를 다시 해볼 생각이다.


- SHA1 패스워드를 적용한 알고리즘이 좋더라. 그래서 이걸 수정/추가해서 코드를 보완

- 아이폰/안드로이드폰에서 접속시 폰의 고유장치번호를 인식하여 최초 등록 단말이 아닌 경우 로그인 불가처리

- 테이블명을 변경하고 칼럼을 추가하고 user 권한을 설정하는 걸 추가


테이블 구조

- phpMyAdmin SQL에서 아래코드를 붙여넣기 하면 자동으로 DB명과 테이블을 생성한다.

- DB명과 user 권한을 수정해서 이용하면 된다.


DB_Functions 클래스 및 DB 접속정보는 첨부파일 참조

- DB_Functions 클래스가 핵심 내용이다.

 <?php

class DB_Functions {
    private $conn;

    // 생성자
    function __construct() {
        require_once 'dbconnect.php';
        // DB 연결
        $db = new Db_Connect();
        $this->conn = $db->connect();
    }

    // 소멸자(destructor)
    function __destruct() {

    }

    // 회원 정보 신규 입력
    public function storeUser($userID, $name, $email, $password, $telNO, $mobileNO) {
        $hash = $this->hashSSHA($password);
        $encrypted_password = $hash['encrypted']; // encrypted password
        $salt = $hash['salt']; // salt

        $stmt = $this->conn->prepare("INSERT INTO members(userID, userNM, email, passwd, salt, telNO,mobileNO,created_at) VALUES(?, ?, ?, ?, ?, ?, ?, NOW())");
        $stmt->bind_param("sssssss", $userID, $name, $email, $encrypted_password, $salt, $telNO, $mobileNO);
        $result = $stmt->execute();
        $stmt->close();

        // check for successful store
        if ($result) {
            $stmt = $this->conn->prepare("SELECT * FROM members WHERE userID = ?");
            $stmt->bind_param("s", $userID);
            $stmt->execute();
            $user = $stmt->get_result()->fetch_assoc();
            $stmt->close();

            return $user;
        } else {
            return false;
        }
    }

    // 로그인 체크
    public function getUser($userID, $password) {
        $stmt = $this->conn->prepare("SELECT * FROM members WHERE userID = ?");
        $stmt->bind_param("s", $userID);

        if ($stmt->execute()) {
            $user = $stmt->get_result()->fetch_assoc();
            $stmt->close();

            // verifying user password
            $salt = $user['salt'];
            $encrypted_password = $user['passwd'];
            $hash = $this->checkhashSSHA($salt, $password);
            // check for password equality
            if ($encrypted_password == $hash) {
                // user authentication details are correct
                return $user;
            }
        } else {
            return NULL;
        }
    }

    // 안드로이드/아이폰 로그인 체크
    public function LoginUserChk($userID,$password,$deviceID){
        if(empty($userID) || empty($password)){
            return 0;
        } else {
            $user = $this->getUser($userID, $password);
            if($user['idx']>0){
                // 장치 일련번호 체크
                if($user['phoneSE'] == NULL){
                    // 신규 장비번호 입력(최초 로그인)
                    $this->LoginUserEquipInput($userID,$deviceID);
                    return $user['idx'];
                } else {
                    if($user['phoneSE'] === $deviceID){
                        return 1; // 일련번호 일치
                    } else {
                        return -1; //일련번호 불일치
                    }
                }
            } else {
                return 0; //계정오류
            }
        }

    }

    // 장치번호 업데이트
    public function LoginUserEquipInput($userID,$deviceID){
        if(strlen($deviceID)>0 && is_numeric($deviceID)){ // 안드로이드폰
            $ostype = 2;
        } else if(strlen($deviceID)>30){ // 아이폰
            $ostype = 1;
        } else { // 기타
            $ostype = 0;
        }

        $sql='update members set phoneSE=?, OStype=? where userID=?';
        $stmt = $this->conn->prepare($sql);
        $stmt->bind_param("sss", $deviceID, $ostype, $userID);
        $status = $stmt->execute();
        if($status == true){
            return 1;
        } else {
            return 0;
        }
    }//end

    // 장치번호 초기화
    public function EquipReset($userID){
        $ostype = 0;
        $sql='update members set phoneSE=NULL, OStype=? where userID=?';
        $stmt = $this->conn->prepare($sql);
        $stmt->bind_param("ss", $ostype, $userID);
        $status = $stmt->execute();
        if($status == true){
            return 1;
        } else {
            return 0;
        }
    }//end

    // 회원 가입 여부 체크
    public function isUserExisted($userID) {
        $stmt = $this->conn->prepare("SELECT userID from members WHERE userID = ?");

        $stmt->bind_param("s", $userID);
        $stmt->execute();
        //$stmt->store_result();
        $result = $stmt->get_result();

        if ($result->num_rows > 0) {
            // user existed
            $stmt->free_result();
            $stmt->close();
            return true;
        } else {
            // user not existed
            $stmt->free_result();
            $stmt->close();
            return false;
        }
    }

    // 회원 정보 삭제
    public function deleteUser($userID){
        $stmt = $this->conn->prepare("delete FROM members WHERE userID = ?");
        $stmt->bind_param("s", $userID);
        $stmt->execute();
        $stmt->close();
    }

    public function hashSSHA($password) {

        $salt = sha1(rand());
        $salt = substr($salt, 0, 10);
        $encrypted = base64_encode(sha1($password . $salt, true) . $salt);
        $hash = array("salt" => $salt, "encrypted" => $encrypted);
        return $hash;
    }

    public function checkhashSSHA($salt, $password) {
        $hash = base64_encode(sha1($password . $salt, true) . $salt);
        return $hash;
    }

}

?>


- user 권한 및 패스워드 설정을 다르게 한 경우에는 해당 파일을 수정해야 한다.


register.php

- 안드로이드에서 전송된 데이터라고 가정하고 값을 직접 입력하여 DB에 잘 저장되는지 테스트했다.


json 으로 넘길 데이터 형태는 개발자가 변경하고 싶은데로 변경하여 사용하면 된다.


login.php


등록된 폰과 다를 경우 관리자에게 문의하여 장치번호를 초기화하고 새로운 폰으로 다시 등록해야만 로그인이 가능해진다.


안드로이드 코드 부분은 다음에 기회되면 작성할 예정이다.

이미 기존에 로그인 처리하는 코드 예제가 있으므로 그걸 수정해서 활용하거나 PHP 코드를 일부 수정해서 사용해도 된다.


androidPHP_Login_and_Registration.zip



PDO 개념 및  사용법 : http://link2me.tistory.com/1332

PDO DB 연결방법 : http://link2me.tistory.com/1398

PDO MemberClass : http://link2me.tistory.com/1399

MVC 패턴 고려한 PDO Class 만들기 : http://link2me.tistory.com/1401

블로그 이미지

Link2Me

,