반응형

7. 사용자와 정보 주고받기: 웹 폼 제작

서버 변수

  • 자동 전역변수인 $_SERVER에는 PHP_SELF, REQUEST_METHOD처럼 웹 서버와 현재 요청에 대한 정보를 제공하는 유용한 원소가 많음
    • PHP_SELF : 현재 요청한 URL의 일부 경로명
    • REQUEST_METHOD : 브라우저가 어떤 HTTP 메서드로 요청했는 지
    • QUERY_STRING : 전체 URL에서 ? 이후 쿼리스트링
    • PATH_INFO
    • SERVER_NAME : 도메인명
    • DOCUMENT_ROOT : 웹사이트 문서가 위치한 웹수 서버 컴퓨터의 디렉토리
    • REMOTE_ADDR : 웹 서버로 요청한 클라이언트의 IP
    • REMOTE_HOST : 웹 서버로 요청한 클라이언트의 IP를 호스트명으로 전환한 값. 하지만 웹 서버가 이 전환 작업을 수행하는 비용이 비교적 높으므로 대부분의 웹 서버는 이 작업을 하지 않음
    • HTTP_REFERER : 누군가 링크를 클릭하여 현재 URL로 들어왔을 때, 링크가 있던 페이지의 URL이 저장됨. 변조가 가능하므로 접근 권한 판별 용도로 사용 금지
    • HTTP_USER_AGENT : 페이지를 요청한 웹 브라우저의 정보
  • $_POST역 자동 전역 변수는 제출된 폼 데이터가 저장되는 배열임. 배열의 키는 폼의 각 요소들의 이름으로 텍스트박스의 name 속성의 이름이 'my_name'이라면 $_POST['my_name']에 값이 저장됨

요소 검증

  • 널 병합 연산자(??)는 PHP 7+. 왼쪽 인수가 빈 문자열이면 우측 인수 반환
  • 클라이언트에서 요소명에 []를 붙이면 배열로 인지함. 서버에서 읽을 때는 []가 없는 요소명을 읽음
  • filter_input(전체 데이터, 타겟항목, 검증필터, 상세옵션) : 필터를 통해 값을 검증함
    • 네번째 인자인 상세옵션은, options라는 키를 가진 단일 배열임 : array('options' => array('min_range' =>2 , 'max_range' => 10))
    • 정수 필터(FILTER_VALIDATE_INT)는 min_range, max_range 와 같은 범위 옵션 지정 가능
    • 부동소수점 필터(FILTER_VALIDATE_FLOAT)는 범위 옵션을 지원하지 않기 때문에 부동 소수점은 직접 범위를 비교해야함 (0.00001의 오차 범위 주의)
  • 항등 연산자(===) : 두 값이 동일하고, 자료형도 동일하면 true. 동등 연산자(==)로 0 == false 비교 시 true가 반환되나, 항등 연산자는 false를 반환
  • INPUT_POST는 제출된 폼 데이터 배열의 집합? // TODO 확인
  • list($a, $b) = [[1,2], [3,4]] : list() 함수를 통해 배열을 각 변수에 할당 가능. 약간 (...) 느낌인건가? //TODO list() 함수 확인

HTML과 자바스크립트 (크로스 사이트 스크립팅 방어)

  • 브라우저와 HTML : 브라우저는 <b>HTML 태그</b>가 텍스트인지 html 태그인지 구문할 수 없음. 이를 위해 텍스트에 인코딩을 적용하여 브라우저가 < 같은 문자를 텍스트로 읽을 수 있도록 함. 브라우저는 &lt;과 같은 값을 디코딩하여 정상적인 텍스트로 출력함
  • htmlentities() : 텍스트 중 HTML 태그를 인코딩함
  • strip_tags() : 텍스트 중 HTML 태그를 제거함 (위험)

 

8. 정보 저장: 데이터베이스

