نحوه ایمن سازی یک برنامه هوش مصنوعی Next.js که در Vercel مستقر شده است
در این راهنمای عمیق، نحوه ایمن سازی یک برنامه هوش مصنوعی Next.js که در Vercel مستقر شده است را نشان خواهم داد. ما با شروع با یک برنامه ساده هوش مصنوعی پر از آسیب پذیری، رویکرد عملی را در پیش خواهیم گرفت. این مقاله شما را راهنمایی میکند که چگونه میتوانید آسیبپذیریها را شناسایی کنید و اصلاحات را در یک برنامه هوش مصنوعی Next.js فعلی اعمال کنید.
این آموزش زمینه های مختلف توسعه نرم افزار مانند تمام پشته، توسعه ابری و ادغام شخص ثالث را پوشش می دهد. شما یاد خواهید گرفت که چگونه از ابزارهایی مانند Git/GitHub، Next.js و Vercel برای بهبود امنیت یک برنامه هوش مصنوعی استفاده کنید.
همچنین، اگر تماشای پیادهسازیهای واقعی را ترجیح میدهید، در انتهای بیشتر بخشها ویدیوهای YouTube وجود دارد تا نحوه انجام آن را به شما نشان دهد.
فهرست مطالب
چگونه کلیدهای OpenAI API خود را دریافت کنید
آسیبپذیری اول: قرار گرفتن در معرض دادههای حساس در قسمت جلو
آسیب پذیری دوم: حملات DOS یا DDOS
حفاظت DDOS Vercel و Cloudflare
آسیب پذیری 3: بدون احراز هویت و مجوز
پیش نیازها
قبل از شروع این آموزش، در اینجا مواردی وجود دارد که باید داشته باشید:
دانش اولیه نوشتن کد Next.js
یک ویرایشگر کد (مانند VSCode) برای نوشتن و ویرایش کد منبع پروژه.
حساب Git و GitHub برای کنترل نسخه، ادغام مداوم و احراز هویت.
یک حساب Vercel برای میزبانی پروژه.
یک حساب توسعه دهنده OpenAI برای دریافت کلید API شما.
شروع به کار
ابتدا باید این مخزن GitHub را کلون کنید.
من مخزن را به طور خاص برای این آموزش ایجاد کردم تا برخی از آسیب پذیری هایی را که ممکن است هنگام ساختن یک برنامه هوش مصنوعی Next.js نادیده بگیرید، به شما نشان دهم.
برای شبیه سازی پروژه، رابط خط فرمان خود را باز کنید و به پوشه ای که می خواهید مخزن در آن باشد بروید.
سپس از دستور git clone
برای کلون کردن پروژه استفاده کنید.
git clone https://github.com/Gidthecoder/nextjs-ai.git
به یاد داشته باشید که باید قبلاً Git را در سیستم خود راه اندازی کرده باشید وگرنه دستور کار نخواهد کرد.
پس از انجام این کار، با استفاده از دستور زیر به پوشه بروید:
cd nextjs-ai
سپس دستور زیر را برای نصب وابستگی های پروژه تایپ کنید:
npm install
پس از انجام این کار، می توانید پوشه را در ویرایشگر متن خود باز کنید و فایل ها را کاوش کنید.
کد به خوبی کامنت گذاری شده است، پس در درک آن نباید مشکل زیادی داشته باشید.
برای اجرای پروژه، ابتدا مطمئن شوید که در دایرکتوری پروژه در CLI خود قرار دارید. سپس دستور زیر را اجرا کنید:
npm run dev
اگر همه چیز به خوبی کار می کند، مرورگر خود را باز کنید و http://localhost:3000/
را در نوار جستجو تایپ کنید.
نتیجه مرورگر باید به این صورت باشد:
که پروژه را در مرورگر نشان می دهد" class="image--center mx-auto" width="1913" height="919" loading="lazy">
اگر به همین نتیجه رسیدید، خودتان را تشویق کنید. کار عالی
اگر سعی کنید از فرم پیام ارسال کنید، متوجه خواهید شد که یک پیام خطا نمایش داده می شود. دلیلش این است که کلید OpenAI API را به پروژه اضافه نکرده اید.
اما فعلا نگران این نباشید. بیایید نگاهی به فایل های پروژه بیندازیم.
اینم ویدیوی این بخش:
کاوش در فایل های پروژه
پروژه ای که ما شبیه سازی کردیم اساسا یک بسته بندی ChatGPT است. شما از آن سوال می پرسید و به شما پاسخ می دهد.
در زیر هود، پروژه از Next.js برای ارسال درخواست API به سرور OpenAI استفاده می کند و پاسخ را به کاربر نمایش می دهد.
اگر پوشه را از طریق VSCode باز کرده باشید، ساختار پروژه باید به شکل زیر باشد:
که ساختار پوشه پروژه را نشان می دهد" class="image--center mx-auto" width="535" height="719" loading="lazy">
این پروژه دارای 4 پوشه سطح بالا است: .next
، node_modules
، public
و src
.
فایلهای موجود در پوشه ریشه شامل .gitignore
، package.json
، tailwind.config.js
، tsconfig.json
و موارد دیگر هستند.
بیشتر کارهای شما در پوشه src/app
خواهد بود زیرا حاوی تمام کدهایی است که برای پروژه نیاز دارید.
پوشه برنامه نشان دهنده روتر برنامه است. این شامل layout.tsx
، page.tsx
، global.css
، پوشه hooks
و غیره است.
فایل layout.tsx
layout.tsx
یک فایل TypeScript است که حاوی کد طرح بندی ریشه است.
از خطوط 1 تا 3، نوع Metadata
، فونت Inter
و یک فایل globals.css
را وارد میکنید که به شما امکان میدهد از کلاسهای ابزار TailwindCSS استفاده کنید.
import type { Metadata } from "next" ; import { Inter } from "next/font/google" ; import "./globals.css" ;
از خط 5 تا 10، 2 متغیرهای inter
و metadata
را ایجاد خواهید کرد. inter
فونت Inter
را مقداردهی اولیه می کند و metadata
یک شی را ذخیره می کند که حاوی عنوان و توضیحات سایت است، مشابه عنوان HTML و عناصر متا.
const inter = Inter({ subsets : [ "latin" ] }); export const metadata: Metadata = { title : "WriterAI" , description : "A ChatGPT wrapper that answers your questions" , };
در مرحله بعد، مولفه RootLayout
را ایجاد می کنید که تمام صفحات پروژه را می پوشاند.
export default function RootLayout ( { children, }: Readonly<{ children: React.ReactNode; }> ) { return ( < html lang = "en" > < body className = {inter.className} > {children} </ body > </ html > ); }
فایل page.tsx
app/page.tsx
یک فایل TypeScript است که نشان دهنده کدی است که هنگام فراخوانی URL ریشه (/) در مرورگر ارائه می شود.
خط 1 دارای دستورالعمل use client
است که فایل را به عنوان یک مؤلفه مشتری اعلام می کند. به این ترتیب ما می توانیم به طور کامل از ویژگی های React.js در کامپوننت استفاده کنیم.
"use client" ;
از خط 3 تا 6، یک قلاب useOpenAI
سفارشی و قلاب های داخلی React را وارد خواهید کرد ( useState
، useRef
، useEffect
، …).
//import the custom hook for getting the response from the OpenAI API import useOpenAI from './hooks/useOpenAI' ; import {useState, useRef, useEffect, FormEvent} from 'react' ;
سپس یک نوع Message
سفارشی ایجاد میکنید که با حالتی که پیامها را بین کاربر و OpenAI API ذخیره میکند، استفاده میشود.
type Message = { role : string; content: string; };
خطوط 13 تا 120 حاوی کد مولفه Home
است که با فراخوانی URL ریشه در مرورگر ارائه می شود.
در مولفه Home
، خط 15 قلاب useOpenAI
را مقداردهی اولیه می کند.
//initialize the custom hook const getCompletion = useOpenAI();
خط 17 یک قلاب useRef
را راهاندازی میکند که برای ارجاع به عنصر ظرف DOM که پیامها را بین کاربر و سرور میپیچد، استفاده میشود.
const chatContainerRef = useRef<HTMLDivElement>( null );
از خطوط 19 تا 23، شما یک متغیر content
ایجاد می کنید که نشان دهنده آرایه ای است که به عنوان مقادیر اولیه برای حالتی که چت/پیام ها را ذخیره می کند، استفاده می شود.
//initial chats for the site let content: Message[] = [ { role : "user" , content : "Are you ready to write about any topic for me" }, { role : "assistant" , content : "Always ready bruv. what is your topic?" } ]
خطوط 25 تا 30 متغیرهای حالت مولفه را نشان می دهند. Input
متن وارد شده در عنصر ورودی توسط کاربر را ردیابی می کند. chats
پیام های کاربر و OpenAI API را ذخیره می کنند. با متغیر content
مقداردهی اولیه می شود. isTyping
زمان تایپ کردن کاربر را ردیابی می کند. مقدار اولیه آن false
است.
//this state stores the input value let [input, setInput] = useState<string>( '' ); //this state stores the chats let [chats, setChats] = useState<Message[]>(content); //this state keeps track of when the AI is typing let [isTyping, setIsTyping] = useState<boolean>( false );
کد در خطوط 31 تا 72 نشان دهنده تابع handlerChat
است که کنترل کننده رویداد است که هر زمانی که کاربر کلید enter یا دکمه درخواست را در فرم بزند فراخوانی می شود.
به طور خلاصه، وظیفه آن این است که درخواست را از فرم دریافت کند، وضعیت های input
، isTyping
و chats
را به روز کند، اعلان را به تابع getCompletion
قلاب useOpenAI
ارسال کند، منتظر پاسخ بماند و آن را به کاربر نمایش دهد.
//handleChat event handler for the submit event let handleChat = async (prompt: string, e : FormEvent) => { //prevent the form from reloading the entire page when submitting e.preventDefault(); //if there is no value in the input or it is clicked when the isTyping is true, do nothing if (!prompt || isTyping) return ; //set isTyping state to true. 'true' adds an element displaying 'AI typing' setIsTyping( true ); //clear the content of the input state. This also clears the input element which displays the value. setInput( '' ); //updates the chats state with the prompt sent from the input setChats( prevChats => { const updatedChats = [...prevChats, { role : 'user' , content : prompt}]; return updatedChats; }); try { //send the prompt through the openai api and wait for the response const result = await getCompletion(prompt); //update the chat prompt with response gotten from the openai api setChats( prevChats => { const updatedChats = [...prevChats, Object (result)]; return updatedChats; }); //set isTyping state to false. 'false' removes the element displaying 'AI typing' setIsTyping( false ) } catch (error) { //catch any possible error from the request console .error( "Error fetching completion:" , error); //set isTyping state to false. 'false' removes the element displaying 'AI typing' setIsTyping( false ) } }
خطوط 73 تا 78 حاوی کد موجود در قلاب useEffect
است. هر زمان که وضعیت chats
به روز شود، سند به پایین پیمایش می شود تا جدیدترین پیام ها نمایش داده شوند.
useEffect( () => { if (chatContainerRef.current) { //whenever the chats state is updated, scroll to the bottom of the container element to display the recent messages chatContainerRef.current.scrollTo({ top : chatContainerRef.current.scrollHeight, behavior : 'smooth' }); } }, [chats]);
کد از خطوط 81 تا 120 حاوی نشانه گذاری است که در مرورگر ارائه می شود.
از خطوط 91 تا 99، محتوای حالت chats
با استفاده از تابع map
به نشانه گذاری اضافه می شود. پیامهای AI در سمت چپ و پیامهای کاربر به سمت راست تراز میشوند
{ //the content of chats state is looped to display the content. if the content is from the user, it will be aligned to the right. if it's from the AI, it'll be aligned to the left chats.map( ( data, index ) => < div key = {index} className = { `${ data.role == 'user' ? ' text-right ' : ' text-left '} my- [ 30px ]`}> < p className = "text-[15px] bg-[#4d4d4dff] max-w-[60%] p-[10px] lg:p-[20px] rounded-xl text-left text-[#f2f2f2ff] inline-block" > {data.content} </ p > </ div > )) }
از خطوط 101 تا 107، یک بلوک کد وجود دارد که همیشه در هر زمانی که حالت isTyping
(که هر زمانی که کاربر پیامی ارسال میکند و در انتظار پاسخ است فعال میشود) true
ارائه میشود.
{ /*if the isTyping state is true, display the element. if not, hide it.*/ } <div className={isTyping? 'block' : 'hidden' }> < div className = 'text-left my-[30px]' > < p className = "text-[15px] bg-[#4d4d4dff] max-w-[60%] p-[10px] lg:p-[20px] rounded-xl text-center text-[#f2f2f2ff] inline-block" > AI Typing... </ p > </ div > </div>
خطوط 113 تا 116 حاوی کدی است که فرم را نشان می دهد. هر زمان که عنصر ورودی تغییر کند، وضعیت input
به روز می شود. و هر زمان که فرم ارسال می شود (زمانی که دکمه کلیک می شود یا ورودی وارد می شود)، تابع handleChat
فراخوانی می شود و مقدار حالت input
و رویداد فرم را به عنوان آرگومان ارسال می کند.
{ /*when the form is submitted, activate a submit event that sends the value of the input and the event to the handleChat function */ } <form action= '' onSubmit={ ( e ) => handleChat(input, e)}> < input className = " lg:w-[50%] w-[70%] ml-[5%] lg:ml-[20%] p-[10px] outline-none bg-[#4d4d4dff] text-[15px] text-[#f2f2f2ff]" type = 'text' value = {input} placeholder = 'Ask your questions' onChange = { ( e ) => setInput(e.target.value)}/> < button className = 'py-[10px] px-[20px] bg-black text-[15px] text-[#f2f2f2ff]' > Ask </ button > </form>
فایل hooks/useOpenAI.ts
در فایل hooks/useOpenAi.ts
، کتابخانه OpenAI را وارد کرده و کلید API را در یک متغیر ذخیره میکنید.
import OpenAI from 'openai' ; const API_KEY = 'YOUR-API-KEY' ;
یک تابع useOpenAI
که نشان دهنده قلاب است ایجاد شد.
در داخل تابع، یک نمونه از شی OpenAI ایجاد شد. همچنین، یک تابع async getCompletion
وجود دارد که درخواست را از آرگومان دریافت میکند و درخواست را به OpenAI API ارسال میکند. در صورت وجود خطایی در مسیر، پیغام خطا برگردانده می شود.
function useOpenAI ( ) { const client = new OpenAI({ apiKey : API_KEY, dangerouslyAllowBrowser : true }); const getCompletion = async (prompt: string) => { try { let completion = await client.chat.completions.create({ messages : [ { "role" : "system" , "content" : "Your job is to write about any topic asked by the user" }, { "role" : "user" , "content" : prompt } ], model : "gpt-3.5-turbo" , }); return completion.choices[ 0 ].message; } catch (e){ return { "role" : "assistant" , "content" : "Something went wrong" } } }; return getCompletion; }
به یاد داشته باشید که هر زمان که از سایت پیامی ارسال می کنید، یک پیام خطا دریافت می کنید.
دلیلش این است که کلیدهای OpenAI API را به پروژه اضافه نکرده اید. بعداً به آن قسمت خواهیم پرداخت.
فایل package.json
فایل Package.json
حاوی اطلاعاتی در مورد کتابخانه ها و اسکریپت های مورد نیاز برای اجرای پروژه شما است
فایل باید به شکل زیر باشد:
{ "name" : "nextjs-ai" , "version" : "0.1.0" , "private" : true , "scripts" : { "dev" : "next dev" , "build" : "next build" , "start" : "next start" , "lint" : "next lint" }, "dependencies" : { "@upstash/ratelimit" : "^2.0.1" , "@vercel/kv" : "^2.0.0" , "next" : "14.2.5" , "next-auth" : "^4.24.7" , "openai" : "^4.52.7" , "react" : "^18" , "react-dom" : "^18" }, "devDependencies" : { "@types/node" : "^20" , "@types/react" : "18" , "@types/react-dom" : "^18" , "eslint" : "^8" , "eslint-config-next" : "14.2.5" , "postcss" : "^8" , "tailwindcss" : "^3.4.1" , "typescript" : "^5" } }
از کد بالا، اسکریپت 'dev' برای اجرای پروژه در حالت توسعه استفاده خواهد شد. به همین دلیل است که از npm run dev
در CLI استفاده می کنید.
از 2 اسکریپت بعدی، build
و start
، برای بهینه سازی فایل ها و شروع پروژه در یک محیط تولید استفاده می شود. لازم نیست نگران این موارد باشید زیرا Vercel همه چیز را مرتب می کند.
next
، react
و react-dom
کتابخانه های اصلی پروژه هستند.
کتابخانه openai
برای برقراری ارتباط با OpenAI API استفاده خواهد شد.
@upstash/ratelimit
، @vercel/kv
و next-auth
برای پیادهسازی برخی از آپشن های امنیتی در بخشهای بعدی استفاده خواهند شد.
وابستگی های توسعه تنها در حین توسعه پروژه مورد استفاده قرار خواهند گرفت. آنها عبارتند از typescript
، tailwindcss
و غیره.
چگونه کلیدهای OpenAI API خود را دریافت کنید
برای اینکه پروژه طبق برنامه کار کند، باید کلید مخفی OpenAI را به پروژه اضافه کنید. این کلید به برنامه اجازه می دهد تا با موفقیت آمیز API OpenAI ادغام شود و به کاربران امکان می دهد درخواست های فوری از سمت مشتری ارسال کنند و پاسخ های هوش مصنوعی را دریافت کنند.
برای دریافت کلیدهای مخفی OpenAI خود، ابتدا باید در سایت platforms.openai.com ثبت نام کنید (یا وارد شوید). در مرحله بعد، اگر این کار را نکرده اید، باید جزئیات کارت پرداخت خود را تنظیم کنید. بدون آنها، API کار نخواهد کرد.
پس از آن، به بخش کلیدهای API بروید تا کلید API خود را ایجاد کنید. حتماً فوراً آن را کپی کنید.
در مرحله بعد، باید کلید API را به عنوان یک متغیر محیطی با ایجاد یک فایل .env.local
در ریشه پروژه خود و چسباندن مقدار در آنجا ذخیره کنید.
NEXT_PUBLIC_API_KEY= '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'
نام ویژگی با NEXT_PUBLIC
شروع می شود تا بتوان از آن در سمت مشتری استفاده کرد.
بعد، در خط 4 از فایل useOpenAI.ts
، کد را به این تغییر دهید:
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
پس از انجام این کار، می توانید فایل ها را ذخیره کنید، منتظر بمانید تا سرور Next.js دوباره کامپایل شود و سایت را آزمایش کنید.
اگر سرور را خاموش کرده اید، به دایرکتوری پروژه خود در CLI بروید و npm run dev
را وارد کنید. پس از کامپایل شدن سرور، مرورگر خود را باز کنید و http://localhost:3000/
را در نوار جستجو تایپ کنید. همچنین، مطمئن شوید که وای فای خود را متصل کرده اید تا درخواست شما به OpenAI API ارسال شود.
پس از بارگیری سایت، سعی کنید با آن تعامل داشته باشید، چند سوال بپرسید و منتظر پاسخ خود از سرورهای OpenAI باشید.
اگر همه چیز را به درستی دنبال کرده باشید، تعامل شما باید به این صورت باشد:
یک ظاهر طراحی شده ممکن است خوب به نظر نرسد اما کار می کند. بعدا می توانید روی استایل کار کنید.
نحوه استقرار پروژه در Vercel
اکنون زمان استقرار پروژه در Vercel است. ما در این آموزش از Vercel برای استقرار استفاده خواهیم کرد، زیرا Vercel Next.js را ایجاد کرد - پس استقرار استرسزا نخواهد بود.
اما قبل از آن، باید تغییرات خود را به GitHub فشار دهید تا بتوانید به راحتی پروژه را در Vercel پیاده سازی کنید.
برای این کار باید دستورات زیر را تایپ کنید:
git init git add . git commit –m "first commit" git branch -m master main
پس از انجام این کار، باید به حساب GitHub خود بروید تا یک مخزن جدید ایجاد کنید. مطمئن شوید که هیچ فایلی از جمله README یا مجوز اضافه نمی کنید تا هنگام استقرار با خطاهای Git مواجه نشوید.
پس از ایجاد مخزن، لینک مخزن را کپی کرده و در دستور زیر قرار دهید:
git remote add origin <link- of -your-repo>
حالا کد را به شاخه اصلی فشار دهید:
git push -u origin main
پس از انجام این کار، مخزن موجود در حساب GitHub خود را تحلیل کنید تا مطمئن شوید که در شعبه اصلی تعهد شده است.
پس از انجام این کار، باید با حساب GitHub خود در Vercel ثبت نام کنید.
در مرحله بعد، مخزن nextjs-ai را از GitHub وارد کرده و آن را مستقر کنید.
قبل از استقرار آن، محتوای فایل .env.local
خود را در قسمت متغیرهای محیطی قرار دهید. پس از آن روی Deploy کلیک کنید و منتظر بمانید تا پروژه اجرا شود.
پس از چند ثانیه، برنامه شما مستقر می شود و URL عمومی پروژه به شما نشان داده می شود.
هنگامی که از پیوند مستقر شده بازدید می کنید، سایت باید اینگونه رفتار کند:
اگرچه این برنامه کاری را که باید انجام می دهد، پر از آسیب پذیری های امنیتی است که ممکن است به دلیل حملات سایبری هزینه زیادی را برای شما به همراه داشته باشد. پس قبل از دریافت نام دامنه مکث می کنیم تا بتوانیم این مشکلات را برطرف کنیم.
آسیب پذیری اول: قرار گرفتن در معرض داده های حساس در Frontend
افشای داده های حساس مانند کلیدهای API در فرانت اند خطرناک است زیرا داده ها می توانند به سرقت رفته و به طور مخرب توسط مهاجم استفاده شوند.
اگرچه کلید API را به عنوان یک متغیر محیطی ذخیره کرده اید، اما همچنان می توان آن را در مرورگر مشاهده کرد. این به این دلیل است که شما از متغیر محیطی در قلاب React استفاده کرده اید که در سمت کلاینت اجرا می شود.
این بدان معنی است که هر کسی که مهارت کافی داشته باشد می تواند کلیدهای API شما را در مرورگر تحلیل کند.
اگرچه OpenAI API به شدت استفاده از API خود را در سمت سرور اعمال میکند، اما یک پایه dangerouslyAllowBrowser
برای یادآوری خطرات استفاده از آن در سمت کلاینت به کاربران ارائه میکند، اکثر APIها این نوع اعمال را ندارند.
اگر من یک مهاجم بودم، چگونه میتوانم کلیدهای OpenAI API شما را دریافت کنم:
ابتدا ابزارهای توسعه دهنده مرورگر را باز می کنم و روی زبانه شبکه کلیک می کنم. سپس، یک اعلان را در ورودی وارد می کنم و روی enter کلیک می کنم. با ارسال درخواست، تب شبکه درخواستهای خروجی را ضبط میکند و تمام اطلاعات را نمایش میدهد.
سپس وقتی روی تب هدرها کلیک کردم و به سرفصل های درخواست رفتم، می توانم هدر مجوز و کلید API را ببینم.
ویدئوی زیر یک نمایش را نشان می دهد:
نحوه رفع آسیب پذیری 1:
پس چگونه این را برطرف می کنید؟ نگران نباشید - ساده است.
برای جلوگیری از افشای داده ها در سمت مشتری، باید کد حساس را به باطن منتقل کنید و از سمت سرور به متغیرهای محیط خود دسترسی داشته باشید. به این ترتیب، هر درخواستی از مرورگر انتزاع می شود و سرور به عنوان پروکسی برای برقراری ارتباط با OpenAI API است.
در Next.js می توانید این کار را با استفاده از کنترلرهای مسیر انجام دهید. ما از کنترلکنندههای مسیر برای دریافت درخواستهای ورودی به مسیر api/ai
، ارسال درخواست به OpenAI API و برگرداندن پاسخها به سمت کلاینت استفاده میکنیم.
Route handlers توابع سمت سرور هستند، پس کد آنها در مرورگر قابل مشاهده نخواهد بود و متغیر محیط شما ایمن می شود.
اکنون که می دانید چه کاری باید انجام دهید، بیایید کد را به روز کنیم.
ابتدا پیشوند NEXT_PUBLIC
را از NEXT_PUBLIC_API_KEY
حذف کنید تا به API_KEY
تبدیل شود. این برای اطمینان از اینکه کلید در سمت مشتری در دسترس نخواهد بود.
API_KEY= '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'
بعد، یک پوشه api
در app
ایجاد کنید. این پوشه همه گرداننده های مسیر را برای پروژه شما ذخیره می کند.
سپس یک پوشه ai
و یک فایل route.ts
در آن ایجاد کنید. پوشه ai
باید در app/api
باشد.
فایل api/ai/route.ts
به درخواست های مسیر api/ai
رسیدگی می کند.
سپس این کد را به فایل api/ai/route.ts
خود اضافه کنید:
import {NextRequest, NextResponse} from 'next/server' ; import OpenAI from 'openai' ; const API_KEY = process.env.API_KEY; const client = new OpenAI({ apiKey : API_KEY }); export async function POST ( req: NextRequest ) { let {prompt} = await req.json(); if (!prompt) { return NextResponse.json({ content : 'Prompt is required' }, { status : 400 }); } try { let completion = await client.chat.completions.create({ messages : [ { role : 'system' , content : 'Your job is to write about any topic asked by the user' }, { role : 'user' , content : prompt } ], model : 'gpt-3.5-turbo' , }); return NextResponse.json(completion.choices[ 0 ].message, { status : 200 }); } catch (error) { console .error(error) return NextResponse.json({ content : 'Internal Server Error' }, { status : 500 }); } };
از کد بالا، توابع NextRequest
و NextResponse
را وارد کردید که نشان دهنده پسوندهای Web Request API و Web Response API هستند. شما همچنین تابع OpenAI
را از کتابخانه openai
وارد کردید.
سپس، یک متغیر ( API_KEY
) ایجاد کردید که متغیر محیطی API_KEY را ذخیره میکند. شما همچنین متغیر دیگری ایجاد کردید که نمونه جدیدی از شی OpenAI را ذخیره می کند.
در نهایت، یک تابع POST برای رسیدگی به درخواست های POST به مسیر api/ai
ایجاد کردید. تابع دستور را دریافت می کند و آن را به OpenAI API ارسال می کند، منتظر پاسخ می ماند و آن را به کاربر برمی گرداند. اگر درخواست دارای ویژگی prompt در بدنه نباشد یا خطایی در مسیر وجود داشته باشد، یک پیام خطا به کاربر برگردانده می شود.
سپس به فایل hooks/useOpenAI.ts
خود بروید و آن را با این موارد جایگزین کنید:
const useOpenAI = () => { const getCompletion = async (prompt: string) => { try { const response = await fetch( '/api/ai' , { method : 'POST' , body : JSON .stringify({ prompt }), headers : { 'Content-Type' : 'application/json' , } }); const result = await response.json() if (!response.ok) { return { role : 'assistant' , content : result.content}; } return result; } catch (error) { return { role : 'assistant' , content : 'Something went wrong' }; } }; return getCompletion; }; export default useOpenAI;
از کد بالا، قلاب useOpenAI
را تغییر دادید تا در صورت فراخوانی getCompletion
، یک درخواست واکشی به مسیر api/ai
ارسال کند و پاسخ را به کاربر برگرداند. در صورت عدم موفقیت درخواست، پیغام خطا به کاربر برگردانده می شود.
اگر این کار را انجام داده اید، وقت آن رسیده است که نقطه پایانی خود را آزمایش کنید.
در سیستم خود، به پروژه CLI بروید و دستور زیر را اجرا کنید:
npm run dev
اگر سایت را تست کردید و همه چیز خوب پیش رفت، به این معنی است که مشکلی ندارد.
حالا بیایید تحلیل کنیم که آیا میتوانید کلیدهای API را در ابزارهای توسعهدهنده دریافت کنید:
از ویدیوی بالا می بینید که هیچ کلید API در سرصفحه های درخواست نمایش داده نشده است.
اکنون که این مشکل حل شد، باید تغییرات را در مخزن GitHub خود اعمال کنید.
هر تغییری که در شاخه اصلی مخزن GitHub شما ایجاد شود به طور خودکار در Vercel مستقر می شود.
این دستورات را برای به روز رسانی مخزن GitHub خود اجرا کنید:
git add . git commit –m "moved sensitive code to backend" git push –u origin main
پس از بهروزرسانی تغییرات، به صفحه استقرار پروژه خود در Vercel بروید تا تأیید کنید که استقرار با موفقیت انجام شده است.
متأسفانه، پیش بینی می شود که استقرار با شکست مواجه شود زیرا شما متغیرهای محیطی را که به Vercel اضافه کرده اید (از NEXT_PUBLIC_API_KEY
به API_KEY
) به روز نکرده اید.
پس باید به setting > محیط متغیر بروید و آن را وارد کنید. فایل env.local
پروژه شما.
هنگامی که این کار را انجام دادید، به صفحه استقرار بروید و آخرین تغییر را دوباره اجرا کنید.
پس از استقرار موفقیتآمیز، از سایت دیدن کنید و برگه شبکه را تحلیل کنید تا تأیید کنید که کلیدهای API هنگام ارسال پیام از اعلان نمایش داده نمیشوند.
اگر هیچ کلید API در هدر درخواست قابل مشاهده نیست، به این معنی است که کد حساس شما با موفقیت به پشتیبان منتقل شده و آخرین تغییرات شما اجرا شده است.
تبریک می گویم! حالا بریم سراغ قسمت بعدی...
اما ابتدا ویدیوی مربوط به این بخش در اینجا آمده است:
آسیب پذیری دوم: حملات DOS و DDOS
اگرچه کلید API ما در پشتیبان ایمن است، برنامه همچنان در برابر حملات انکار سرویس (DOS) و حملات انکار سرویس توزیع شده (DDOS) آسیب پذیر است.
حمله DOS زمانی است که سایت شما مملو از درخواست های بیش از حد از یک دستگاه باشد که سرور شما را تحت الشعاع قرار می دهد و مانع از لذت بردن کاربران واقعی از خدمات برنامه شما می شود.
یک حمله پیشرفته تر، حمله انکار سرویس توزیع شده (DDOS) است که شامل ارسال تعداد زیادی درخواست از چندین دستگاه به طور همزمان به سایت شما است.
مناطق مختلف یک سایت می توانند در برابر حملات DOS یا DDOS آسیب پذیر باشند. این حمله می تواند زیرساخت DNS، پایگاه داده، نقاط پایانی API و حتی فایل های استاتیک شما را هدف قرار دهد.
بدون استراتژیهای کاهش موثر، حملات DoS یا DDoS میتواند منجر به خسارات مالی قابل توجهی به دلیل افزایش سرسامآور هزینههای سرویس ابری و هزینههای مربوط به بازیابی و ایمنسازی سایت شما شود.
برای درک نحوه عملکرد این حمله، بیایید سعی کنیم یک حمله ساده DOS را در برنامه هوش مصنوعی خود شبیه سازی کنیم.
اگر یک مهاجم بودید، میتوانید اسکریپت زیر را در کنسول مرورگر خود اجرا کنید تا 50 درخواست به مسیر api/ai
ارسال کنید.
//function to send the request const getCompletion = async (prompt) => { try { const response = await fetch( '/api/ai' , { method : 'POST' , body : JSON .stringify({ prompt }), headers : { 'Content-Type' : 'application/json' , }, }); const result = await response.json(); if (!response.ok) { return { role : 'assistant' , content : result.content}; } return result; } catch (error) { return { role : 'assistant' , content : 'Something went wrong' }; } }; //function for sending the request 50 times const attackServer = async () => { const prompt = [ 'Write about a lion' , 'write about a tiger' , 'write about america' , 'write about ice cream' , 'write about pizza' ]; const numRequests = 50 ; const results = []; for ( let i = 0 ; i < numRequests; i++) { const startTime = performance.now(); const result = await getCompletion( prompt[ Math .floor( Math .random()* 4 )] ); const endTime = performance.now(); const responseTime = endTime - startTime; results.push({ index : i, result, responseTime, }); console .log( `Request ${i + 1 } : Response time = ${responseTime} ms` ); } return results; }; // command to activate the attack and display the result attackServer().then( ( results ) => { console .log( 'All requests completed' ); console .table(results); });
از کد بالا، دو تابع async ( getCompletion
و attackServer
) را در کنسول جایگذاری کردید.
getCompletion
حاوی درخواست واکشی است که به مسیر api/ai
ارسال میشود. attackServer
حاوی کدی است که برای فراخوانی تابع getCompletion
50 بار استفاده می شود.
پس از آن، آخرین فرمانهایی را که تابع attackServer
را اجرا میکنند، قرار دادهاید و نتیجه حاوی اطلاعات مربوط به تمام درخواستهای ارسالشده، از جمله دادههای دریافتشده از سرور و زمان پاسخدهی را نمایش میدهد.
مال من اینطور گذشت:
اگرچه این حمله ساده شامل ارسال 50 درخواست به مسیر API شخص ثالث بود، اما در واقع برای استفاده از OpenAI API من تقریباً 0.02 دلار هزینه داشت. اگر حمله شامل 50000 درخواست بود، 20 دلار برای من هزینه داشت. اگر این حمله شامل 50،000،000 درخواست بود، نزدیک به 20،000 دلار برای من هزینه داشت.
استراتژی های مختلفی را می توان در Next.js و Vercel برای محافظت از سایت شما در برابر این حملات پیاده سازی کرد. آنها شامل محدودیت نرخ، فایروال ها، حفاظت از Vercel/Cloudflare DDOS، حالت چالش حمله، مدیریت هزینه و نظارت بر وب سایت هستند.
محدود کردن نرخ
محدود کردن نرخ روشی است که برای مسدود کردن درخواستهای تکراری از دستگاههایی که از تعداد آنها در یک بازه زمانی بیشتر است استفاده میشود. اگر درخواست ها از یک آستانه فراتر رود، کاربر می تواند به طور موقت دسترسی به یک سرویس را محدود کند.
الگوریتمهای محدودکننده نرخ متفاوتی وجود دارد، اما ما از یک الگوریتم ساده استفاده میکنیم که دسترسی به نقطه پایانی api/ai را زمانی که کاربر درخواستهای زیادی ارسال میکند، محدود میکند.
ما از پایگاه داده Vercel KV و کتابخانه @upstash/ratelimit
برای پیاده سازی این الگوریتم محدود کننده نرخ استفاده خواهیم کرد.
@upstash/ratelimit
یک کتابخانه قدرتمند محدود کننده نرخ است که برای استفاده در یک محیط بدون سرور مانند توابع Next.js طراحی شده است. Vercel KV یک سرویس پایگاه داده Redis است که ما از آن برای پیگیری درخواست های کاربر استفاده خواهیم کرد.
ابتدا باید Vercel KV را راه اندازی کنیم. برای انجام این کار، با کلیک بر روی Storage \> Create Vercel KV Database یک پایگاه داده KV ایجاد کنید. پس از نمایش کادر محاوره ای، اطلاعات لازم را پر کنید. به فیلد نام مقداری بدهید، یک منطقه را انتخاب کنید، محیط خود را (اعم از توسعه، پیش نمایش یا تولید) انتخاب کنید و در نهایت روی اتصال کلیک کنید. سپس به پروژه متصل شوید.
سپس، متغیرهای محیط خود را در تب تنظیمات تحلیل کنید تا تأیید کنید که کلیدها و نشانههای KV شما اضافه شدهاند.
سپس یک فایل middleware.ts
در پوشه src
ایجاد کنید و این کد را به آن اضافه کنید:
import { NextRequest, NextResponse } from 'next/server' ; import { Ratelimit } from '@upstash/ratelimit' ; import { kv } from '@vercel/kv' ; const ratelimit = new Ratelimit({ redis : kv, // 1 requests from the same IP for every 30 seconds limiter : Ratelimit.slidingWindow( 1 , '30 s' ), }); export const config = { matcher : '/api/ai' } export default async function middleware ( request: NextRequest ) { const ip = request.ip || '127.0.0.1' ; const { success, pending, limit, reset, remaining } = await ratelimit.limit( ip ); console .log(success) return success ? NextResponse.next() : NextResponse.json({ role : 'assistant' , content : 'too many requests' }, { status : 429 }); }
از کد بالا، NextRequest
و NextResponse
از next/server
، Ratelimit
از @upstash/ratelimit
و kv
از @Vercel/kv
وارد کردید. در مرحله بعد، تابع Ratelimit
برای استفاده از پایگاه داده KV تنظیم می کنید و به ازای هر 30 ثانیه تنها 1 درخواست را مجاز می دانید.
سپس یک متغیر پیکربندی ایجاد کردید تا مطمئن شوید که فقط درخواستهای مسیر api/ai
با نرخ محدود هستند. در نهایت، یک تابع میانافزار ایجاد کردید که درخواستهای api/ai را تحلیل میکند. اگر آدرس آیپی درخواست از آستانه حد مجاز تجاوز نکرده باشد، به api/ai
ارسال میشود. اگر از آستانه فراتر رفته باشد، یک پیام خطا به کاربر برگردانده می شود.
برای تأیید اینکه الگوریتم محدودکننده نرخ با موفقیت اجرا شده است، باید مخزن GitHub را به روز کنید و آخرین استقرار Vercel را آزمایش کنید.
شما نمی توانید الگوریتم را در سرور محلی آزمایش کنید زیرا request.ip
فقط در Vercel موجود است.
طبق معمول، دستورات زیر را دنبال کنید تا تغییرات محلی را در GitHub اعمال کنید:
git add . git commit –m "rate limiting algorithm done" git push –u origin main
پس از موفقیت آمیز بودن به روز رسانی، از صفحه استقرار پروژه خود در Vercel دیدن کنید تا تأیید کنید که تغییرات GitHub با موفقیت اجرا شده است.
اکنون به پیوند سایت مستقر شده مراجعه کرده و اسکریپت زیر را جایگذاری کنید تا تأیید کنید که الگوریتم محدود کننده نرخ شما با موفقیت پیاده سازی شده است.
//function to send the request const getCompletion = async (prompt) => { try { const response = await fetch( '/api/ai' , { method : 'POST' , body : JSON .stringify({ prompt }), headers : { 'Content-Type' : 'application/json' , }, }); if (!response.ok) { return { role : 'assistant' , content : 'Internal server error' }; } const result = await response.json(); return result; } catch (error) { return { role : 'assistant' , content : 'Something went wrong' }; } }; //function for sending the request 50 times const attackServer = async () => { const prompt = [ 'Write about a lion' , 'How are you' , 'write about america' , 'write about ice cream' , 'what is your name' ]; const numRequests = 50 ; const results = []; for ( let i = 0 ; i < numRequests; i++) { const startTime = performance.now(); const result = await getCompletion( prompt[ Math .floor( Math .random()* 4 )] ); const endTime = performance.now(); const responseTime = endTime - startTime; results.push({ index : i, result, responseTime, }); console .log( `Request ${i + 1 } : Response time = ${responseTime} ms` ); } return results; }; // command to activate the attack and display the result attackServer().then( ( results ) => { console .log( 'All requests completed' ); console .table(results); });
از ویدیوی بالا می بینید که بیشتر پاسخ ها پیغام خطا بوده است. این بدان معناست که الگوریتم محدود کننده نرخ کار می کند. اگر هر کاربری سعی کند هر 30 ثانیه بیش از 1 درخواست ارسال کند، یک پیام خطا دریافت می کند.
حفاظت DDOS Vercel و Cloudflare
علاوه بر محدود کردن نرخ، می توانید از کاهش خودکار DDOS Vercel استفاده کنید. طبق وب سایت Vercel، هیچ هزینه ای برای محافظت DDOS وجود ندارد. پس تنها کاری که می توانید انجام دهید این است که به خدمات آنها اعتماد کنید.
همچنین، اگر نام دامنه دارید (یا میخواهید بخرید)، میتوانید از Cloudflare استفاده کنید و از حفاظت و امنیت DDOS رایگان و نامحدود برای برنامه خود بهره مند شوید. برای اطلاعات بیشتر می توانید سایت CloudFlare را تحلیل کنید.
ویژگی های امنیتی Vercel
علاوه بر اقدامات فوق ، شما همچنین می توانید از ترکیبی از مدیریت هزینه ، حالت چالش حمله ، قوانین Vercel WAF و نظارت وب سایت برای تقویت امنیت برنامه خود استفاده کنید.
اول ، شما مدیریت هزینه را تنظیم کرده اید تا به شما اطلاع دهد که صورتحساب شما به آستانه خاصی برسد و هنگام رسیدن به مبلغ ، پروژه های خود را مکث کنید. هنگامی که می دانید چقدر هزینه خواهید کرد ، می توانید مبلغ را در تنظیمات مدیریت هزینه خود تعیین کنید.
یکی دیگر از ویژگی های امنیتی Vercel حالت Attack Challenge است. می توانید از حالت Attack Challenge استفاده کنید تا کاربران خود قبل از ادامه استفاده از سایت شما ، برخی از چک های تأیید را تصویب کنند. این کارها باید به طور موقت انجام شود که در مورد صورتحساب خود اعلان دریافت کنید یا متوجه ترافیک غیرمعمول در سایت خود شوید.
قوانین WAF سفارشی نیز وجود دارد. قوانین مختلفی در تنظیمات وجود دارد که می توانید از آنها استفاده کنید. می توانید قوانینی را تنظیم کنید که دسترسی به نقاط پایانی خاص و روش های درخواست را محدود کند. همچنین می توانید کاربران را با یک عنوان درخواست خاص ، آدرس IP ، پروتکل ، قاره و کشور محدود کنید.
همیشه نظارت بر ترافیک سایت و استفاده از منابع شما همیشه مهم است. اگر متوجه هر گونه سنبله ای که در یک مسیر خاص بدون جریان استفاده مناسب قرار دارد ، می توانید حالت Attack Challenge و قوانین WAF سفارشی را تنظیم کنید تا احتمال حمله کاهش یابد.
به طور خلاصه ، برای محافظت از برنامه AI خود در برابر حمله DOS/DDOS ، می توانید محدودیت نرخ ، مدیریت هزینه ، فایروال های سفارشی ، حالت چالش حمله و نظارت بر وب سایت را تنظیم کنید.
من فکر می کنم به راحتی می توان بيان کرد این آسیب پذیری برطرف شده است.
آسیب پذیری 3: بدون تأیید اعتبار و مجوز
اگرچه شما اقداماتی را انجام داده اید که از سایت شما در برابر اشکال مختلف حمله محافظت می کند ، آسیب پذیری دیگری می تواند کارهایی را که تاکنون انجام داده اید تضعیف کند.
میدونی چیه؟
این عدم وجود مکانیسم های احراز هویت و مجوز است.
برای برنامه ای که به یک API خارجی متصل می شود ، عدم وجود مکانیسم های احراز هویت و مجوز می تواند وب سایت شما را در برابر حملات مانند DDOS و CSRF بسیار آسیب پذیر کند. این امر به این دلیل است که هرکسی می تواند به وب سایت مراجعه کرده و از آن استفاده کند بدون اینکه چک های امنیتی را انجام دهد.
محدود کردن نرخ شما و امنیت DDOS اگر درخواست ها قانونی به نظر برسند و از هزاران (یا میلیون ها) کاربر استفاده می شود ، نمی تواند کارهای زیادی انجام دهد. و اگر به همه درخواست ها پاسخ داده شود ، می تواند هزینه زیادی برای شما داشته باشد.
درست همانطور که من قادر به ارسال نامه به نقطه پایانی هستم ، میلیون ها کاربر بالقوه نیز می توانند این کار را انجام دهند.
و به همین دلیل است که شما باید احراز هویت و مجوز را پیاده سازی کنید.
احراز هویت زمانی است که کاربران قبل از اینکه بتوانند به یک سایت دسترسی پیدا کنند تأیید می شوند. مجوز را تحلیل می کند که آیا کاربر مجاز به دسترسی یا استفاده از یک ویژگی است.
برای این کار از کتابخانه next-auth
استفاده خواهید کرد. Next-Auth کتابخانه ای است که به شما امکان می دهد اشکال مختلف تأیید اعتبار را در سایت بعدی خود اجرا کنید.
شما از این کتابخانه برای تنظیم احراز هویت GitHub OAuth استفاده خواهید کرد. به این ترتیب فقط به کسانی که از طریق GitHub احراز هویت می شوند مجاز به ارسال درخواست به مسیر api/ai
شما هستند.
ابتدا باید شناسه مشتری و مشتری خود را از داشبورد GitHub خود مخفی کنید. بدون این ، سایت شما قادر به استفاده از احراز هویت GitHub نخواهد بود.
برای به دست آوردن این موضوع ، شما باید یک برنامه GitHub OAuth ( تنظیمات > تنظیمات توسعه دهنده ) ایجاد کنید ، و سپس شناسه مشتری و راز را برای پرونده .env.local
خود تهیه و کپی کنید.
برای یادگیری نحوه انجام این کار می توانید ویدیوی زیر را تحلیل کنید:
در پرونده .env.local
خود ، کلیدها باید GITHUB_CLIENT_SECRET
و GITHUB_SECRET_ID.
GITHUB_CLIENT_ID=Ov23liRYdIehpA61t3Js GITHUB_SECRET_ID= 547 vfbsjgfsk4859030
در مرحله بعد ، شما همچنین به ایجاد یک کلید مخفی نیاز دارید که توسط next-auth
برای رمزگذاری نشانه های JWT خود استفاده شود. مقدار نباید به راحتی قابل حدس باشد. می توانید از دستور openssl
(در صورت نصب آن در رایانه شخصی خود) در خط فرمان خود استفاده کنید تا بتوانید یک مقدار پیچیده دریافت کنید که قابل حدس نیست.
openssl rand –base64 32
پس از اتمام این کار ، باید مقدار را کپی کرده و آن را در پرونده .env.local
خود جایگذاری کنید
اگر openssl
ندارید ، می توانید یک مقدار تصادفی پیچیده ایجاد کرده و به جای آن از آن استفاده کنید.
NEXTAUTH_SECRET=OtPuemlSrP8At2uZFIMrc47WBT14pifeKhziIW8
در مرحله بعد ، شما باید مسیر پایه را برای تأیید اعتبار مشخص کنید. این به طور کلی URL صفحه اصلی سایت است. این بدان معناست که احراز هویت و مجوز تمام مسیرهای شما را در بر می گیرد. از آنجا که شما می توانید سایت را در محیط محلی آزمایش کنید ، http://localhost:3000/
استفاده خواهید کرد.
این بدان معنی است که شما باید در پرونده .env.local
خود کلید دیگری ( NEXTAUTH_URL
) داشته باشید.
NEXTAUTH_URL=http: //localhost:3000/
پس از اتمام این کار ، باید یک عملکرد یاور ایجاد کنید که پیکربندی ها را برای next-auth
ذخیره کند.
در پوشه برنامه ، یک پوشه helper
ایجاد کرده و یک پرونده authOption.ts
را به آن اضافه کنید.
در مرحله بعد ، این کد را به پرونده app/helper/authOptions.ts
اضافه کنید:
import { NextAuthOptions } from "next-auth" ; import GithubProvider from "next-auth/providers/github" ; export const authOptions: NextAuthOptions = { // Configure one or more authentication providers providers : [ GithubProvider({ clientId : process.env.GITHUB_CLIENT_ID as string, clientSecret : process.env.GITHUB_SECRET as string, }) ], secret : process.env.NEXTAUTH_SECRET as string, session : { strategy : 'jwt' , maxAge : 60 * 2 //expires 2 minutes after the last request }, };
از کد بالا پیکربندی را ایجاد کرده اید که توسط next-auth
استفاده می شود. پیکربندی مشخص می کند که برنامه از ارائه دهنده GitHub برای تأیید اعتبار استفاده می کند. راز نیز به پیکربندی اضافه شد. و در آخر ، شما از یک توکن JWT پیکربندی شده برای انقضا 2 دقیقه پس از آخرین درخواست استفاده خواهید کرد.
در مرحله بعد ، یک پرونده api/auth/[…nextauth]/route.ts
در پوشه برنامه خود ایجاد کنید.
ساختار پوشه باید به این شکل باشد:
که پس از ایجاد پوشه ها و پرونده های بیشتر ، ساختار پوشه به روز شده را نشان می دهد" class="image--center mx-auto" width="482" height="534" loading="lazy">
بعد ، کد زیر را به پرونده اضافه کنید:
import NextAuth from "next-auth" ; import {authOptions} from "@/app/helper/authOption" ; const handler = NextAuth(authOptions); export { handler as GET, handler as POST };
از کد فوق ، شما عملکرد NextAuth
و authOptions
(از app/helper/authOption
) را وارد کردید. در مرحله بعد ، شما از این authOption
برای اولیه سازی next-auth
استفاده کردید. بالاخره ، شما کنترل کننده را صادر کردید تا بتوانید از آن در سمت سرور استفاده کنید.
در مرحله بعد ، یک مؤلفه مشتری ایجاد کنید که جلسه را در سمت مشتری در دسترس قرار دهد. از این مؤلفه برای بسته بندی محتوای پرونده Root Layout ( app/layout.tsx
) استفاده می شود.
در پوشه یاور خود ، یک فایل provider.tsx
ایجاد کرده و این کد را به آن اضافه کنید:
"use client" ; import {SessionProvider} from "next-auth/react" ; export function Provider ( {children}: {children: React.ReactNode} ) { return < SessionProvider > {children} </ SessionProvider > ; }
در مرحله بعد ، در طرح ریشه خود ( app/layout.tsx
) ، ارائه دهنده جلسه را وارد کنید و آن را در اطراف کودکان از مؤلفه RootLayout
بپیچانید:
//layout.tsx import {Provider} from "@/app/helper/provider" ; //…other code export default function RootLayout ( { children, }: Readonly<{ children: React.ReactNode; }> ) { return ( < html lang = "en" > < body className = {inter.className} > < Provider > {children} </ Provider > </ body > </ html > ); }
پس از ارائه ارائه دهنده جلسه در اطراف پروژه ، این بدان معنی است که می توانید از جلسه در سمت مشتری برای اجرای احراز هویت و مجوز استفاده کنید.
در پرونده app/page.tsx
خود ، باید از عملکرد signIn
next-auth/react
useSession
signOut
این امر به کاربران امکان می دهد تا بتوانند وارد سیستم شوند ، از سیستم خارج شوند و اطلاعات پروفایل خود را مشاهده کنند.
import {signIn, signOut, useSession} from 'next-auth/react' ;
در مرحله بعد ، در مؤلفه Home
، این کد را به آن اضافه کنید:
const { data : session, status} = useSession(); console .log( "status" , status); console .log( "session" , session);
کد فوق جلسه و وضعیت (تأیید شده یا غیر معتبر) کاربر را از قلاب useSession
دریافت می کند. سپس وضعیت و جلسه در کنسول مرورگر نمایش داده می شود تا رفتار مشاهده شود. هنگام استقرار تغییرات می توانید Console.log را حذف کنید.
بعد ، کد را در عنصر هدر (از خطوط 92 تا 94) جایگزین کنید:
<header className= "flex flex-row fixed w-[100%] top-0 left-0 p-[10px] px-[20px] text-white text-center bg-[#242424]" > < a className = 'text-[15px]' > WriterAI </ a > < div className = "ml-auto flex flex-row gap-[10px]" > { session ? < a > {session.user.name} </ a > : < a onClick = {() => { signIn('github') }} className="cursor-pointer">Sign in </ a > } { session && < a onClick = {() => { signOut() }} className="cursor-pointer">Sign out </ a > } </ div > </header>
از کد فوق ، هنگامی که کاربر روی لینک ورود به سیستم کلیک می کند ، آنها به GitHub هدایت می شوند تا اجازه انتقال اطلاعات را بدهند. و هنگامی که پیوند ورود به سیستم کلیک می شود ، جلسه از بین می رود.
همچنین ، اگر صفحه در حالی که جلسه هنوز فعال است بارگیری شود ، اطلاعات کاربر و یک لینک ثبت نام نمایش داده می شود. اما اگر جلسه ای وجود نداشته باشد ، فقط یک لینک ورود به سیستم نمایش داده می شود.
همچنین ، کد را در کانتینر فرم (خطوط 129 تا 135) جایگزین کنید تا فرم از کاربران غیرمجاز پنهان شود و فقط کسانی که احراز هویت می کنند می توانند ارسال ها را به سرور ارسال کنند.
<div className= 'fixed w-[100%] p-[10px] bottom-0 bg-[#242424]' > { /*when the form is submitted, activate a submit event that sends the value of the input and the event to the handleChat function */ } {session ? < form action = '' onSubmit = {(e) => handleChat(input, e)}> < input className = " lg:w-[50%] w-[70%] ml-[5%] lg:ml-[20%] p-[10px] outline-none bg-[#4d4d4dff] text-[15px] text-[#f2f2f2ff]" type = 'text' value = {input} placeholder = 'Ask your questions' onChange = { ( e ) => setInput(e.target.value)}/> < button className = 'py-[10px] px-[20px] bg-black text-[15px] text-[#f2f2f2ff]' > Ask </ button > </ form > : < a className = "block text-white text-[20px] text-center cursor-pointer" onClick = {() => { signIn('github') }}>Sign in to send messages </ a > } </div>
حال اگر تغییرات را ذخیره کرده و سایت خود را بارگیری مجدد کنید ، این باید به نظر برسد که سایت شما به نظر می رسد:
که چگونه پروژه باید به دنبال کاربران غیرمجاز باشد" class="image--center mx-auto" width="1919" height="1014" loading="lazy">
هنگامی که روی پیوند ورود به سیستم کلیک می کنید ، برای مجوز به GitHub هدایت می شوید و پس از آن به سایت هدایت می شوید. پس از موفقیت مجوز ، اطلاعات کاربر در سایت قابل مشاهده خواهد بود.
اینجا مال من است:
که چگونه پروژه باید به دنبال کاربران معتبر باشد" class="image--center mx-auto" width="1064" height="867" loading="lazy">
اگر کنسول مرورگر را تحلیل کنید ، اطلاعات جلسه شامل نام ، ایمیل و زمان/تاریخ انقضا را مشاهده می کنید. به یاد داشته باشید که ما جلسه را پیکربندی کردیم تا 2 دقیقه پس از آخرین درخواست ارسال شده به سرور منقضی شود. اگر در مدت 2 دقیقه درخواستی را به سرور ارسال نکنید ، جلسه از بین می رود و از شما خواسته می شود دوباره وارد شوید.
به یاد داشته باشید که این تنظیماتی است که من در پرونده helper/authOption.ts
خود اضافه کردم. می توانید جلسه را پیکربندی کنید تا روزها ، هفته ها یا ماهها فعال باشد.
هنگام ارسال سریع و دریافت پیام های خطا ، نیازی به نگرانی نیست. این امر به این دلیل است که شما برخی از متغیرهای محیط را در Vercel دارید که به پرونده .env.local
اضافه نشده است. هنگامی که در نهایت تغییرات را به روز می کنید ، می توانید طبق معمول درخواست های خود را به سایت مستقر ارسال کنید.
هنگامی که شما روی پیوند ورود به سیستم نیز کلیک می کنید ، جلسه از بین می رود ، برنامه بارگیری مجدد می شود و فرم پنهان می شود.
اگر همانطور که من فقط توضیح دادم کار کند ، این بدان معنی است که همه چیز خوب پیش رفت.
همچنین ، شما باید مجوز را در مسیر API/AI اضافه کنید تا کاربران غیرمجاز نتوانند درخواست ها را مستقیماً به نقطه پایانی ارسال کنند.
در پرونده api/ai/route.ts
خود ، باید getServerSession
و authOptions
را وارد کنید.
import {authOptions} from "@/app/helper/authOption" ; import { getServerSession} from "next-auth" ;
در مرحله بعد ، در عملکرد پست ، این کد را به آن اضافه کنید:
let session = await getServerSession(authOptions); if (!session) { return NextResponse.json({ content : 'Unauthorized access. Authentication required' }, { status : 401 }) }
کد فوق همچنین با ارسال کد وضعیت 401 و یک پیام خطا ، از دریافت پاسخ از مسیر api/ai
جلوگیری می کند.
پس از اتمام این کار ، وقت آن است که تغییرات را به repo github فشار دهیم.
اما قبل از آن ، شما باید متغیرهای محیط ( GITHUB_CLIENT_ID
، GITHUB_SECRET
، NEXTAUTH_SECRET
، NEXTAUTH_URL
) را در پرونده .env.local
خود کپی کرده و در بخش متغیرهای محیط تنظیمات پروژه خود در vercel قرار دهید.
پس از آن ، کد زیر را اجرا کنید تا تغییرات به GitHub را فشار دهید:
git add . git commit –m "added GitHub authentication and authorization" git push –u origin main
پس از موفقیت به روزرسانی ، به صفحه استقرار پروژه خود در Vercel مراجعه کنید تا تأیید کنید که تغییرات GitHub با موفقیت مستقر شده است.
اما متأسفانه ، خطایی که ما متوجه نشدیم ، باعث می شود استقرار از بین برود.
این پیام خطا است:
که خطای استقرار را نشان می دهد که در Vercel دیده می شود" class="image--center mx-auto" width="1483" height="784" loading="lazy">
از آنجایی که من هم نمی دانستم چه اشتباهی رخ داده است ، از Chatgpt درخواست کمک کردم. و من توانستم مسئله را پیدا کنم.
استقرار ناکام ماند زیرا ما آن session.user
را تضمین نکردیم. کاربر قبل از دسترسی به آن تعریف شده است
که پیام های chatgpt را نشان می دهد" class="image--center mx-auto" width="950" height="627" loading="lazy">
پس در پرونده page.tsx
خود ، حتماً کد را مطابق شکل زیر اصلاح کنید:
{ session && session.user ? < a > {session.user.name} </ a > : < a onClick = {() => { signIn('github') }} className="cursor-pointer">Sign in </ a > }
پس از آن ، تغییرات را به Github فشار داده و منتظر بمانید تا این پروژه با موفقیت در Vercel مستقر شود.
اکنون می توانید برای شروع آزمایش احراز هویت به لینک مستقر مراجعه کنید.
اما وقتی روی پیوند ورود به سیستم کلیک می کنید ، صفحه بارگیری نمی شود. این امر به این دلیل است که شما URL را در متغیر محیط و GitHub خود از http://localhost:3000
به دامنه پروژه مستقر خود تغییر نداده اید.
پس باید اطلاعات استقرار پروژه خود را در Vercel تحلیل کرده و نام دامنه را کپی کنید.
در مرحله بعد ، به تنظیم متغیرهای محیط خود بروید و NEXTAUTH_URL
به نام دامنه پروژه به روز کنید. معدن https://nextjs-ai-pro.vercel.app
است.
در مرحله بعد ، به برنامه OAuth که ایجاد کرده اید بروید ، نام دامنه را در URL صفحه اصلی و URL پاسخ به تماس از http://localhost:3000
/به نام دامنه Vercel جایگزین کنید.
به یاد داشته باشید که اگر نام دامنه خود را دارید (به عنوان مثال ، Domain.com) ، به جای نام دامنه Vercel از آن استفاده می شود.
اگر سایت را بارگیری کنید و روی پیوند ورود به سیستم کلیک کنید ، به GitHub هدایت می شوید و جلسه شما ایجاد می شود. وقتی از سیستم خارج شوید ، جلسه نیز نابود خواهد شد.
وقتی سعی می کنید اسکریپت زیر را در کنسول مرورگر خود اجرا کنید ، به دلیل ورود به سیستم ، پیام خطا دریافت خواهید کرد.
//function to send the request const getCompletion = async (prompt) => { try { const response = await fetch( '/api/ai' , { method : 'POST' , body : JSON .stringify({ prompt }), headers : { 'Content-Type' : 'application/json' , }, }); const result = await response.json(); if (!response.ok) { return { role : 'assistant' , content : result.content }; } return result; } catch (error) { return { role : 'assistant' , content : 'Something went wrong' }; } }; getCompletion().then( ( result ) => { console .log(result) })
این بدان معنی است که کاربران غیرمجاز قادر به اجرای اسکریپت در ابزارهای توسعه دهنده شما نیستند تا یک پاسخ هوش مصنوعی دریافت کنند.
همچنین می توانید سایت را آزمایش کرده و برخی از اعلان ها را ارسال کنید.
اگر همه اینها مطابق آنچه توضیح داده شده است ، این بدان معنی است که احراز هویت و مجوز با موفقیت اجرا شده است. تبریک می گویم!
می توانید فیلم را برای این بخش در زیر مشاهده کنید:
بهینه سازی کد
اکنون بیشتر کارها انجام شده است. اما می توانید کارهای دیگری را برای بهینه سازی بیشتر کد خود انجام دهید. این کاملاً به این بستگی دارد که شما چگونه می خواهید.
به عنوان مثال ، شما می توانید کد را مجدداً اصلاح کنید ، یک ظاهر طراحی شده را بهبود بخشید ، مدت زمان عملکرد را برای سریعتر افزایش دهید ، سیاههها را بهینه کنید ، خطاهای خود را در میان افزار ، تنظیم واحد و تست ادغام ، تنظیم خط لوله CI/CD ، به روزرسانی ابرداده پروژه خود را به روز کنید. (عنوان ، توضیحات ، آرم) ، برای استقرار خود یک محیط پیش نمایش ایجاد کنید ، علائم ناموفق را به صفحه فرود تغییر دهید و ویژگی های بسیاری را به برنامه خود اضافه کنید.
نتیجه گیری
در این آموزش ، ما موارد زیادی را مورد تحلیل قرار داده ایم. شما یاد گرفتید که چگونه برنامه Next.js خود را با API های شخص ثالث ادغام کنید. شما همچنین یاد گرفتید که چگونه با اجرای استراتژی هایی مانند محدود کردن نرخ ، تنظیم فایروال ها و اجرای احراز هویت و مجوز در برنامه هوش مصنوعی خود که در Vercel مستقر شده است ، برنامه خود را از رایج ترین حملات سایبری تأمین کنید.
با استفاده از همان منطق در برنامه بعدی خود. JS AI ، من اطمینان دارم که شما می توانید بدون ترس از بیدار شدن از یک صورتحساب نجومی یا خرابی سرور ، با خیال راحت برنامه خود را مستقر کنید.
برای اینکه در کنار امن باشید ، حتماً روزانه استقرار Vercel خود را کنترل می کنید. همچنین می توانید اگر در مهارت های خود در این زمینه اطمینان ندارید ، یک مهندس نرم افزار حرفه ای را برای تأیید امنیت برنامه خود استخدام کنید.
ارسال نظر