post-image

Tổng hợp các mẹo hay khi dùng PHP

Tổng quan

PHP đã và đang trở thành ngôn ngữ lập trình phổ biến nhất cho các ứng dụng Web. Vậy nên những mẹo này sẽ tăng tốc độ thành thạo và làm cho mã nguồn sạch hơn và tối ưu hóa hơn cho hiệu suất.

Tránh SQL Injection

  • VD: Khi người dùng nhập thông tin đăng nhập và bấm login, thông tin của người dùng sẽ được gửi một tới server thông qua một POST request sau đó sẽ được gán vào một câu lệnh SQL. Đoạn code đó sẽ trông như này:
$query = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "' AND password = '" . $_POST['password'] . "'"; 
  • Giả sử dữ liệu gửi lên email="[email protected]" và password="12345678" thì câu query sẽ như sau:
$query = "SELECT * FROM users WHERE email='[email protected]' AND password='12345678'"
  • Đấy là trường hợp người dùng nhập đúng vậy nếu người dùng cố tình nhập sai thì sao?

  • Kết quả câu query sẽ như dưới, người dùng chỉ cần nhập email là có thể truy cập được.
SELECT * FROM users WHERE email='[email protected]' OR 1=1; 
-- ' AND password='123456' 

Vậy làm thế nào để tránh tấn công SQL Injection ?

  • Và đây là một vài mẹo của mình đó là đừng bao giờ tin vào thông tin người dùng nhập vào.
  • Validate dữ liệu trên server side: Sử dụng hàm mysql_real_escape_string() để loại bỏ những kí tự có thể gây ảnh hưởng đến câu lệnh SQL.
$email = mysql_real_escape_string($_POST['email']); 
$password = mysql_real_escape_string($_POST['password']); 
<?php
$query = $db->prepare('SELECT * FROM users WHERE email=:email AND password=:password');

$query->execute([
    'email' => $_POST['email'],
    'password' => $_POST['password'],
]);

2. Sự khác biệt giữa các toán tử so sánh

  • Đây là một mẹo hay, nhưng cần một ví dụ thực tế chứng minh khi một so sánh không nghiêm ngặt có thể gây ra vấn đề.
  • Nếu bạn sử dụng strpose() để xác định xem một chuỗi con có tồn tại trong một chuỗi hay không (nó trả về FALSE nếu không tìm thấy chuỗi con và trả về vị trí đầu tiên xuất hiện của chuỗi con), kết quả có thể gây hiểu nhầm:
<?php

$authors = 'Chris & Sean';

if (strpos($authors, 'Chris')) {
    echo 'Chris is an author.';
} else {
    echo 'Chris is not an author.';
}
  • Vì chuỗi con Chris xuất hiện ở đầu ‘Chris & Sean’, strpose() trả về đúng 0, cho biết vị trí đầu tiên trong chuỗi.
  • Bởi vì câu lệnh có điều kiện coi đây là Boolean, nó trả về FALSE. và kết quả sẽ là Chris is not an author. -> Sai logic
  • Điều này có thể được sửa chữa bằng một so sánh nghiêm ngặt:
<?php

if (strpos($authors, 'Chris') !== FALSE) {
    echo 'Chris is an author.';
} else {
    echo 'Chris is not an author.';
}

3. Shortcut The Else

  • Kiểm tra người dùng có phải admin không? dựa vào username.
<?php

if (auth($username) == 'admin') {
    $admin = TRUE;
} else {
    $admin = FALSE;
}
  • Đoạn code trên có vẻ ổn, nhưng nếu nhà phát triển sau đó thêm một role khác.
<?php

if (auth($username) == 'admin') {
    $admin = TRUE;
} elseif (auth($username) == 'mod') {
    $moderator = TRUE;
} else {
    $admin = FALSE;
    $moderator = FALSE;
}
  • Đoạn code trên, nếu người dùng cung cấp tên ngừoi chạy vào điều kiện auth($username) == 'mod' thì $admin chưa được khởi tạo. Điều này có thể dẫn đến kết quả không mong muốn hoặc lỗ hổng bảo mật.
  • Ngoài ra, một case tương tự $moderator không được khởi tạo khi chạy điều kiện auth($username) == 'admin'.
  • Bằng cách khởi tạo $admin và $moderator đầu tiên để tránh được tình huống này.
<?php

$admin = FALSE;
$moderator = FALSE;

if (auth($username) == 'admin') {
    $admin = TRUE;
} elseif (auth($username) == 'mod') {
    $moderator = TRUE;
} else {
    $admin = FALSE;
    $moderator = FALSE;
}
  • Nên tạo một function để xử lý user có được phép xem một trang cụ thể.
<?php