PDO와 데이터 조작

  • PHP 데이터 오브젝트(PDO, PHP Data Objects) : 데이터베이스 프로그램 추상화 계층. Php 내장 기능
  • PDO 인수 설명
    • 첫번째 인자 : 데이터 원본 네임(DSN, Data Source Name), key=value 형태로 세미콜론으로 이어붙임
    • 두번째 인자 : 사용자명
    • 세번째 인자 : 패스워드
  • PDO 오류모드 3가지 : 예외, 침묵, 경고
    • 예외 : $db->setAttribute(PDO::ATTR_ERRMODE, EPD::ERRMODE_EXCEPTION)으로 설정하면 데이터베이스와 관련된 모든 문제를 빠짐없이 점검 가능. 이 모드는 PDO가 예외처리 하지 않으면 프로그램 실행이 중단됨
    • 침묵 : 기본 상태로, PDO 메서드는 실행 중 문제가 생기면 false를 반환한다. 그래서 기본 침묵 모드에서는 exec()의 실행결과를 확인 후 false라면 PDO의 errorInfo() 메서를 이용해 오류 정보를 얻을 수 있음
    • 경고 : $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING)을 지정하면 경고모드로 실행. 이 모드는 침묵 모드와 마찬가지로 예외를 발생시키지 않으며 오류 발생 시 false를 반환하지만 PHP 엔진이 경고 수준의 오류 메시지 생성함. 이 메시지는 오류 처리 설정에 따라 화면 또는 로그파일로 출력됨. 12장에서 오류메시지가 출력될 위치를 제어하는 방법 안내
  • exec()의 반환값 : 오류 시 false를 반환하나,쿼리가 성공적으로 실행되어도 반영된 로우가 없으면 0을 반환하므로 반드시 삼중 등호(===)를 이용해 검증해야함
  • 데이터 삽입 보안 : Prepared 구문을 통해 쿼리 실행을 두 단계로 분리하여, 원치않은 sql 주입 공격에 방어할 수 있음

데이터 조회

  • query() : 데이터 조회를 위한 메서드
  • fetch() : 조회결과가 존재하면 데이터에 대한 배열, 더이상 존재하지 않으면 거짓 반환. 배열에 대한 접근은 컬럼명 혹은 인덱스 순서로 가능
  • rowCount() : 쿼리가 반환하는 모든 로우를 가져와서 개수를 셈 (대용량 데이터 조회 시에는 native count(*) 쿼리 사용하는 게 나음)
  • fetchAl() : 조회한 데이터를 한번에 가져와 배열에 넣음
  • 쿼리 결과가 단 건일 경우 : query()와 fetch() 메서드를 한번에 사용할 수 있음. ex) $db->query(‘쿼리문’)->fetch(); //TODO 마찬가지로 fetchAll()도 가능한거 아닌가?
  • 데이터 반환 배열의 키 형식 변경 : PDO는 데이터를 배열로 반환할 때 기본적으로 키 유형을 숫자키와 문지열키 방식을 제공하는데, 설정에 따라 숫자키, 문자열키, 객체로 지정할 수 있음
    • fetch(), fetchAll() 단위로 변경 : PDO fetch(), fetchAll()할 때 첫번째 인수에 원하는 방식을 전달할 수 있음. 숫자키 배열(PDO::FETCH_NUM), 문자키 배열(PDO::FETCH_ASSOC), 객체(PDO::FETCH_OBJ) 중 하나의 형식으로 설정가능
    • 쿼리 단위로 변경(setFetchMode()) : 하나의 쿼리에 대해 기본 방식 설정을 변경하려면 PDOStatement 객체의 setFetchMode()를 호출함 
    • $q = $db->query(‘쿼리문’); $q->setFetchMode(PDO::FETCH_NUM);
    • 데이터베이스 접속 객체 단위로 변경(setAttribute()를 설정하면 setFetchMode(), fetch()에 아무 값도 넣을 필요 없음)
      $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO:GETCH_NUM);
  • SQL 안전하게 전달하기
    • PHP 내장함수 strtr() : SQL의 와일드카드인 %와 _를 역슬래시로 이스케이프함
    • PDO queto() : 문자열을 따옴표로 감싸서 처리
    • 예제
      $dish = $db->quote($_POST{‘dish_name’]);
       $dish = strtr($dish, array(‘_’ => ‘\_’, ‘%’ => ‘\%’)); 
      $db->exec(“UPDATE ~~~ WHERE dish_name LIKE $dish”);
    • //TODO 왜 queto(), strtr()를 사용할 때에는 ? 자리지시자 사용못하고 변수명 써야하지??

 

