HTML فارم کا دستخط
گاہک براؤزر میں ہی فارم کی کوئی بھی فیلڈ بدل سکتا ہے — مثال کے طور پر، کم رقم لکھ سکتا ہے۔ اسی لیے انوائس کی فیلڈز دستخط شدہ کنٹینر میں جاتی ہیں: سروس دستخط کا دوبارہ حساب لگاتی ہے اور اگر وہ مماثل نہ ہو تو انوائس نہیں بناتی۔
اسٹور کا سرور دستخط کا حساب لگاتا ہے۔ براؤزر تک صرف تیار data اور signature کا جوڑا پہنچتا ہے۔
دستخط کس چیز پر ہوتا ہے
data = base64url(json)
signature = HMAC-SHA256(data, formSecret)
HMAC کا حساب data اسٹرنگ پر ہوتا ہے، JSON کے بائٹس پر نہیں۔ وصول کنندہ دستخط کو بھیجی گئی اسٹرنگ سے جوں کا توں جانچتا ہے، اور صرف اس کے بعد اسے ڈی کوڈ کرتا ہے۔
اس سے یہ نتیجہ نکلتا ہے کہ کوئی canonicalization نہیں ہوتی: کلیدوں کی ترتیب، JSON کی انڈینٹیشن اور Unicode escaping کا طریقہ دستخط پر اثر نہیں ڈالتے۔ کوئی بھی معیاری JSON انکوڈر کام کرتا ہے۔
حوالہ جاتی سیٹ
یہ قدریں اپنے کوڈ میں ڈالیں اور نتیجے کا موازنہ کریں۔
مثال کی خفیہ کلید:
dvc3khto5WpjEPojZfYJsbJzFEPPVnRa
رقم اور ادائیگی کی مہلت نمبرز کی صورت میں؛ اسٹرنگز بھی قبول ہیں:
{"projectId":"a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d","amountFiat":10.5,"currencyFiat":"USD","timeToPay":1,"description":"Order #7, delivery","serviceData":"order-7"}
data اسٹرنگ انہی بائٹس کا base64url ہے۔ یہاں پیڈنگ ہٹی ہوئی ہے؛ وصول کنندہ اسے پیڈنگ کے ساتھ بھی قبول کرتا ہے:
eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZmLTRhN2ItOGM5ZC0wZTFmMmEzYjRjNWQiLCJhbW91bnRGaWF0IjoxMC41LCJjdXJyZW5jeUZpYXQiOiJVU0QiLCJ0aW1lVG9QYXkiOjEsImRlc2NyaXB0aW9uIjoiT3JkZXIgIzcsIGRlbGl2ZXJ5Iiwic2VydmljZURhdGEiOiJvcmRlci03In0
اوپر والی خفیہ کلید کے ساتھ اس اسٹرنگ کا دستخط:
09628020c47400c9231420450af642adfdb3c246f33e3e12cb8e28f96c2a785e
ان قدروں کے ساتھ نیچے کی مثالیں بالکل یہی data اور دستخط پرنٹ کرتی ہیں۔ یہی JSON، آخر میں "output":"errors" کلید کے ساتھ، ایک مختلف data اسٹرنگ اور مختلف دستخط دیتا ہے۔
اگر JSON ایک جیسا نظر آنے کے باوجود data مماثل نہیں ہے، تو وجہ JSON انکوڈر ہے: کلیدوں کی مختلف ترتیب یا فاصل کے بعد خالی جگہیں۔ وصول کنندہ ایسا کنٹینر قبول کرتا ہے، لیکن وہ حوالے سے حرف بہ حرف مماثل نہیں ہوتا۔
مثالیں
- PHP
- TypeScript
- JavaScript
- Python
<?php
function signedContainer(array $fields, string $formSecret): array {
$data = rtrim(strtr(base64_encode(json_encode($fields)), '+/', '-_'), '=');
return [
'data' => $data,
'signature' => hash_hmac('sha256', $data, $formSecret),
];
}
// Test values from the reference set. Before going live,
// replace the secret with the form secret from your project settings
$container = signedContainer([
'projectId' => 'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
'amountFiat' => 10.5,
'currencyFiat' => 'USD',
'timeToPay' => 1,
'description' => 'Order #7, delivery',
'serviceData' => 'order-7',
], 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');
echo $container['data'], "\n";
echo $container['signature'], "\n";
import { createHmac } from 'node:crypto';
// Values are JSON strings or numbers, optional keys may be omitted
export interface FormFields {
projectId: string;
amountFiat: string | number;
currencyFiat: string;
timeToPay: string | number;
description?: string;
serviceData?: string;
output?: string;
}
export interface FormContainer {
data: string;
signature: string;
}
export const signedContainer = (fields: FormFields, formSecret: string): FormContainer => {
const data = Buffer.from(JSON.stringify(fields), 'utf8').toString('base64url');
return {
data,
signature: createHmac('sha256', formSecret).update(data, 'utf8').digest('hex'),
};
};
// Test values from the reference set. Before going live,
// replace the secret with the form secret from your project settings
const container = signedContainer({
projectId: 'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
amountFiat: 10.5,
currencyFiat: 'USD',
timeToPay: 1,
description: 'Order #7, delivery',
serviceData: 'order-7',
}, 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');
console.log(container.data);
console.log(container.signature);
یہ اسٹور کے سرور کا کوڈ ہے، براؤزر کا نہیں: دستخط کا حساب وہیں لگتا ہے جہاں خفیہ کلید محفوظ ہے۔
import { createHmac } from 'node:crypto';
const signedContainer = (fields, formSecret) => {
const data = Buffer.from(JSON.stringify(fields), 'utf8').toString('base64url');
return {
data,
signature: createHmac('sha256', formSecret).update(data, 'utf8').digest('hex'),
};
};
// Test values from the reference set. Before going live,
// replace the secret with the form secret from your project settings
const container = signedContainer({
projectId: 'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
amountFiat: 10.5,
currencyFiat: 'USD',
timeToPay: 1,
description: 'Order #7, delivery',
serviceData: 'order-7',
}, 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');
console.log(container.data);
console.log(container.signature);
import base64
import hashlib
import hmac
import json
def signed_container(fields: dict, form_secret: str) -> dict:
# compact separators: the default ones add spaces after ',' and ':'
body = json.dumps(fields, separators=(',', ':'))
data = base64.urlsafe_b64encode(body.encode('utf-8')).decode().rstrip('=')
return {
'data': data,
'signature': hmac.new(
form_secret.encode('utf-8'), data.encode('utf-8'), hashlib.sha256
).hexdigest(),
}
# Test values from the reference set. Before going live,
# replace the secret with the form secret from your project settings
container = signed_container({
'projectId': 'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
'amountFiat': 10.5,
'currencyFiat': 'USD',
'timeToPay': 1,
'description': 'Order #7, delivery',
'serviceData': 'order-7',
}, 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa')
print(container['data'])
print(container['signature'])
حوالے سے حرف بہ حرف مطابقت کے لیے separators آرگیومنٹ ضروری ہے۔ وصول کنندہ JSON کی ڈیفالٹ سیٹنگز کے ساتھ بھی ٹھیک کام کرتا ہے۔
مثال کی قدریں ASCII ہیں، لیکن کوڈ بغیر کسی تبدیلی کے کسی بھی Unicode کے ساتھ کام کرتا ہے۔ PHP اور Python بطور ڈیفالٹ غیر ASCII حروف کو \uXXXX کی صورت میں escape کرتے ہیں، جبکہ JavaScript خام UTF-8 بھیجتا ہے — وصول کنندہ دونوں قبول کرتا ہے۔
دو عام غلطیاں
کنٹینر کے بجائے JSON پر دستخط ہوتا ہے۔ HMAC کا حساب data اسٹرنگ پر ہوتا ہے، یعنی base64url پر۔ یہ غلطی «سب کچھ مماثل ہے، لیکن دستخط نہیں» جیسی نظر آتی ہے۔
دستخط کے بعد کنٹینر دوبارہ بنایا جاتا ہے۔ اگر data کا ایک بائٹ بھی بدل جائے، تو دستخط غلط ہو جاتا ہے۔ جوڑا اپنے کوڈ میں ایک ہی جگہ بنائیں۔
اگر دستخط مماثل نہ ہو
JSON سے data تک اور data سے دستخط تک کی زنجیر کو مرحلہ وار جانچیں؛ حوالے سے سب سے پہلے الگ ہونے والی کڑی ہی وجہ ہے۔ پہلے data جانچیں: یہ خفیہ کلید پر منحصر نہیں۔