در منظره مدرن توسعه وب، ایمنسازی رابطهای برنامهنویسی (API) نه تنها یک بهترین روش، بلکه یک الزام اساسی است. با ارتباط برنامههای تکصفحهای (SPA) و اپلیکیشنهای موبایل با سرورهای بکاند، احراز هویت مبتنی بر جلسه اغلب دشوار میشود. اینجاست که توکنهای وب JSON (JWT) درخشش میکنند. JWT به استاندارد صنعتی برای احراز هویت بدون حالت تبدیل شده است و به توسعهدهندگان اجازه میدهد هویت کاربر را به صورت امن بین کلاینت و سرور منتقل کنند بدون اینکه دادههای جلسه در سمت سرور ذخیره شود. در این مقاله، مکانیکهای JWT را کالبدشکافی میکنیم، پیادهسازی آن را در پایتون بررسی میکنیم و ملاحظات امنیتی حیاتی را که اغلب پیادهسازیهای آماتور را از سیستمهای آماده برای تولید جدا میکند، بحث خواهیم کرد.
درک آناتومی یک JWT
قبل از غرق شدن در کد، درک اینکه یک JWT واقعاً چیست ضروری است. یک JWT یک استاندارد باز (RFC 7519) است که روشی فشرده و خودکفا برای انتقال امن اطلاعات بین طرفین به عنوان یک شیء JSON تعریف میکند. این اطلاعات قابل بررسی و اعتماد است زیرا به صورت دیجیتال امضا شده است. JWTها را میتوان با استفاده از یک راز (با الگوریتم HMAC) یا یک جفت کلید عمومی/خصوصی با استفاده از RSA یا ECDSA امضا کرد.
یک JWT از سه بخش تشکیل شده است که با نقطه (.) از هم جدا میشوند:
- هدر (Header): معمولاً شامل دو بخش است: نوع توکن (JWT) و الگوریتم امضای مورد استفاده (مثلاً HS256، RS256).
- پالود (Payload): شامل ادعاها (Claims) است. ادعاها بیانیههایی درباره یک موجودیت (معمولاً کاربر) و دادههای اضافی هستند. سه نوع ادعا وجود دارد: ثبتشده، عمومی و خصوصی.
- امضا (Signature): با گرفتن هدر رمزنگاریشده، پالود رمزنگاریشده، یک راز و الگوریتم مشخصشده در هدر ایجاد میشود و سپس آن امضا میگردد.
توکن حاصل به این شکل به نظر میرسد:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
پیادهسازی JWT در پایتون با Flask
برای این مثال عملی، ما از پایتون همراه با Flask و کتابخانه PyJWT استفاده خواهیم کرد. این پیکربندی نشان میدهد که چگونه توکنها را تولید، کدگذاری و تأیید کنیم. ابتدا مطمئن شوید که کتابخانه مورد نیاز نصب شده است:
pip install flask pyjwt
در زیر یک پیادهسازی اولیه از یک برنامه Flask آمده است که ورود کاربر و تولید توکن را مدیریت میکند. توجه داشته باشید که در یک محیط تولید، هرگز نباید رازها را به صورت سختکد شده وارد کنید یا رمزهای عبور را به صورت متن ساده ذخیره کنید.
import jwt
import datetime
from flask import Flask, request, jsonify
app = Flask(__name__)
# در محیط تولید، از متغیرهای محیطی استفاده کنید
SECRET_KEY = "your-super-secret-key-change-this-in-production"
def generate_token(user_id):
"""تولید یک توکن JWT با انقضا."""
payload = {
'user_id': user_id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1),
'iat': datetime.datetime.utcnow()
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
return token
@app.route('/login', methods=['POST'])
def login():
"""نقطه پایانی ورود ساده."""
data = request.json
# در واقعیت، اعتبارنامهها را در اینجا با پایگاه داده بررسی کنید
if data.get('username') == 'admin' and data.get('password') == 'password':
token = generate_token(1)
return jsonify({'token': token})
return jsonify({'message': 'Invalid credentials'}), 401
@app.route('/protected', methods=['GET'])
def protected_route():
"""مسیری محافظتشده که نیاز به JWT معتبر دارد."""
token = request.headers.get('Authorization').split(" ")[1]
try:
# تأیید و رمزگشایی توکن
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return jsonify({'message': 'Access granted to user', 'user_id': data['user_id']})
except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token'}), 401
if __name__ == '__main__':
app.run(debug=True)
بهترین شیوههای امنیتی و دامهای رایج
اگرچه JWTها قدرتمند هستند، اما پیادهسازی نادرست میتواند منجر به آسیبپذیریهای امنیتی شدید شود. در اینجا قوانین حیاتی برای دنبال کردن آورده شده است:
1. هرگز دادههای حساس را در پالود ذخیره نکنید: از آنجایی که پالود فقط رمزنگاری شده با base64 است و رمزنگاری نشده است، هر کسی که توکن را داشته باشد میتواند محتوای آن را بخواند. هرگز رمزهای عبور، اطلاعات هویتی شخصی (PII) یا دادههای مالی حساس را در پالود JWT قرار ندهید. اگر نیاز به ذخیره دادههای حساس دارید، یک شناسه مرجع را در توکن ذخیره کنید و جزئیات را از یک پایگاه داده امن دریافت کنید.
2. همیشه الگوریتم را اعتبارسنجی کنید: یکی از رایجترین حملات علیه پیادهسازیهای JWT، تغییر الگوریتم است. اگر سرور توکنهای RS256 را میپذیرد اما مهاجم یک توکن HS256 را که با کلید عمومی امضا شده است ارائه میدهد، سرور ممکن است آن را به اشتباه تأیید کند. همیشه الگوریتمهای مجاز را به صراحت مشخص کنید (مثلاً algorithms=["HS256"]) در تابع رمزگشایی خود.
3. عمر کوتاه را با توکنهای تازهسازی پیادهسازی کنید: توکنهای دسترسی باید عمر کوتاهی (مثلاً 15 دقیقه) داشته باشند تا پنجره فرصت برای مهاجمی که توکن را دزدیده است محدود شود. از توکنهای تازهسازی برای دریافت توکنهای دسترسی جدید بدون نیاز به ورود مجدد کاربر استفاده کنید. توکنهای تازهسازی باید به صورت امن (مثلاً کوکیهای HttpOnly) ذخیره و در سمت سرور یا با سیاستهای چرخش مدیریت شوند.
4. انتقال را ایمن کنید: همیشه اپلیکیشن خود را از طریق HTTPS ارائه دهید. JWTها معمولاً در هدر Authorization منتقل میشوند. اگر از طریق HTTP منتقل شوند، توکن میتواند از طریق حملات مرد میانی (Man-in-the-Middle) دستکاری شود.
نتیجهگیری
احراز هویت JWT یک راهحل قوی و مقیاسپذیر برای ایمنسازی اپلیکیشنهای وب مدرن ارائه میدهد. با درک ساختار آن، پیادهسازی صحیح آن با استفاده از کتابخانههایی مانند PyJWT و پایبندی به دستورالعملهای امنیتی سختگیرانه، توسعهدهندگان میتوانند از APIهای خود در برابر دسترسی غیرمجاز محافظت کنند. به یاد داشته باشید که امنیت یک فرآیند مداوم است؛ جریانهای احراز هویت خود را به طور منظم حسابرسی کنید، وابستگیها را بهروز نگه دارید و در مورد تهدیدات نوظهور آگاه بمانید. با رویکرد درست، JWTها میتوانند متحد قدرتمندی در جعبه ابزار امنیت اپلیکیشن شما باشند.