9. 파일 다루기

  • 생략

 

10. 사용자 추적: 쿠키와 세션

쿠키

  • 쿠키 설정(setcookie()) : 웹 클라이언트에게 쿠키를 저장하라고 헤더 생성
  • 서버 전역변수 $_COOKIE : 웹 브라우저가 보낸 쿠키 내용이 담김
  • setcookie()를 통해서 웹 클라이언트에게 쿠키를 전송하기 전에 값을 URL 인코딩 방식으로 변환한다. PHP가 쿠기값을 인코딩하지 않게 하려면 setrawcookie()를 사용할 수 있으나 ‘=‘, ‘,’, ‘;’ 등을 비롯해 그 어떤 화이트 스페이스도 쿠키 값에 사용할 수 없음
  • setcoocke()의 위치 : 페이지가 아무런 출력을 하지 않았을 때 호출되어야 함. 페이지의 시작 태그인 <?php 바로 뒤에 나와야함
  • 쿠키의 수명 : 기본적으로 쿠키 수명은 웹 클라이언트의 수명을 따르며, 웹 브라우저 종료 시 쿠키도 함께 삭제됨. 0을 입력하면 기본값을 사용하겠다는 의미. 쿠키의 만료시간을 변경하려면 setcookie()의 세 번째 인수에 만료 시각을 설정하면 됨. 유닉스 타임스탬프 넣으면 됨
  • 쿠키의 적용 범위와 경로
    • 범위 : 쿠키는 쿠키를 설정한 페이지의 하위 디렉토리에 있는 페이지를 요청할 때도 함께 전송됨. 그러나 다른 디렉터리에 있는 페이지 요청시에는 쿠키가 전송되지 않음
    • 경로 : URL에서 호스트명 뒷부분을 경로(path)라고 부름. 쿠키가 설정한 경로와 일치하지 않는 다른 경로에도 쿠키를 보내려면 네번째 인수로 허용 경로를 전달
  • 쿠키의 도메인 : setcoocke()의 다섯번째 인수. 기본값은 쿠키를 설정했던 호스트와 같은 호스트의 요청에만 쿠키를 전송함. 예를 들어 다섯번째 인수에 ‘.example.com’를 입력하면 ‘dev.example.com’, ‘prod.example.com’처럼 호스트명이 입력된 값으로 끝나는 서버로 요청할 때 쿠키를 전송함. setcookie()에서 지정한 도메인은 서버명의 끝부분과 반드시 일치해야함. 호스팅되는 서버와 쿠키에 설정한 도메인명이 일치하지 않으면 해당 서버로 전송하지 않음. 예를 들어 쿠키 도메인을 ‘.naver.com’으로 한다고 해도 호스팅되는 서버가 ‘example.com’이라면 그 쿠키를 ‘.naver.com’ 도메인의 모든 서버로 전송되지는 않음
  • HTTPS : setcookie()의 여섯번째 인수에 true 값을 전달하면 웹 클라이언트는 오직 URL이 https로 시작하는 보안 연결 요청에만 쿠키를 전송함. 이 경우 setcookie()를 실행하는 페이지에 대한 요청이 보안 연결을 통해서만 이루어지는지 확인해야 하며, 이어지는 요청이 보안 연결이 아닐 경우 쿠키가 전송되지 않는다는 점을 유의
  • HttpOnly : 일곱번째 인수에 true를 전달하면 웹 클라이언트가 HttpOnly 쿠키를 생성함. 이 쿠키는 일반 쿠키처럼 클라이언트와 서버 사이를 옥고가지만, 클라이언트의 자바스크립트가 접근할 수 없음. 이 설정은 크로스 사이트 스크립팅 공격으로부터 쿠키를 보호함
  • 쿠키 삭제 : 쿠키 값에 빈 문자열을 넣음. 단, 쿠키 설정 시 만료 시각이나 도메인을 기본값이 아닌 특정한 값으로 설정한 경우 삭제할 때도 동일한 설정값을 사용해야 올바르게 삭제됨. 대부분의 경우 쿠키의 만료시각, 경로, 도메인을 기본값으로 설정해도 무난함

