ความปลอดภัยเวบฮุก
เรียนรู้วิธีการรักษาความปลอดภัยของจุดสิ้นสุดเวบฮุกโดยใช้การตรวจสอบลายเซ็น ป้องกันการวนซ้ำ และใช้แนวทางปฏิบัติที่ดีด้านความปลอดภัย
ภาพรวม
ความปลอดภัยของเวบฮุกมีความสำคัญเพื่อให้แน่ใจว่าเหตุการณ์เวบฮุกที่ได้รับเป็นของแท้และส่งโดย Omise คำแนะนำนี้ครอบคลุม:
- การตรวจสอบลายเซ็น HMAC-SHA256
- การเปรียบเทียบสตริงที่ปลอดภัยจากการกำหนดเวลา
- การจัดการคีย์ลับเวบฮุก
- ขั้นตอนการหมุนเวียนลับ
- ป้องกันการโจมตีเวลาวน
การตรวจสอบลายเซ็น
Omise ลงนามในคำขอเวบฮุกทั้งหมดด้วย HMAC-SHA256 โดยใช้คีย์ลับเวบฮุกของคุณ ลายเซ็นอยู่ในส่วนหัว X-Omise-Signature HTTP
วิธีการตรวจสอบลายเซ็น
- Omise สร้างแฮช HMAC-SHA256 ของเนื้อหาคำขอดิบโดยใช้คีย์ลับของคุณ
- ลายเซ็นถูกส่งในส่วนหัว
X-Omise-Signature - เซิร์ฟเวอร์ของคุณคำนวณแฮช HMAC-SHA256 เดียวกัน
- เปรียบเทียบลายเซ็นที่คำนวณกับลายเซ็นที่ได้รับ
- ประมวลผลเวบฮุกเฉพาะหากลายเซ็นตรงกัน
ตัวอย่างการใช้งาน
// Node.js - การตรวจสอบลายเซ็น
const crypto = require('crypto');
function verifySignature(rawBody, signature, secretKey) {
const expectedSignature = crypto
.createHmac('sha256', secretKey)
.update(rawBody)
.digest('hex');
// ใช้การเปรียบเทียบที่ปลอดภัยจากการกำหนดเวลา
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/omise', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-omise-signature'];
const rawBody = req.body;
if (!signature || !verifySignature(rawBody, signature, process.env.OMISE_WEBHOOK_KEY)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(rawBody.toString());
res.status(200).json({ received: true });
processWebhook(event);
});
# Python - การตรวจสอบลายเซ็น
import hmac
import hashlib
def verify_signature(payload, signature, secret_key):
if isinstance(payload, str):
payload = payload.encode('utf-8')
expected_signature = hmac.new(
secret_key.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
@app.route('/webhooks/omise', methods=['POST'])
def handle_webhook():
payload = request.get_data()
signature = request.headers.get('X-Omise-Signature')
if not signature or not verify_signature(payload, signature, os.environ['OMISE_WEBHOOK_KEY']):
return jsonify({'error': 'Invalid signature'}), 401
event = request.get_json()
response = jsonify({'received': True})
process_webhook_async(event)
return response, 200
# Ruby - การตรวจสอบลายเซ็น
require 'openssl'
def verify_signature(payload, signature, secret_key)
expected_signature = OpenSSL::HMAC.hexdigest(
OpenSSL::Digest.new('sha256'),
secret_key,
payload
)
ActiveSupport::SecurityUtils.secure_compare(signature, expected_signature)
end
post '/webhooks/omise' do
payload = request.body.read
signature = request.env['HTTP_X_OMISE_SIGNATURE']
unless signature && verify_signature(payload, signature, ENV['OMISE_WEBHOOK_KEY'])
halt 401, { error: 'Invalid signature' }.to_json
end
event = JSON.parse(payload)
status 200
{ received: true }.to_json
end
การเปรียบเทียบที่ปลอดภัยจากการกำหนดเวลา
ใช้ฟังก์ชันการเปรียบเทียบที่ปลอดภัยจากการกำหนดเวลาเสมอเพื่อป้องกันการโจมตีการกำหนดเวลา การเปรียบเทียบสตริงปกติ (==, ===) สามารถรั่วไหลข้อมูลลายเซ็นผ่านความแตกต่างของการกำหนดเวลา
ฟังก์ชันการเปรียบเทียบที่ปลอดภัยจากการกำหนดเวลา
| ภาษา | ฟังก์ชัน |
|---|---|
| Node.js | crypto.timingSafeEqual() |
| Python | hmac.compare_digest() |
| Ruby | Rack::Utils.secure_compare() หรือ ActiveSupport::SecurityUtils.secure_compare() |
| PHP | hash_equals() |
| Go | subtle.ConstantTimeCompare() |