31. Bảo mật và tối ưu WordPress Meta boxes

Như vậy sau khi kết thúc bài Tương tác database với WordPress Metaboxes thì tôi đã hướng dẫn bạn cách tạo ra một box mới với các ô textbox dùng để nhập liệu và nạp dữ liệu vào database. Nhưng như vậy vẫn chưa xong đâu đấy, chúng ta vẫn còn thiếu 4 điều kiện bảo mật để hợp chuẩn do WordPress đề ra. Bài viết này chỉ xoay quanh vấn đề bảo mật form và tối ưu cách viết code ngắn gọn và dễ tái sử dụng cho các Metaboxes khác. Tôi sẽ hướng dẫn bạn đầy đủ cách Bảo mật và tối ưu WordPress Metaboxes.

1/ Bảo mật WordPress Metaboxes

Tôi sẽ bắt tay vào việc bảo mật Book Metaboxes , đây là box mà tôi hướng dẫn bạn xây dựng ở bài trước, nếu bạn chưa xem kỹ bài hoặc quên bài thì làm ơn xem lại bài mà tôi đã dẫn link ở đầu bài.

Điều kiện bảo mật đầu tiên thì trong form của bạn sẽ có một số input ẩn, và bên trong input đó sẽ có các giá trị mặc định do hệ thống WordPress tạo ra, để tạo ra được các input ẩn đó thì tôi sẽ giới thiệu bạn phương thức là wp_nonce_field(), và nó có 4 tham số nhưng trong bài này bạn chỉ quan tâm tới 2 tham số đầu tiên.

Lưu ý : Tôi sẽ sử dụng lại toàn bộ code ở bài trước, chỉ bổ sung thêm vào code các điều kiện bảo mật và tối ưu code.

Nội dung file data.php:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

<?php

class Kenshin_Metaboxes_Data {

private $_create_id = 'ks-mb-data-';

private $_meta_key = '_ks_mb_data_';

private $_metabox_id = 'ks-mb-data';

public function __construct(){

add_action('add_meta_boxes', array($this, 'create'));

add_action('save_post', array($this, 'save'));

}

public function save($post_id){

// Có giá trị tương đương với $_POST

$postVal = $_POST;

// Đưa dữ liệu vào table wp_postmeta

update_post_meta($post_id, $this->_meta_key . 'title',

sanitize_text_field($postVal[$this->_create_id . 'title']));

update_post_meta($post_id, $this->_meta_key . 'price',

sanitize_text_field($postVal[$this->_create_id . 'price']));

update_post_meta($post_id, $this->_meta_key . 'author',

sanitize_text_field($postVal[$this->_create_id . 'author']));

update_post_meta($post_id, $this->_meta_key . 'info',

strip_tags($postVal[$this->_create_id . 'info']));

}

public function create(){

add_action('admin_enqueue_scripts', array($this,'add_css_file'));

add_meta_box($this->_metabox_id, 'Book Metaboxes', array($this, 'display'), 'post');

}

public function display($post){

wp_nonce_field($this->_metabox_id, $this->_metabox_id . '-nonce');

echo '<div class="ks-mb-data">';

// Phần tử form Book Title

$inputID = $this->_create_id . 'title';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'title', true);

$html = '';

$html .= '<label>Title :</label>';

$html .= '<input type="text" name= "'.$inputID.'" id="'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Price

$inputID = $this->_create_id . 'price';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'price', true);

$html = '';

$html .= '<label>Price :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Author

$inputID = $this->_create_id . 'author';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'author', true);

$html = '';

$html .= '<label>Author :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Info

$inputID = $this->_create_id . 'info';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'info', true);;

$html = '';

$html .= '<label>Infomation :</label>';

$html .= '<textarea name = "'.$inputID.'" id = "'.$inputID.'" rows="6" cols= "50">'.$inputValue.'</textarea>';

echo $html;

echo '</div>';

}

public function add_css_file(){

wp_register_style('ks_mb_data', KENSHIN_METABOXES_CSS_URL . '/mb-data.css', array(),'1.0');

wp_enqueue_style('ks_mb_data');

}

}

