דלגו לתוכן

SQL מניעת הזרקה

הזרקת SQL היא אחת מפגיעות יישומי האינטרנט המסוכנות והנפוצות ביותר. מדריך זה מכסה כיצד להגן על המודולים XOOPS שלך מפני התקפות הזרקה SQL.

  • אבטחה-שיטות עבודה מומלצות - מדריך אבטחה מקיף
  • CSRF-הגנה - מערכת אסימונים ומחלקה XoopsSecurity
  • קלט-חיטוי - MyTextSanitizer ואימות

הזרקת SQL מתרחשת כאשר קלט המשתמש נכלל ישירות בשאילתות SQL ללא חיטוי או פרמטרים מתאימים.

// DANGEROUS - DO NOT USE
$id = $_GET['id'];
$sql = "SELECT * FROM " . $xoopsDB->prefix('items') . " WHERE id = " . $id;
$result = $xoopsDB->query($sql);

אם משתמש מעביר 1 OR 1=1 כמזהה, השאילתה הופכת:

SELECT * FROM xoops_items WHERE id = 1 OR 1=1

זה מחזיר את כל הרשומות במקום רק אחת.

שימוש בשאילתות עם פרמטרים

Section titled “שימוש בשאילתות עם פרמטרים”

ההגנה היעילה ביותר מפני הזרקת SQL היא שימוש בשאילתות עם פרמטרים (הצהרות מוכנות).

שאילתה בסיסית עם פרמטרים

Section titled “שאילתה בסיסית עם פרמטרים”
// Get database connection
$xoopsDB = XoopsDatabaseFactory::getDatabaseConnection();
// SECURE - Using parameterized query
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') . " WHERE id = ?";
$result = $xoopsDB->query($sql, [(int)$_GET['id']]);
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') .
" WHERE username = ? AND status = ?";
$result = $xoopsDB->query($sql, [$username, $status]);

חלק מהפשטות מסד הנתונים תומכות בפרמטרים בעלי שם:

$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') .
" WHERE username = :username AND status = :status";
$result = $xoopsDB->query($sql, [
':username' => $username,
':status' => $status
]);

באמצעות XoopsObject ו-XoopsObjectHandler

Section titled “באמצעות XoopsObject ו-XoopsObjectHandler”

XOOPS מספקת גישה למסד נתונים מונחה עצמים המסייעת במניעת הזרקת SQL דרך מערכת הקריטריונים.

שימוש בקריטריונים בסיסיים

Section titled “שימוש בקריטריונים בסיסיים”
// Get the handler
$itemHandler = xoops_getModuleHandler('item', 'mymodule');
// Create criteria
$criteria = new Criteria('category_id', (int)$categoryId);
// Get objects - automatically safe from SQL injection
$items = $itemHandler->getObjects($criteria);

CriteriaCompo עבור תנאים מרובים

Section titled “CriteriaCompo עבור תנאים מרובים”
$criteria = new CriteriaCompo();
$criteria->add(new Criteria('category_id', (int)$categoryId));
$criteria->add(new Criteria('status', 'published'));
$criteria->add(new Criteria('uid', (int)$userId));
// Optional: Add ordering and limits
$criteria->setSort('created');
$criteria->setOrder('DESC');
$criteria->setLimit(10);
$criteria->setStart(0);
$items = $itemHandler->getObjects($criteria);
// Equal (default)
$criteria->add(new Criteria('status', 'active'));
// Not equal
$criteria->add(new Criteria('status', 'deleted', '!='));
// Greater than
$criteria->add(new Criteria('count', 100, '>'));
// Less than or equal
$criteria->add(new Criteria('price', 50, '<='));
// LIKE (for partial matching)
$criteria->add(new Criteria('title', '%' . $searchTerm . '%', 'LIKE'));
// IN (multiple values)
$criteria->add(new Criteria('id', '(' . implode(',', $ids) . ')', 'IN'));
$criteria = new CriteriaCompo();
$criteria->add(new Criteria('status', 'published'));
// OR condition
$orCriteria = new CriteriaCompo();
$orCriteria->add(new Criteria('uid', (int)$userId), 'OR');
$orCriteria->add(new Criteria('is_public', 1), 'OR');
$criteria->add($orCriteria);

השתמש תמיד במערכת קידומת הטבלה XOOPS:

// Correct - using prefix
$table = $xoopsDB->prefix('mytable');
$sql = "SELECT * FROM {$table} WHERE id = ?";
// Also correct
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') . " WHERE id = ?";
$sql = "INSERT INTO " . $xoopsDB->prefix('mytable') .
" (title, content, uid, created) VALUES (?, ?, ?, ?)";
$result = $xoopsDB->query($sql, [
$title,
$content,
(int)$userId,
time()
]);
if ($result) {
$newId = $xoopsDB->getInsertId();
}
// Create new object
$item = $itemHandler->create();
// Set values - handler escapes automatically
$item->setVar('title', $title);
$item->setVar('content', $content);
$item->setVar('uid', (int)$userId);
$item->setVar('created', time());
// Insert
if ($itemHandler->insert($item)) {
$newId = $item->getVar('itemid');
}
$sql = "UPDATE " . $xoopsDB->prefix('mytable') .
" SET title = ?, content = ?, updated = ? WHERE id = ?";
$result = $xoopsDB->query($sql, [
$title,
$content,
time(),
(int)$id
]);
// Get existing object
$item = $itemHandler->get((int)$id);
if ($item) {
$item->setVar('title', $title);
$item->setVar('content', $content);
$item->setVar('updated', time());
$itemHandler->insert($item);
}
$sql = "DELETE FROM " . $xoopsDB->prefix('mytable') . " WHERE id = ?";
$result = $xoopsDB->query($sql, [(int)$id]);
$item = $itemHandler->get((int)$id);
if ($item) {
$itemHandler->delete($item);
}

מחיקה בכמות גדולה עם קריטריונים

Section titled “מחיקה בכמות גדולה עם קריטריונים”
$criteria = new Criteria('status', 'deleted');
$itemHandler->deleteAll($criteria);

אם אינך יכול להשתמש בהצהרות מוכנות, השתמש בבריחה נכונה:

// Using mysqli_real_escape_string
$safe_value = $xoopsDB->escape($value);
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') .
" WHERE title = '" . $safe_value . "'";

עם זאת, העדיף תמיד הצהרות מוכנות על פני בריחה.

בניית שאילתות דינמיות בצורה בטוחה

Section titled “בניית שאילתות דינמיות בצורה בטוחה”

שמות עמודות דינמיות בטוחות

Section titled “שמות עמודות דינמיות בטוחות”

לא ניתן להגדיר פרמטרים של שמות עמודות. אימות מול רשימת הלבנים:

$allowed_columns = ['title', 'created', 'updated', 'status'];
$sort = $_GET['sort'] ?? 'created';
if (!in_array($sort, $allowed_columns)) {
$sort = 'created'; // Default safe value
}
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') .
" ORDER BY {$sort} DESC";

שמות טבלאות דינמיות בטוחות

Section titled “שמות טבלאות דינמיות בטוחות”

באופן דומה, אמת את שמות הטבלה:

$allowed_tables = ['items', 'categories', 'comments'];
$table = $_GET['table'] ?? 'items';
if (!in_array($table, $allowed_tables)) {
$table = 'items';
}
$sql = "SELECT * FROM " . $xoopsDB->prefix($table) . " WHERE id = ?";

בניית WHERE סעיפים באופן דינמי

Section titled “בניית WHERE סעיפים באופן דינמי”
$criteria = new CriteriaCompo();
// Add conditions based on input
if (!empty($_GET['category'])) {
$criteria->add(new Criteria('category_id', (int)$_GET['category']));
}
if (!empty($_GET['status'])) {
$allowed_statuses = ['draft', 'published', 'archived'];
if (in_array($_GET['status'], $allowed_statuses)) {
$criteria->add(new Criteria('status', $_GET['status']));
}
}
if (!empty($_GET['search'])) {
$search = '%' . $_GET['search'] . '%';
$criteria->add(new Criteria('title', $search, 'LIKE'));
}
$items = $itemHandler->getObjects($criteria);

היזהר עם שאילתות LIKE כדי להימנע מהזרקת תווים כלליים:

// Escape special characters in search term
$searchTerm = str_replace(['%', '_'], ['\%', '\_'], $searchTerm);
// Then use in LIKE
$criteria->add(new Criteria('title', '%' . $searchTerm . '%', 'LIKE'));

בעת שימוש בסעיפים IN, ודא שכל הערכים מוקלדים כהלכה:

// Array of IDs from user input
$inputIds = $_POST['ids'] ?? [];
// Sanitize: ensure all are integers
$safeIds = array_map('intval', $inputIds);
$safeIds = array_filter($safeIds, function($id) { return $id > 0; });
if (!empty($safeIds)) {
$idList = implode(',', $safeIds);
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') .
" WHERE id IN ({$idList})";
$result = $xoopsDB->query($sql);
}

או עם קריטריונים:

if (!empty($safeIds)) {
$criteria = new Criteria('id', '(' . implode(',', $safeIds) . ')', 'IN');
$items = $itemHandler->getObjects($criteria);
}

בעת ביצוע מספר שאילתות קשורות:

// Start transaction
$xoopsDB->query("START TRANSACTION");
try {
// Query 1
$sql1 = "INSERT INTO " . $xoopsDB->prefix('items') . " (title) VALUES (?)";
$result1 = $xoopsDB->query($sql1, [$title]);
if (!$result1) {
throw new Exception('Insert failed');
}
$itemId = $xoopsDB->getInsertId();
// Query 2
$sql2 = "INSERT INTO " . $xoopsDB->prefix('item_meta') .
" (item_id, meta_key, meta_value) VALUES (?, ?, ?)";
$result2 = $xoopsDB->query($sql2, [$itemId, 'author', $author]);
if (!$result2) {
throw new Exception('Meta insert failed');
}
// Commit
$xoopsDB->query("COMMIT");
} catch (Exception $e) {
// Rollback on error
$xoopsDB->query("ROLLBACK");
throw $e;
}

לעולם אל תחשוף שגיאות SQL למשתמשים:

$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') . " WHERE id = ?";
$result = $xoopsDB->query($sql, [(int)$id]);
if (!$result) {
// Log the actual error for debugging
error_log('Database error: ' . $xoopsDB->error());
// Show generic message to user
redirect_header('index.php', 3, 'An error occurred. Please try again.');
exit();
}

טעויות נפוצות שיש להימנע מהן

Section titled “טעויות נפוצות שיש להימנע מהן”

טעות 1: אינטרפולציה ישירה של משתנה

Section titled “טעות 1: אינטרפולציה ישירה של משתנה”
// WRONG
$sql = "SELECT * FROM {$table} WHERE id = {$id}";
// RIGHT
$sql = "SELECT * FROM " . $xoopsDB->prefix('mytable') . " WHERE id = ?";
$result = $xoopsDB->query($sql, [(int)$id]);
// WRONG - addslashes is NOT sufficient
$safe = addslashes($_GET['input']);
// RIGHT - use parameterized queries or proper escaping
$sql = "SELECT * FROM table WHERE col = ?";
$result = $xoopsDB->query($sql, [$_GET['input']]);

טעות 3: אמון במזהים מספריים

Section titled “טעות 3: אמון במזהים מספריים”
// WRONG - assuming input is numeric
$id = $_GET['id'];
$sql = "SELECT * FROM table WHERE id = " . $id;
// RIGHT - explicitly cast to integer
$id = (int)$_GET['id'];
$sql = "SELECT * FROM table WHERE id = ?";
$result = $xoopsDB->query($sql, [$id]);
// Data from database is NOT automatically safe
$userData = $itemHandler->get($id);
$username = $userData->getVar('username');
// WRONG - trusting data from database
$sql = "SELECT * FROM log WHERE username = '" . $username . "'";
// RIGHT - always use parameters
$sql = "SELECT * FROM log WHERE username = ?";
$result = $xoopsDB->query($sql, [$username]);

בדוק את הטפסים שלך עם התשומות האלה כדי לבדוק אם יש הזרקה של SQL:

  • ' OR '1'='1
  • 1; DROP TABLE users--
  • 1 UNION SELECT * FROM users--
  • admin'--
  • ' OR 1=1#

אם אחד מאלה גורם להתנהגות בלתי צפויה או שגיאות, יש לך פגיעות.

השתמש בכלי בדיקת הזרקה אוטומטיים של SQL במהלך הפיתוח:

  • SQLMap
  • סוויטת בורפ
  • OWASP ZAP

סיכום שיטות עבודה מומלצות

Section titled “סיכום שיטות עבודה מומלצות”
  1. השתמש תמיד בשאילתות עם פרמטרים (הצהרות מוכנות)
  2. השתמש ב-XoopsObject/XoopsObjectHandler במידת האפשר
  3. השתמש בשיעורי קריטריונים לבניית שאילתות
  4. הרשימה הלבנה של הערכים המותרים עבור עמודות ושמות טבלאות
  5. הטל ערכים מספריים במפורש עם (int) או (float)
  6. לעולם אל תחשוף שגיאות מסד נתונים למשתמשים
  7. השתמש בעסקאות עבור שאילתות קשורות מרובות
  8. בדיקת הזרקת SQL במהלך הפיתוח
  9. Escape LIKE תווים כלליים בשאילתות חיפוש
  10. חיטוי ערכי IN בנפרד

#Security #sql-injection #database #xoops #prepared-statements #Criteria