דלגו לתוכן

חיטוי קלט

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

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

לעולם אל תסמוך על קלט משתמש. כל הנתונים ממקורות חיצוניים חייבים להיות:

  1. מאומת: בדוק שהוא תואם את הפורמט והסוג הצפויים
  2. מחטא: הסר או בריחה דמויות שעלולות להיות מסוכנות
  3. Escape: בעת הפלט, escape עבור ההקשר הספציפי (HTML, JavaScript, SQL)

XOOPS מספקת את המחלקה MyTextSanitizer (המכונה בדרך כלל $myts) עבור חיטוי טקסט.

// Get the singleton instance
$myts = MyTextSanitizer::getInstance();
$myts = MyTextSanitizer::getInstance();
// For plain text fields (no HTML allowed)
$title = $myts->htmlSpecialChars($_POST['title']);
// This converts:
// < to &lt;
// > to &gt;
// & to &amp;
// " to &quot;
// ' to &#039;

שיטת displayTarea() מספקת עיבוד מקיף של אזורי טקסט:

$myts = MyTextSanitizer::getInstance();
$content = $myts->displayTarea(
$_POST['content'],
$allowhtml = 0, // 0 = No HTML allowed, 1 = HTML allowed
$allowsmiley = 1, // 1 = Smilies enabled
$allowxcode = 1, // 1 = XOOPS codes enabled (BBCode)
$allowimages = 1, // 1 = Images allowed
$allowlinebreak = 1 // 1 = Line breaks converted to <br>
);
$myts = MyTextSanitizer::getInstance();
// HTML special characters escaping
$safe_text = $myts->htmlSpecialChars($text);
// Strip slashes if magic quotes are on
$text = $myts->stripSlashesGPC($text);
// Convert XOOPS codes (BBCode) to HTML
$html = $myts->xoopsCodeDecode($text);
// Convert smileys to images
$html = $myts->smiley($text);
// Make clickable links
$html = $myts->makeClickable($text);
// Complete text processing for preview
$preview = $myts->previewTarea($text, $allowhtml, $allowsmiley, $allowxcode);
// Validate integer ID
$id = isset($_REQUEST['id']) ? (int)$_REQUEST['id'] : 0;
if ($id <= 0) {
redirect_header('index.php', 3, 'Invalid ID');
exit();
}
// Alternative with filter_var
$id = filter_var($_REQUEST['id'] ?? 0, FILTER_VALIDATE_INT);
if ($id === false || $id <= 0) {
redirect_header('index.php', 3, 'Invalid ID');
exit();
}
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if (!$email) {
redirect_header('form.php', 3, 'Invalid email address');
exit();
}
$url = filter_var($_POST['url'], FILTER_VALIDATE_URL);
if (!$url) {
redirect_header('form.php', 3, 'Invalid URL');
exit();
}
// Additional check for allowed protocols
$parsed = parse_url($url);
$allowed_schemes = ['http', 'https'];
if (!in_array($parsed['scheme'], $allowed_schemes)) {
redirect_header('form.php', 3, 'Only HTTP and HTTPS URLs are allowed');
exit();
}
$date = $_POST['date'] ?? '';
// Validate date format (YYYY-MM-DD)
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
redirect_header('form.php', 3, 'Invalid date format');
exit();
}
// Validate actual date validity
$parts = explode('-', $date);
if (!checkdate($parts[1], $parts[2], $parts[0])) {
redirect_header('form.php', 3, 'Invalid date');
exit();
}
// Remove all characters except alphanumeric, underscore, and hyphen
$filename = preg_replace('/[^a-zA-Z0-9_-]/', '', $_POST['filename']);
// Or use a whitelist approach
$allowed_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
$filename = '';
foreach (str_split($_POST['filename']) as $char) {
if (strpos($allowed_chars, $char) !== false) {
$filename .= $char;
}
}
$myts = MyTextSanitizer::getInstance();
// Short text (titles, names)
$title = $myts->htmlSpecialChars(trim($_POST['title']));
// Limit length
if (strlen($title) > 255) {
$title = substr($title, 0, 255);
}
// Check for empty required fields
if (empty($title)) {
redirect_header('form.php', 3, 'Title is required');
exit();
}
// Integer
$count = (int)$_POST['count'];
$count = max(0, min($count, 1000)); // Ensure range 0-1000
// Float
$price = (float)$_POST['price'];
$price = round($price, 2); // Round to 2 decimal places
// Validate range
if ($price < 0 || $price > 99999.99) {
redirect_header('form.php', 3, 'Invalid price');
exit();
}
// Checkbox values
$is_active = isset($_POST['is_active']) ? 1 : 0;
// Or with explicit value check
$is_active = ($_POST['is_active'] ?? '') === '1' ? 1 : 0;
// Validate array input (e.g., multiple checkboxes)
$selected_ids = [];
if (isset($_POST['ids']) && is_array($_POST['ids'])) {
foreach ($_POST['ids'] as $id) {
$clean_id = (int)$id;
if ($clean_id > 0) {
$selected_ids[] = $clean_id;
}
}
}
// Validate against allowed values
$allowed_statuses = ['draft', 'published', 'archived'];
$status = $_POST['status'] ?? '';
if (!in_array($status, $allowed_statuses)) {
redirect_header('form.php', 3, 'Invalid status');
exit();
}