Lúc này tôi sẽ khởi tạo biến toàn cục $_metabox_id = ‘ks-mb-data’, nó chứa giá trị tên ID của Book Metaboxes. và ngay tại phương thức display(), nơi mà tôi xử lý form sẽ bổ sung vào đó phương thức wp_nonce_field() và hai tham số mà tôi truyền vào đó đầu tiên sẽ là tên ID của box và tham số thứ hai là tên ID của box nối chuỗi với phần tử -nonce. Để kiểm tra bạn vào bất kì bài viết nào và sau đó bấm F12 kiêm tra vị trí form thì sẽ thấy xuất hiện 4 trường input ẩn như hình.

Bảo mật và tối ưu WordPress Meta boxes

phuong-thuc-wp-nonce-field

Bạn thấy id form là ks-mb-data-nonce và name của nó cũng thế và 3 trường còn lại thì tôi cũng không biết nó làm nhiệm vụ gì , nhưng chắc là kiểm tra token và hạn chế một số lỗi bảo mật cho form mà thôi.

Vậy câu hỏi đặt ra, các trường input này sẽ gọi đến đâu để thực thi nhiệm vụ ? vâng các input này sẽ được gọi tới phương thức save() , nơi mà tôi xử lý code để đưa dữ liệu vào table wp_postmeta.

Lúc này hệ thống WordPress sẽ kiểm tra xem điều kiện nonce_field nó có tồn tại hay không,nếu tồn tại sẽ nạp dữ liệu vào database còn không sẽ trả về kết quả là ID của một bài viết rỗng.

1

if (!$postVal[$this->_metabox_id . '-nonce']) return $post_id;

Lúc này ở phương thức display() bạn tạm vô hiệu phương thức wp_nonce_field() và sau đó quay lại nhập liệu vào các ô textbox rồi bấm update bài viết xem coi có gì xảy ra không nha.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

<?php

class Kenshin_Metaboxes_Data {

private $_create_id = 'ks-mb-data-';

private $_meta_key = '_ks_mb_data_';

private $_metabox_id = 'ks-mb-data';

public function __construct(){

add_action('add_meta_boxes', array($this, 'create'));

add_action('save_post', array($this, 'save'));

}

public function save($post_id){

// Có giá trị tương đương với $_POST

$postVal = $_POST;

if (!$postVal[$this->_metabox_id . '-nonce']) return $post_id;

// Đưa dữ liệu vào table wp_postmeta

update_post_meta($post_id, $this->_meta_key . 'title',

sanitize_text_field($postVal[$this->_create_id . 'title']));

update_post_meta($post_id, $this->_meta_key . 'price',

sanitize_text_field($postVal[$this->_create_id . 'price']));

update_post_meta($post_id, $this->_meta_key . 'author',

sanitize_text_field($postVal[$this->_create_id . 'author']));

update_post_meta($post_id, $this->_meta_key . 'info',

strip_tags($postVal[$this->_create_id . 'info']));

}

public function create(){

add_action('admin_enqueue_scripts', array($this,'add_css_file'));

add_meta_box($this->_metabox_id, 'Book Metaboxes', array($this, 'display'), 'post');

}

public function display($post){

//wp_nonce_field($this->_metabox_id, $this->_metabox_id . '-nonce');

echo '<div class="ks-mb-data">';

// Phần tử form Book Title

$inputID = $this->_create_id . 'title';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'title', true);

$html = '';

$html .= '<label>Title :</label>';

$html .= '<input type="text" name= "'.$inputID.'" id="'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Price

$inputID = $this->_create_id . 'price';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'price', true);

$html = '';

$html .= '<label>Price :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Author

$inputID = $this->_create_id . 'author';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'author', true);

$html = '';

$html .= '<label>Author :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Info

$inputID = $this->_create_id . 'info';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'info', true);;

$html = '';

$html .= '<label>Infomation :</label>';

$html .= '<textarea name = "'.$inputID.'" id = "'.$inputID.'" rows="6" cols= "50">'.$inputValue.'</textarea>';

echo $html;

echo '</div>';

}

public function add_css_file(){

wp_register_style('ks_mb_data', KENSHIN_METABOXES_CSS_URL . '/mb-data.css', array(),'1.0');

wp_enqueue_style('ks_mb_data');

}

}

Kết quả là tôi không đưa được dữ liệu từ form vào database, tại vì hệ thống kiểm tra không thấy được sự tồn tại của các trường input ẩn nên hệ thống hủy bỏ quá trình tiếp theo là cập nhật dữ liệu vào database. Bạn thấy đấy một khi bạn đã đưa phương thức bảo mật vào form thì bắt buộc bạn phải tuân thủ theo quy tắc nhập liệu do hệ thống WordPress đề ra.