세션

  • 기본적으로 세션은 PHPSESSID라는 쿠키를 사용함. PHP 엔진은 세션을 시작할 때 이 쿠키가 있는지 확인하고 존재하지 않으면 생성함. 이 값은 임의의 알파벳과 숫자가 섞인 문자열임. 각 웹 클라이언트는 서로 다른 세션 아이디를 부여받음
  • 서버는 PHPSESSID 쿠키에 담긴 세션 아이디를 통해 고유한 웹 클라이언트를 식별하고, 이를 통해 엔진은 각 웹 클라이언트 별로 데이터 집합을 분리하고 유지함
  • 세션 활성화 : 페이지에서 세션을 사용하려면 스크립트가 시작되는 지점에서 session_start()를 호출해야함. 이 함수는 setcookie()처럼 출력이 전송되기 전에 호출해야함. session.auto_start를 On으로 설정하면 모든 페이지에서 세션을 사용할 수 있으며, ,일일이 session_start()를 호출할 필요가 없음
  • 세션 데이터는 자동 전역변수인 $_SESSION 배열에 저장됨
  • 서버는 $_SESSION 배열의 값을 읽어 사용자가 특정 페이지를 몇 번 봤는 지, 현재까지의 열람 횟수를 알 수 있음
  • 클라이언트 요청의 마지막 단계에서 $_SESSION의 정보가 웹 서버에 파일로 저장됨. 이 파일은 세션 아이디별로 생성됨
  • 프로그램이 실행되는 동안 $_SESSION은 PHPSESSID 쿠키에 담긴 세션 아이디에 대응하는 단 하나의 데이터만 담음. 이 세션을 활성화된 세션이라고 함
  • $_SESSION 변수는 쿠키와 달리 일반적인 배열처럼 다룰 수 있음. $_SESSION[‘order’][] 문법은 $_SESSION[‘order’]를 배열로 지정하고 새로운 원소를 마지막에 추가하라는 의미임
  • 세션의 지속시간
    • session.gc_maxlifetime : 기본 지속시간은 24분이며, 이 시간 내에 한번이라도 세션 데이터에 접근하면 계속 보존됨. session.gc_maxlifetime 설정 지시자에 초를 입력하여 유지기간을 변경할 수 있음. 이 값은 서버 설정에서 변경하거나 프로긂에서 ini_set() 함수를 호출해 변경할 수 있음 (ini_set()함수 호출 시 session_start()보다 먼저 호출되야함)
    • 세션의 삭제 과정 : 세션을 사용하는 요청이 시작될 때, PHP 엔진은 1%의 확률로 세션을 정리하며, 전체 서버에 존재하는 세션을 검색하고 만료된 세션을 삭제함. 1%는 다분히 임의적인 수치지만 그 임의성이 프로그램의 효율을 높임. 많은 요청으로 분주한 사이트에서 매 요청마다 만료된 세션을 검색하고 파기하기에는 서버의 자원이 너무 많이 소모됨. 만료된 세션을 즉각적으로 제거하려면 1%라는 확률에 머물러 있을 필요가 없다. session.gc_probability 설정 지시자는 요청이 시작될 때 만료 세션 제거 작업을실행할 확률을 제어함. 요청이 실행될 때 마다 만료 세션 제거 작업을 실행하려면 이 값을 100으로 설정한다. 단 이 설정은 session_start() 전에 호출해야함 //TODO 이해못함
    • session.auto_start 설정 지시자로 세션을 활성화하면 ini+set() 함수로 session.gc_maxlifetime이나 session.gc_probability의 값을 바꿀 수 없으며 서버 설정 자체를 변경ㅇ해야함
    • 사용자의 세션 아이디를 저장하는 쿠키(PHPSESSID)도 설정 매개변수를 통해 속성 변경 가능함 (ex: session.name=‘PHPSESSID’, session.cookie_lifetime=0 등)