בעת שימוש ב-XMF, מחלקת Request מספקת טיפול נקי יותר בקלט:

use Xmf\Request;
// Get integer
$id = Request::getInt('id', 0);
// Get string
$title = Request::getString('title', '');
// Get array
$ids = Request::getArray('ids', []);
// Get with method specification
$id = Request::getInt('id', 0, 'POST');
$search = Request::getString('q', '', 'GET');
// Check request method
if (Request::getMethod() !== 'POST') {
redirect_header('form.php', 3, 'Invalid request method');
exit();
}

עבור טפסים מורכבים, צור מחלקת אימות ייעודית:

<?php
namespace XoopsModules\MyModule;
class Validator
{
private $errors = [];
public function validateItem(array $data): bool
{
$this->errors = [];
// Title validation
if (empty($data['title'])) {
$this->errors['title'] = 'Title is required';
} elseif (strlen($data['title']) > 255) {
$this->errors['title'] = 'Title must be 255 characters or less';
}
// Email validation
if (!empty($data['email'])) {
if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$this->errors['email'] = 'Invalid email format';
}
}
// Status validation
$allowed = ['draft', 'published'];
if (!in_array($data['status'], $allowed)) {
$this->errors['status'] = 'Invalid status';
}
return empty($this->errors);
}
public function getErrors(): array
{
return $this->errors;
}
public function getError(string $field): ?string
{
return $this->errors[$field] ?? null;
}
}

נוֹהָג:

$validator = new Validator();
$data = [
'title' => $_POST['title'],
'email' => $_POST['email'],
'status' => $_POST['status'],
];
if (!$validator->validateItem($data)) {
$errors = $validator->getErrors();
// Display errors to user
}

חיטוי לאחסון מסדי נתונים

Section titled “חיטוי לאחסון מסדי נתונים”

בעת אחסון נתונים במסד הנתונים:

$myts = MyTextSanitizer::getInstance();
// For storage (will be processed again on display)
$title = $myts->addSlashes($_POST['title']);
// Better: Use prepared statements (see SQL Injection Prevention)
$sql = "INSERT INTO " . $xoopsDB->prefix('mytable') . " (title) VALUES (?)";
$result = $xoopsDB->query($sql, [$_POST['title']]);

הקשרים שונים דורשים בריחה שונה:

$myts = MyTextSanitizer::getInstance();
// HTML context
echo $myts->htmlSpecialChars($title);
// Within HTML attributes
echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
// JavaScript context
echo json_encode($title);
// URL parameter
echo urlencode($title);
// Full URL
echo htmlspecialchars($url, ENT_QUOTES, 'UTF-8');

בעיה: הנתונים מקודדים מספר פעמים

// Wrong - double encoding
$title = $myts->htmlSpecialChars($myts->htmlSpecialChars($_POST['title']));
// Right - encode once, at the appropriate time
$title = $_POST['title']; // Store raw
echo $myts->htmlSpecialChars($title); // Encode on output

בעיה: חלק מהפלטים מקודדים, חלק לא

פתרון: השתמש תמיד בגישה עקבית, רצוי בקידוד בפלט:

// Template assignment
$GLOBALS['xoopsTpl']->assign('title', htmlspecialchars($title, ENT_QUOTES, 'UTF-8'));

בעיה: רק חיטוי ללא אימות

פתרון: תקף תמיד תחילה ולאחר מכן חיטוי:

// First validate
if (!preg_match('/^[a-z0-9_]+$/', $_POST['username'])) {
redirect_header('form.php', 3, 'Username contains invalid characters');
exit();
}
// Then sanitize for storage/display
$username = $myts->htmlSpecialChars($_POST['username']);

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

Section titled “סיכום שיטות עבודה מומלצות”
  1. השתמש ב-MyTextSanitizer לעיבוד תוכן טקסט
  2. השתמש ב-filter_var() לאימות פורמט ספציפי
  3. השתמש בהטלת סוג עבור ערכים מספריים
  4. רשימה הלבנה של ערכים מותרים עבור כניסות נבחרות
  5. אמת לפני חיטוי
  6. בריחה בפלט, לא בקלט
  7. השתמש בהצהרות מוכנות עבור שאילתות מסד נתונים
  8. צור מחלקות אימות לטפסים מורכבים
  9. לעולם אל תבטח באימות בצד הלקוח - אמת תמיד בצד השרת

#אבטחה #חיטוי #validation #xoops #MyTextSanitizer #טיפול בקלט