Lúc này chúng ta sẽ xét tới điều kiện bảo mật thứ hai, đó chính là so sánh xem giá trị của điều kiện thứ nhất là các thông số của các trường input ẩn có giống với giá trị đang gửi tới phương thức save()hay không, nếu các thông số không giống nhau thì hệ thống cũng sẽ ngăn bạn đưa dữ liệu vào database.

1

if(!wp_verify_nonce($postVal[$this->_meta_box_id . '-nonce'],$this->_meta_box_id)) return $post_id;

Tiếp đến sẽ xét tới điều kiện thứ ba là bình thường khi bạn đang biên tập dang dở một bài viết nào đó, sau đó bạn loay hoay chém gió trên facebook mà không đếm xỉa tới bài viết thì mặc định của WordPress là cứ 150s sẽ tự động save bài viết dùm bạn. Đối với trường hợp khi bạn đang sử dụng Metaboxes và bạn không muốn khi autosave sẽ cập nhật luôn dữ liệu từ Metaboxes vào database.

1

if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return $post_id;

Và điều kiện bảo mật cuối cùng là sẽ kiểm tra quyền hạn xem user đó có được phép edit các giá trị của các ô textbox hay không.

1

if(!current_user_can('edit_post', $post_id)) return $post_id;

Và đây là full code bảo mật.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

<?php

class Kenshin_Metaboxes_Data {

private $_create_id = 'ks-mb-data-';

private $_meta_key = '_ks_mb_data_';

private $_metabox_id = 'ks-mb-data';

public function __construct(){

add_action('add_meta_boxes', array($this, 'create'));

add_action('save_post', array($this, 'save'));

}

public function save($post_id){

// Có giá trị tương đương với $_POST

$postVal = $_POST;

if (!$postVal[$this->_metabox_id . '-nonce']) return $post_id;

if(!wp_verify_nonce($postVal[$this->_metabox_id . '-nonce'],$this->_metabox_id)) return $post_id;

if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return $post_id;

if(!current_user_can('edit_post', $post_id)) return $post_id;

// Đưa dữ liệu vào table wp_postmeta

update_post_meta($post_id, $this->_meta_key . 'title',

sanitize_text_field($postVal[$this->_create_id . 'title']));

update_post_meta($post_id, $this->_meta_key . 'price',

sanitize_text_field($postVal[$this->_create_id . 'price']));

update_post_meta($post_id, $this->_meta_key . 'author',

sanitize_text_field($postVal[$this->_create_id . 'author']));

update_post_meta($post_id, $this->_meta_key . 'info',

strip_tags($postVal[$this->_create_id . 'info']));

}

public function create(){

add_action('admin_enqueue_scripts', array($this,'add_css_file'));

add_meta_box($this->_metabox_id, 'Book Metaboxes', array($this, 'display'), 'post');

}

public function display($post){

wp_nonce_field($this->_metabox_id, $this->_metabox_id . '-nonce');

echo '<div class="ks-mb-data">';

// Phần tử form Book Title

$inputID = $this->_create_id . 'title';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'title', true);

$html = '';

$html .= '<label>Title :</label>';

$html .= '<input type="text" name= "'.$inputID.'" id="'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Price

$inputID = $this->_create_id . 'price';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'price', true);

$html = '';

$html .= '<label>Price :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Author

$inputID = $this->_create_id . 'author';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'author', true);

$html = '';

$html .= '<label>Author :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Info

$inputID = $this->_create_id . 'info';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'info', true);;

$html = '';

$html .= '<label>Infomation :</label>';

$html .= '<textarea name = "'.$inputID.'" id = "'.$inputID.'" rows="6" cols= "50">'.$inputValue.'</textarea>';

echo $html;

echo '</div>';

}

public function add_css_file(){

wp_register_style('ks_mb_data', KENSHIN_METABOXES_CSS_URL . '/mb-data.css', array(),'1.0');

wp_enqueue_style('ks_mb_data');

}

}

2/ Tối ưu WordPress Metaboxes

Lúc này bạn để ý ngay chỗ xử lý của phương thức update_post_meta() nó khá dài dòng, và câu hỏi đặt ra lỡ bạn có hơn 10 textbox thì chỗ đó sẽ trở thành đám rừng luôn đấy.