로그인과 사용자 식별

  • 세션은 특정 사용자와 익명의 관계를 수립함
  • 세션 기반 로그인 방식은 다음과 같은 단계로 이루어짐
    • 사용자명과 비밀번호를 요청하는 폼 출력
    • 폼 제출 확인
    • (제출된 비밀번호가 맞다면) 사용자명을 세션에 추가
    • 인증된 사용자만 할 수 있는 작업에서 세션의 사용자명 확인
    • 사용자가 로그아웃하면 세션의 사용자명 삭제
  • 로그아웃은 일반적인 배열처럼 unset()을 사용하여 $_SESSION에서 키와 값을 제거할 수 있음

비밀번호 해시화

  • PHP의 password_hash() : 값을 해시 문자열로 변경 (PHP 5.5.0+)
  • PHP의 password_verify() : 사용자가 제출한 평문 비밀번호를 해시화하여 해시 비밀번호와 비교함. 두 해시가 일치하면 true 반환 (PHP 5.5.5+)
  • PHP 5.5.5 버전 미만에서는 password_compat 라이브러리를 통해 위 함수들을 사용 가능함

출력 버퍼링(output buffering)

  • 앞서 말했듯, setcookie()나 session_start()를 호출하기 전에 출력이 전송되면 PHP 엔진은 오류 메시지를 출력함. 이와 같은 함수들은 서버의 응답에 헤더를 추가하는데, 헤더는 모든 출력에 앞서 추가되어야만 올바르게 전송됨. 때문에 print 구문이나 블럭 외부의 HTML보다 먼저 이 함수들을 호출해야함. 만약 ‘headers already sent’ 오류 메시지가 나오면 문제를 일으킨 출력이 어디서 발생하는 지 코드를 자세히 조사해야함. setcookie()나 session_start() 전에 print 구문이 없는지, 페이지에서 블럭 외부에는 심지어 빈 줄도 있으면 안된다.
  • 출력 버퍼링은 이런 모든 점검과 빈 줄까지 완전히 해결할 방안임. 출력 버퍼링을 사용하면 PHP 엔진은ㅇ 요청 처리가 완전히 끝날ㄹ 떄 까지 모든 출력 전송을 대기시킴. 설정된 모든 헤더를 전송하고 나면 대기시켰던 출력을 이어서 전송함. 출력 버퍼링을 작동시키려면 서버 설정에서 output_buffering 설정 지시자를 On으로 지정하면 됨. 출력 버퍼링을 사용하면 웹 클라이언트가 페이지 내용을 얻기 전까지 아주 약간의 시간이 더 소요되지만, 코드를 고치는 데 들어갈 수 있는 수 많은 시간을 절약할 수 있음
반응형

'Programming > PHP' 카테고리의 다른 글

[책] Learning PHP 챕터 4~6 요약  (0) 2021.06.30
[책] Learning PHP 챕터 1~3 요약  (1) 2021.06.29