פגמים בלוגיקה עסקית - Business Logic Flaws¶
מהם פגמים בלוגיקה עסקית¶
פגמי לוגיקה עסקית (Business Logic Flaws) הם חולשות שנובעות מפגמים בתכנון ולא מבעיות טכניות. בניגוד ל-SQLi או XSS שבהם הבעיה היא בטיפול בקלט, כאן הקוד עובד בדיוק כמו שתוכנן - אבל התכנון עצמו שגוי.
חולשות אלו קשות לגילוי אוטומטי כי סורקים מחפשים בעיות טכניות. נדרשת הבנה עמוקה של הלוגיקה העסקית כדי למצוא אותן.
מניפולציית מחירים - Price Manipulation¶
שינוי מחיר בבקשה¶
אפליקציות שסומכות על הלקוח לשלוח את המחיר:
POST /cart/add HTTP/1.1
Host: shop.example.com
Content-Type: application/json
{
"productId": 42,
"quantity": 1,
"price": 999.99
}
תקיפה - שינוי המחיר:
POST /cart/add HTTP/1.1
Host: shop.example.com
Content-Type: application/json
{
"productId": 42,
"quantity": 1,
"price": 0.01
}
קוד פגיע:
app.post('/cart/add', (req, res) => {
const { productId, quantity, price } = req.body;
// פגיע! סומך על המחיר מהלקוח
cart.addItem({
productId,
quantity,
unitPrice: price,
total: price * quantity
});
res.json({ success: true });
});
כמויות שליליות¶
POST /cart/add HTTP/1.1
Host: shop.example.com
Content-Type: application/x-www-form-urlencoded
productId=1&quantity=-10&price=100
תוצאה: סך העגלה הופך לשלילי, מה שעלול לגרום לזיכוי:
// פגיע - אין בדיקת כמות חיובית
app.post('/cart/add', (req, res) => {
const product = db.getProduct(req.body.productId);
const quantity = parseInt(req.body.quantity);
const total = product.price * quantity; // 100 * (-10) = -1000
cart.addItem({ productId: product.id, quantity, total });
// סך העגלה: -1000. התשלום "יחזיר" כסף!
});
גלישת מספר שלם - Integer Overflow¶
בשפות עם מספרים שלמים קבועי גודל, ערך גדול מדי גולש ונהפך לשלילי:
// בשפת C או שפות עם integer overflow
int quantity = 2147483647; // MAX_INT
int price = 2;
int total = quantity * price; // overflow! total = -2
ניצול קופונים והנחות - Coupon/Discount Abuse¶
ערימת קופונים - Coupon Stacking¶
POST /cart/apply-coupon HTTP/1.1
code=SAVE10
POST /cart/apply-coupon HTTP/1.1
code=WELCOME20
POST /cart/apply-coupon HTTP/1.1
code=VIP30
קוד פגיע שלא מגביל מספר קופונים:
@app.route('/apply-coupon', methods=['POST'])
def apply_coupon():
code = request.form['code']
coupon = Coupon.query.filter_by(code=code).first()
if not coupon:
return jsonify({'error': 'Invalid coupon'}), 400
# פגיע - אין בדיקה כמה קופונים כבר הוחלו
cart = get_user_cart()
cart.discounts.append(coupon)
cart.total -= coupon.amount
db.session.commit()
return jsonify({'new_total': cart.total})
החלת הנחה אחרי שינוי מחיר¶
- הוספת מוצר זול (10 דולר) לעגלה
- החלת קופון 50% (הנחה = 5 דולר)
- שינוי המוצר בעגלה למוצר יקר (1000 דולר)
- ההנחה עדיין חלה - אבל האם היא 5 דולר או 50%?
// פגיע - ההנחה נשמרת כאחוז אבל לא מחושבת מחדש
app.post('/cart/update-item', (req, res) => {
const item = cart.getItem(req.body.itemId);
const newProduct = db.getProduct(req.body.newProductId);
// מעדכן את המוצר אבל לא מחשב מחדש הנחות
item.productId = newProduct.id;
item.price = newProduct.price;
// item.discount לא השתנה!
cart.save();
});
שימוש חוזר בקוד חד-פעמי¶
POST /cart/apply-coupon HTTP/1.1
code=ONETIME50
HTTP/1.1 200 OK
{"message": "50% discount applied"}
-- הסרת הקופון --
POST /cart/remove-coupon HTTP/1.1
code=ONETIME50
-- החלה מחדש --
POST /cart/apply-coupon HTTP/1.1
code=ONETIME50
HTTP/1.1 200 OK
{"message": "50% discount applied"}
הקוד בודק אם הקופון שומש - אבל הסרה ושימוש מחדש עוקפת את הבדיקה.
עקיפת תהליכי עבודה - Workflow Bypass¶
דילוג על שלבים בתהליך מרובה שלבים¶
תהליך רכישה תקין:
שלב 1: POST /cart/review -> בחירת מוצרים
שלב 2: POST /cart/shipping -> הזנת כתובת
שלב 3: POST /cart/payment -> תשלום
שלב 4: POST /cart/confirm -> אישור הזמנה
תקיפה - דילוג ישירות לאישור:
קוד פגיע:
@app.route('/cart/confirm', methods=['POST'])
def confirm_order():
cart = get_user_cart()
# פגיע - אין בדיקה שהתשלום בוצע
order = Order.create(
items=cart.items,
total=cart.total,
status='confirmed'
)
cart.clear()
return jsonify({'order_id': order.id})
ניצול מכונת מצבים - State Machine Abuse¶
גישה למצבים שלא אמורים להיות נגישים:
מצב תקין: PENDING -> APPROVED -> SHIPPED -> DELIVERED
תקיפה: PENDING -> DELIVERED (דילוג על APPROVED ו-SHIPPED)
@app.route('/order/update-status', methods=['POST'])
def update_status():
order = Order.query.get(request.form['orderId'])
new_status = request.form['status']
# פגיע - אין בדיקת מעבר מצב חוקי
order.status = new_status
db.session.commit()
if new_status == 'REFUNDED':
refund_payment(order) # מחזיר כסף להזמנה שמעולם לא שולמה!
return jsonify({'success': True})
פגמים באמון מרומז - Implicit Trust Flaws¶
אמון בולידציה צד-לקוח¶
<!-- ולידציה בצד לקוח בלבד -->
<form id="updateProfile">
<input name="email" type="email" required>
<input name="name" maxlength="50">
<!-- שדה נסתר שנשלח אוטומטית -->
<input name="role" type="hidden" value="user">
<button type="submit">עדכן</button>
</form>
תקיפה - שינוי השדה הנסתר:
POST /profile/update HTTP/1.1
Content-Type: application/x-www-form-urlencoded
email=attacker@evil.com&name=Hacker&role=admin
הסלמת הרשאות דרך עדכון פרופיל¶
app.post('/profile/update', (req, res) => {
const userId = req.session.userId;
// פגיע - מעדכן את כל השדות שנשלחו ללא סינון
User.findByIdAndUpdate(userId, req.body);
res.json({ success: true });
});
בקשה זדונית:
POST /profile/update HTTP/1.1
Content-Type: application/json
{
"name": "Normal User",
"email": "user@example.com",
"role": "admin",
"isVerified": true,
"balance": 999999
}
ניצול עיגול מטבע - Currency Rounding Exploitation¶
# מחיר מוצר: $0.01
# כמות: 1
# הנחה: 50%
price = 0.01
discount = 0.5
discounted_price = price * (1 - discount) # $0.005
# עיגול כלפי מטה -> $0.00
# המוצר חינמי!
# תקיפה: רכישת מוצרים ב-$0.005 שמעוגלים ל-$0.00
# ואז מכירתם חזרה ב-$0.01
@app.route('/purchase', methods=['POST'])
def purchase():
item_price = 0.01
quantity = int(request.form['quantity'])
discount = get_user_discount() # 0.5 = 50%
# פגיע - עיגול לטובת הקונה
total = round(item_price * quantity * (1 - discount), 2)
# 0.01 * 1 * 0.5 = 0.005 -> round(0.005, 2) = 0.0
if total <= user.balance:
user.balance -= total # מוריד 0 מהיתרה
add_items_to_inventory(user, quantity)
מניפולציית מלאי - Inventory Manipulation¶
-- הוספת 1000 פריטים לעגלה (מרוקנים את המלאי)
POST /cart/add HTTP/1.1
productId=1&quantity=1000
-- מלאי המוצר: 0. אף אחד אחר לא יכול לקנות
-- המתנה עד שהמחיר יורד
-- עדכון כמות לפריט אחד ורכישה במחיר המוזל
POST /cart/update HTTP/1.1
productId=1&quantity=1
ניצול מערכת הפניות - Referral System Abuse¶
-- יצירת חשבון עם לינק הפניה של עצמנו
POST /register HTTP/1.1
email=user1@temp.com&referralCode=MY_CODE
-- שני הצדדים מקבלים בונוס
-- חוזרים על התהליך עם כתובות זמניות
POST /register HTTP/1.1
email=user2@temp.com&referralCode=MY_CODE
POST /register HTTP/1.1
email=user3@temp.com&referralCode=MY_CODE
קוד פגיע:
app.post('/register', async (req, res) => {
const user = await User.create(req.body);
if (req.body.referralCode) {
const referrer = await User.findOne({
referralCode: req.body.referralCode
});
// פגיע - אין בדיקה שהמפנה והנרשם הם אנשים שונים
// אין הגבלה על מספר ההפניות
if (referrer) {
referrer.balance += 10; // בונוס למפנה
user.balance += 5; // בונוס לנרשם
await referrer.save();
}
}
await user.save();
res.json({ success: true });
});
דוגמה מקיפה - חנות מקוונת פגיעה¶
const express = require('express');
const app = express();
// מודל מוצר
const products = {
1: { name: 'Laptop', price: 1500, stock: 10 },
2: { name: 'Phone', price: 800, stock: 20 },
3: { name: 'Cable', price: 5, stock: 100 }
};
// פגיעות 1: אין ולידציה של מחיר בצד שרת
app.post('/api/cart/add', (req, res) => {
const { productId, quantity, price } = req.body;
cart.items.push({ productId, quantity, unitPrice: price });
res.json({ success: true });
});
// פגיעות 2: אין בדיקת כמות שלילית
app.post('/api/cart/update', (req, res) => {
const item = cart.getItem(req.body.itemId);
item.quantity = req.body.quantity; // יכול להיות שלילי!
cart.recalculateTotal();
res.json({ total: cart.total });
});
// פגיעות 3: ערימת קופונים ללא הגבלה
app.post('/api/cart/coupon', (req, res) => {
const coupon = getCoupon(req.body.code);
if (coupon) {
cart.appliedCoupons.push(coupon);
cart.total -= coupon.discount;
}
res.json({ total: cart.total });
});
// פגיעות 4: אישור הזמנה ללא בדיקת תשלום
app.post('/api/order/confirm', (req, res) => {
const order = createOrder(cart);
order.status = 'confirmed';
cart.clear();
res.json({ orderId: order.id });
});
// פגיעות 5: עדכון סטטוס ללא בדיקת מעבר חוקי
app.post('/api/order/status', (req, res) => {
const order = getOrder(req.body.orderId);
order.status = req.body.status;
if (req.body.status === 'refunded') {
refundUser(order.userId, order.total);
}
res.json({ success: true });
});
הגנות¶
ולידציה בצד שרת לכל כלל עסקי¶
app.post('/cart/add', (req, res) => {
const product = db.getProduct(req.body.productId);
// מוגן - המחיר נלקח מבסיס הנתונים, לא מהלקוח
const price = product.price;
// ולידציה של כמות
const quantity = parseInt(req.body.quantity);
if (quantity <= 0 || quantity > product.stock) {
return res.status(400).json({ error: 'Invalid quantity' });
}
cart.addItem({
productId: product.id,
quantity,
unitPrice: price,
total: price * quantity
});
});
בדיקת מעברי מצב חוקיים¶
VALID_TRANSITIONS = {
'PENDING': ['APPROVED', 'CANCELLED'],
'APPROVED': ['SHIPPED', 'CANCELLED'],
'SHIPPED': ['DELIVERED'],
'DELIVERED': ['REFUNDED'],
'CANCELLED': [],
'REFUNDED': []
}
def update_order_status(order, new_status):
if new_status not in VALID_TRANSITIONS.get(order.status, []):
raise ValueError(
f'Invalid transition: {order.status} -> {new_status}'
)
order.status = new_status
הגבלת קופונים¶
def apply_coupon(cart, code):
# בדיקה שלא הוחלו יותר מדי קופונים
if len(cart.applied_coupons) >= MAX_COUPONS:
raise ValueError('Maximum coupons reached')
# בדיקה שהקופון לא שומש
if code in cart.applied_coupons:
raise ValueError('Coupon already applied')
# בדיקה שהקופון תקף
coupon = Coupon.query.filter_by(
code=code,
is_active=True
).first()
if not coupon or coupon.expiry < datetime.now():
raise ValueError('Invalid or expired coupon')
cart.applied_coupons.append(code)
cart.recalculate_total()
סיכום¶
פגמי לוגיקה עסקית דורשים חשיבה יצירתית ולא כלים טכניים. הנקודות המרכזיות:
- בדקו כל הנחה שהאפליקציה עושה לגבי התנהגות המשתמש
- נסו כמויות שליליות, ערכים קיצוניים, ודילוג על שלבים
- חפשו שדות נסתרים שניתן לשנות
- בדקו אם ולידציה מתבצעת בצד הלקוח בלבד
- מפו את כל מעברי המצב האפשריים ובדקו מעברים לא חוקיים
- הגנה: ולידציה בצד שרת לכל כלל עסקי, ללא יוצא מן הכלל