Tôi sẽ chia sẽ bạn cách viết tối ưu, đầu tiên bạn tạo biến $data là một mảng dùng để chứa các thông số và sau đó dùng vòng lặp foreach để xử lý vấn đề.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

<?php

class Kenshin_Metaboxes_Data {

private $_create_id = 'ks-mb-data-';

private $_meta_key = '_ks_mb_data_';

private $_metabox_id = 'ks-mb-data';

public function __construct(){

add_action('add_meta_boxes', array($this, 'create'));

add_action('save_post', array($this, 'save'));

}

public function save($post_id){

// Có giá trị tương đương với $_POST

$postVal = $_POST;

if (!$postVal[$this->_metabox_id . '-nonce']) return $post_id;

if(!wp_verify_nonce($postVal[$this->_metabox_id . '-nonce'],$this->_metabox_id)) return $post_id;

if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return $post_id;

if(!current_user_can('edit_post', $post_id)) return $post_id;

$data = array(

'title' => sanitize_text_field($postVal[$this->_create_id . 'title']),

'price' => sanitize_text_field($postVal[$this->_create_id . 'price']),

'author' => sanitize_text_field($postVal[$this->_create_id . 'author']),

'info' => strip_tags($postVal[$this->_create_id . 'info'])

);

foreach ($data as $key => $val){

update_post_meta($post_id, $this->_meta_key . $key ,$val);

}

// Đưa dữ liệu vào table wp_postmeta

/* update_post_meta($post_id, $this->_meta_key . 'title',

sanitize_text_field($postVal[$this->_create_id . 'title']));

update_post_meta($post_id, $this->_meta_key . 'price',

sanitize_text_field($postVal[$this->_create_id . 'price']));

update_post_meta($post_id, $this->_meta_key . 'author',

sanitize_text_field($postVal[$this->_create_id . 'author']));

update_post_meta($post_id, $this->_meta_key . 'info',

strip_tags($postVal[$this->_create_id . 'info'])); */

}

public function create(){

add_action('admin_enqueue_scripts', array($this,'add_css_file'));

add_meta_box($this->_metabox_id, 'Book Metaboxes', array($this, 'display'), 'post');

}

public function display($post){

wp_nonce_field($this->_metabox_id, $this->_metabox_id . '-nonce');

echo '<div class="ks-mb-data">';

// Phần tử form Book Title

$inputID = $this->_create_id . 'title';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'title', true);

$html = '';

$html .= '<label>Title :</label>';

$html .= '<input type="text" name= "'.$inputID.'" id="'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Price

$inputID = $this->_create_id . 'price';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'price', true);

$html = '';

$html .= '<label>Price :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Author

$inputID = $this->_create_id . 'author';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'author', true);

$html = '';

$html .= '<label>Author :</label>';

$html .= '<input type="text" name = "'.$inputID.'" id = "'.$inputID.'" value = "'.$inputValue.'" size="25" />';

echo $html;

// Phần tử form Book Info

$inputID = $this->_create_id . 'info';

$inputValue = get_post_meta($post->ID, $this->_meta_key . 'info', true);;

$html = '';

$html .= '<label>Infomation :</label>';

$html .= '<textarea name = "'.$inputID.'" id = "'.$inputID.'" rows="6" cols= "50">'.$inputValue.'</textarea>';

echo $html;

echo '</div>';

}

public function add_css_file(){

wp_register_style('ks_mb_data', KENSHIN_METABOXES_CSS_URL . '/mb-data.css', array(),'1.0');

wp_enqueue_style('ks_mb_data');

}

}

Với cách viết này mỗi khi bạn bổ sung thêm textbox thì chỉ khai báo trong mảng và việc còn lại để vòng lặp xử lý là được. Bạn sẽ không còn phải bận tâm khi Box của bạn có nhiều textbox nửa.

3/ Lời kết

Kết thúc bài Bảo mật và tối ưu WordPress Metaboxes thì tôi tin chắc rằng lúc này đây bạn sẽ có một thói quen khi làm việc với WordPress đó là tuân thủ mọi quy tắc mà nó đề ra, điều này cũng không làm mất quá nhiều thời gian của bạn mà nó có ích cho bạn trong khâu bảo mật website.

Series Navigation<< Tương tác database với WordPress Meta boxesHiển thị WordPress Meta boxes ngoài trang chủ >>

Nguồn: laptrinhweb.org

Last updated