function authorized($username, $page) {
    if (!isBlacklisted($username)) {
        if (isAdmin($username)) {
            return TRUE;
        } elseif (isAllowed($username, $page)) {
            return TRUE;
        } else {
            return FALSE;
        }
    } else {
        return FALSE;
    }
}
  • Nếu bạn muốn giảm số lượng dòng, có thể ghép điều kiện như sau.
<?php

function authorized($username, $page) {
    if (!isBlacklisted($username)) {
        if (isAdmin($username) || isAllowed($username, $page)) {
            return TRUE;
        } else {
            return FALSE;
        }
    } else {
        return FALSE;
    }
}
  • Bạn có thể giảm toàn bộ hàm thành một điều kiện ghép duy nhất
<?php

function authorized($username, $page) {
    if (!isBlacklisted($username) && (isAdmin($username) || isAllowed($username, $page)) {
        return TRUE;
    } else {
        return FALSE;
    }
}
  • Cuối cùng, functioncó thể được giảm xuống thành một lần return
<?php

function authorized($username, $page) {
    return (!isBlacklisted($username) && (isAdmin($username) || isAllowed($username, $page));
}
  • Nếu mục tiêu của bạn là giảm số lượng dòng, bạn đã hoàn thành. Tuy nhiên, nhìn code rất dối, khó hiểu.
  • Điều này đưa chúng ta đến mẹo sẽ trả về ngay lập tức nếu thỏa mãn điều kiện.
<?php

function authorized($username, $page) {

    if (isBlacklisted($username)) {
        return FALSE;
    }

    if (isAdmin($username)) {
        return TRUE;
    }

    return isAllowed($username, $page);
}
  • Mặc dù sử dụng nhiều dòng mã hơn, nhưng nó rất đơn giản, dễ hiểu và hữu ích khi logic của bạn phức tạp hơn.

4. Luôn sử dụng {} sau biểu thức điều kiện

  • Xem ví dụ sau:
<?php
if (date('d M') == '21 May')
    $birthdays = [
        'Al Franken',
        'Chris Shiflett',
        'Chris Wallace',
        'Lawrence Tureaud'
    ];
  • Bạn đã có danh sách người sinh nhật vào 21 May và sau đó gọi hàm party(TRUE);
<?php
if (date('d M') == '21 May')
    $birthdays = [
        'Al Franken',
        'Chris Shiflett',
        'Chris Wallace',
        'Lawrence Tureaud'
    ];
    party(TRUE);
  • Nhưng không hàm party(TRUE); luôn chạy không cần điều kiện date('d M') == '21 May'.
  • Do đó nên sử dụng {} sau biểu thức điều kiện dù chỉ có một dòng lệnh trong đó.
<?php

if (date('d M') == '21 May') {
    $birthdays = [
        'Al Franken',
        'Chris Shiflett',
        'Chris Wallace',
        'Lawrence Tureaud'
    ];
    party(TRUE);
}

5. Hàm str_replace(), ereg_replace() và preg_replace()

  • Mẹo thứ 5 đó là nếu bạn sử dụng biểu thức chính quy, ereg_replace() và preg_replace() sẽ nhanh hơn str_replace. Vì str str_replace không hỗ trợ khớp mẫu.
  • Sự lựa chọn giữa các string functions và regular expression functions phù hợp với mục đích, không phải là nhanh hơn.
  • Nếu bạn cần khớp mẫu, sử dụng hàm biểu thức chính quy ereg_replacepreg_replace.
  • Nếu bạn cần khớp chuỗi sử dụng str_replace.

6. Cẩn thận khi sử dụng toán tử 3 ngôi.

  • Xem ví dụ sau:
<?php

$host = strlen($host) > 0 ? $host : htmlentities($host);

  • Tác giả muốn $host = htmlentities($host) nếu độ dài chuỗi lớn hơn 0, nhưng thay vào đó lại vô tình làm ngược lại.

7. Sử dụng một Framework để phát triển ứng dụng web

8. Sử dụng isset() thay cho strlen()

  • Xem ví dụ sau:
<?php

if (isset($username[5])) {
    // The username is at least six characters long.
}
  • Khi bạn coi các chuỗi là mảng, mỗi ký tự trong chuỗi là một phần tử trong mảng.
  • Bằng cách xác định xem một phần tử cụ thể có tồn tại hay không, bạn có thể xác định xem chuỗi đó có dài ít nhất nhiều ký tự hay không. (Lưu ý rằng ký tự đầu tiên là phần tử 0, vì vậy $ username [5] là ký tự thứ sáu trong $ username).
  • Lý do: hàm isset() sẽ nhanh hơn strlen() vì strlen() là một function còn isset() là một language construct xem thêm.

Trả lời

Email của bạn sẽ không được hiển thị công khai.