이제 웹 개발시 보안은 "하면 좋고 안 해도 그만"인 요소가 아니라 고객과 비즈니스를 보호하기 위해서는 필수로 고려해야 하는 가장 중요한 부분중 하나가 되었고 이를 위해 개발팀이 우선적으로 고려해야 할 사항은 시큐어 코딩(Secure coding)입니다.

하지만 시큐어 코딩은 보안에 대한 일정 수준의 이해를 필요로 하며 바쁜 프로젝트 일정상 이를 고려하지 않고 개발이 되는 경우가 많은게 사실입니다.


라라벨은 프레임워크 차원에서 보안을 위해 여러 가지 기능을 제공하고 있으므로 주요 취약점들에 대해 어렵지 않게 대응이 가능합니다.


public 폴더 노출 최소화 및 설정 파일 숨김

기존의 PHP 웹 애플리케이션들은 애플리케이션이 파일 시스템과 매칭되는 경우가 많았고 이로 인해 웹 애플리케이션 구동에 필요한 설정 파일(DB 연결 정보, 파일 경로등)도 웹 서버에 위치시키는 경우가 많았습니다.

 


예로 다음 예제처럼 DB 연결 정보를 담고 있는 설정 파일을 require 로 사용할 경우를 생각해 봅시다.

<?php
 
require_once('conf/db_conn.inc');
 
connect_db(); 
?>
PHP

 

이 경우 브라우저에서 example.com/conf/db_conn.inc 를 바로 호출할 경우 DB 계정 정보가 노출되는 등의 보안 취약점이 존재할 위험이 있습니다.

이를 막기 위해서는 웹 서버가 특정 확장자(예: .inc, .htaccess)는 서비스하지 않도록 별도의 보안 설정을 해주거나 또는 웹 서버의 DocumentRoot 이외의 경로에 db_conn.inc 를 두고 애플리케이션에서 절대 경로로 읽어 들여야 합니다.

 

 이런 보안 취약점이 발생할 수 있는 여지를 차단하고자 라라벨은 구동에 필요한 설정 파일을 웹 서버의 DocumentRoot 와 격리하였으므로 설정 파일 유출에 따른 보안 문제를 최소화해 줍니다.


.php 의 직접 호출이 아닌 애플리케이션 라우팅을 통해 서비스를 제공하도록 설계되었고 public 폴더에는 단 하나의 PHP(index.php) 파일만 존재합니다.

이를 통해 public 폴더의 노출을 최소화하였고 .php 파일 직접 호출을 통한 공격이나 설정 파일을 브라우저에서 다운로드 받을 수 있는 취약점 발생 가능성을 최소화하였습니다.


SQL 삽입(SQL Injection) 공격

만약 사용자 id와 암호를 입력받아서 로그인을 처리하는 페이지가 있을 경우 SQL Injection 을 고려하지 않을 경우 다음과 같이 코딩하게 됩니다.

$sql="SELECT * FROM users WHERE userid='$userid' and password='$password'";

$result=mysql_query($sql);
$count=mysql_num_rows($result);

if($count==1){
    //login 성공
}
else {
    //login 실패
}
PHP

PHP 7 에서는 mysql 익스텐션이 삭제되었으므로 mysql_* 로 시작하는 함수들을 더 이상 사용할 수 없으며 대신 mysqli 나 pdo_mysql을 사용해야 합니다.


위와 같은 코드는 클라이언트가 보낸 문자열을 검증없이 사용하므로 공격자가 password 에  ' or '1' = '1 라는 문자열을 넣으면 or 뒤의 문장이 참이므로 id와 암호를 몰라도 관리자로 로그인이 가능해져 버립니다.


라라벨은 이를 막기위해 모든 파라미터를 문자열로 처리하지 않고 PDO 변수에 바인딩하여 처리하므로  대부분의 SQL Injection 공격을 막아낼 수 있습니다.


라라벨의 DB 기능을 사용하여 사용자의 id 와 암호를 조회하기 위해 다음과 같이 코드를 작성했다고 가정해 보겠습니다.

 DB::select('select * from users where id = :userid and password = :password', ['userid' => 'myid', 'password' => 'mypasswd']);
PHP


라라벨에서 DB 쿼리를 생성하는 기능을 제공하는 Query Builder  는 파라미터를 내부적으로 준비된 문장(Prepared Statement)와 매개변수 바인딩(Bound Parameter) 를 통해 다음과 같이 안전하게 실행됩니다.

$s = $dbh->prepare('SELECT * FROM users WHERE userid = :userid and password = :password') ;
$s->bindParam(':userid ', $userid );
$s->bindParam(':password', $password); 
PHP

이제 공격자가 SQL 삽입 공격을 시도해도 입력한 문자는 이미 해석이 끝난 SQL 문에 파라미터로 전달되므로 aaa' or '1' = '1 를 입력해도 password 컬럼의 값이 aaa' or '1'='1' 인 레코드를 찾게되므로 개발자가 특별히 신경쓰지 않아도 기본적으로 SQL 삽입 공격을 방지할 수 있습니다.


그리고 사이트간 스트립팅(CSS 또는 XSS; Cross-site Scripting) 공격을 막기위해 view 단에 출력하는 데이타는 htmlentities() 함수로 변환하고 있으며(블레이드 템플릿에서 설명합니다.) 웹 애플리케이션의 가장 취약한 부분중 하나인 사이트간 요청 위조(CSRF; Cross-site request Forgery) 를 막기 위해 기본적으로 POST, DELETE, PUT 같이 서버의 데이타를 수정하거나 삭제할 가능성이 있는 액션에는 CSRF 토큰을 삽입하고 검증하고 있습니다.

이는 라라벨의 기본 기능이므로 애플리케이션 개발자는 VIEW 단에 CSRF 를 출력하는 코드인 csrf_token(); 만 추가하면 서버단에서 별도의 CSRF 검증 코드를 구현하지 않아도 CSRF 공격에 견고한 서비스를 만들 수 있습니다.


또 세션을 가로채서 공격하는 HTTP Session Hijacking 을 막기 위해 모든 세션 데이타는 강력한 알고리즘으로 암호화되어 저장됩니다.


라라벨이 제공하는 보안 기능에 대해서는 뒷 장 "세션과 보안/인증" 에서 자세히 살펴 보겠습니다.