대량 할당(Mass Assignment)이란

전 절에서 설명한 데이타 삽입 및 갱신은 모델의 프로퍼티에 $task->name = 'Name'; 과 같이 값을 할당하고 save() 를 호출하는 방법이며 의도한 대로 잘 동작했습니다.


웹 애플리케이션은 사용자의 입력값을 데이타베이스에 저장하거나 데이타베이스에 있는 내용을 화면에 보여주는 작업이 많으며 HTML 폼이나 AJAX 요청등을 통해 사용자의 입력값은 서버로 전송되며 보통 배열로 변환되어 애플리케이션에 전달됩니다.

위와 같은 프로퍼티 방식은 배열의 요소를 일일이 풀어서 모델의 프로퍼티에 할당해야 하는 불편함이 있으므로 바로 배열을 전달할수 있으면 좋을텐데 왜 대량 할당 예외가 발생했을까요?


원인은 바로 애플리케이션 보안때문입니다.

사용자가 배열로 전달한 입력값을 바로 Eloquent 에 전달하여 처리하는 것은 매우 쉽지만 큰 보안상의 문제가 있습니다. 그것은 사용자가 악의적인 의도를 가졌을 지도 모르기 때문에 모든 입력값을 검증해야 하지만 위와 같이 사용자의 입력값을 배열로 받아서 바로 사용하면 입력 값 검증이 안 된다는 점입니다.


예로 사용자 정보 테이블에 사용자가 관리자인지 여부를 설정하는 is_root 이라는 boolean 타입의 컬럼이 있을 경우 공격자는 사용자의 정보를 수정하는 기능을 수행하면서 입력값으로 is_root=true 를 설정하여 서버에 전송하면 위와 같이 배열을 바로 모델에 반영한다면 공격자는 간단하게 관리자 권한을 획득할 수도 있습니다.


이런 위험한 상황을 방지하기 위해 Eloquent 는 모델에 배열을 직접 전달하여 데이타베이스를 변경할 때 개발자가 명시적으로 입력이 가능한 컬럼을 지정해 주어야 합니다.

변경이 허락되지 않은 컬럼들에 대해서 값을 대량 할당할 경우 발생하는 예외가 MassAssignmentException 이며 컬럼들에 대해 대량 할당을 가능하게 하는 것은 두 가지 방식이 있습니다.

fillable - 화이트 리스트 방식

모델의 $fillable 배열에 입력이 가능한 컬럼을 명시적으로 지정하는 방식입니다. 다음은 name, project_id 컬럼을 입력 가능하게 하는 설정입니다. 명시적으로 정의된 컬럼외에는 배열에 값이 있어도 무시하게 되므로 description 에 설정한 값은 입력되지 않습니다.

class Task extends Model
{
    protected $fillable = ['name', 'project_id'];
}
CODE

만약 모든 컬럼에 값을 설정 가능하게 하려면 다음과 같이 '*' 를 설정해 주면 되지만 기본키나 암호 컬럼같이 민감한 컬럼을 덮어쓸 우려가 있으므로 일반적으로는 권장하지는 않습니다.

class Task extends Model
{
    protected $fillable = ['*'];
}
CODE

guarded - 블랙 리스트 방식

모델의 $guarded 변수는 보호해야 할 컬럼을 명시적으로 지정하는 방식으로 여기에 지정되지 않은 컬럼은 모두 값 설정이 가능해 집니다. 다음은 description 항목을 보호하는 설정으로 배열에 있는 해당 값은 데이타베이스에 반영되지 않습니다.

class Task extends Model
{
    protected $guarded = ['description'];
}
CODE

하나의 모델에서 $fillable 과 $guarded 는 모두 사용할 수 없으며 한 가지 방식만 사용해야 합니다.


이제 위 예제를 화이트 리스트 방식을 사용하여 대량 할당을 처리하기 위해 Task 모델의 $fillable 배열 변수에 허용할 컬럼명을 설정해 보겠습니다.

class Task extends Model
{
    protected $fillable = ['name', 'project_id'];
}
CODE


이제 모델의 삽입을 save() 가 아닌 create() 를 사용하여 처리하기 위해 InsertMass() 메소드를 만들고 아래와 같이 create() 에 $param 배열을 전달합니다.

public function getInsertMass()
{
    $param = [
        'name' => '예제 작성',
        'project_id' => 1,
        'description' => 'insert mass 예제 작성',
       ];

    $task = Task::create($param);

    return response()->json($task,
         200, [], JSON_PRETTY_PRINT);
}
CODE


이제 브라우저로 http://homestead.app/orm/insert-mass 에 연결하면 정상적으로 모델이 입력되는것을 확인할 수 있으며 $fillable 에 없는 description 컬럼은 배열에 값이 있었어도 제외시킨 것을 알 수 있습니다.



Eloquent 내부적으로 다음 조건을 만족할 때 MassAssignmentException 예외를 던지며 이 조건은 Illuminate/Database/Eloquent/Model.php totallyGuarded() 에 정의되어 있습니다.

  • $fillable 배열의 요소 갯수가 0 이고
  • $guarded 배열이 [*] 로 설정