Membuat Middleware ExpressJS Sederhana Untuk Mendeteksi Bot - CRUDPRO

Membuat Middleware ExpressJS Sederhana Untuk Mendeteksi Bot

Pembahasan ini menunjukkan cara membuat middleware ExpressJS sederhana untuk mendeteksi dan memblokir bot jahat. Tujuan dari posting blog ini bukan untuk masuk ke detail deteksi bot, tetapi untuk menjelaskan cara menggunakan middleware untuk mengintegrasikan mekanisme deteksi bot sederhana ke dalam aplikasi ExpressJS Anda.

Apa itu middleware?

Jika Anda terbiasa dengan Express, Anda mungkin telah menentukan route untuk menangani request Anda. Untuk menentukan route, gunakan sintaks berikut:

app.METHOD(PATH, HANDLER)

Handler adalah fungsi yang menerima dua parameter, objek request dan objek respons.

app.get('/', function (req, res) {
  res.send('Hello World!')
})

Secara default, objek request berisi informasi tentang request yang masuk, seperti header HTTP, Tetapi bagaimana jika Anda ingin menerapkan fungsi untuk setiap request yang masuk? Periksa apakah pengguna masuk atau ubah setiap respons. Bagaimana cara menambahkan custome header khusus? Sangat membosankan untuk mengubah semua route untuk menerapkan fungsi yang sama. Di situlah middleware masuk.

Middleware memungkinkan Anda untuk menentukan fungsi yang dapat memodifikasi objek request dan respons untuk semua request atau subset request yang cocok dengan pola tertentu.

Sangat mudah untuk mendefinisikan middleware di Express. Middleware ekspres adalah fungsi yang menerima tiga parameter berikut sebagai input.

  • request objek.
  • Objek respons.
  • Fungsi berikut.
function myMiddleware(req, res, next) {
    // do whatever you want
    // e.g. modify the request object to enrich it
    req.mynewfield = 'added by myMiddleware';

    // or modify the response to add a custom header
    res.setHeader("X-New-Header", "addedByMyMiddleware");

    // Finally, don't forget to call the next function to pass
    // the request to the next middleware or the route callback
    next();
}

Seperti halnya mendeklarasikan route, objek request berisi informasi tentang request yang masuk dan objek respons berisi informasi tentang respons yang dikirim oleh server. Parameter berikut adalah fungsi yang bertanggung jawab untuk memanggil middleware berikutnya. Jika tidak ada middleware, ini adalah route yang bertanggung jawab untuk memproses request.

Dengan menggunakan pendekatan ini, Anda dapat dengan mudah menghubungkan beberapa middleware. Misalnya, Anda memiliki middleware pertama yang memeriksa apakah pengguna bukan bot, dan middleware kedua yang memeriksa apakah pengguna masuk. Oleh karena itu, ketika request tiba di pengendali route, Anda akan tahu apakah request itu berasal. Dari pengguna manusia atau bot, dan jika pengguna ini masuk.

Untuk menggunakan middleware di aplikasi Express Anda, cukup beri tahu aplikasi Anda untuk menggunakan middleware.

app.use(myMiddleware);

Membuat proyek ExpressJS

Sekarang setelah Anda memahami dasar-dasar middleware, mari buat proyek ExpressJS sederhana yang digunakan untuk menjelaskan cara membuat middleware deteksi bot.

Mulailah dengan membuat direktori untuk meng-host proyek Express Anda.

mkdir bot_middleware_app
cd bot_middleware_app

Selanjutnya, inisialisasi proyek Node dan instal Express.

npm init -y
npm install express

Terakhir, buat aplikasi Express sederhana dalam file bernama app.js.

// app.js

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Server listening at http://localhost:${port}`))

Untuk meluncurkan aplikasi Express, jalankan node app.js dari terminal. Server Express dibuat yang mendengarkan pada port 3000. Untuk memverifikasi bahwa server berfungsi dengan baik, buka http://localhost:3000/ dari browser Anda. Halaman yang menampilkan "Hello World!" Ditampilkan.

Membuat middleware pendeteksi bot

Mulailah dengan membuat direktori baru yang menampung kode middleware Anda.

mkdir middleware

Buat file JavaScript di direktori ini yang berisi kode untuk middleware deteksi bot Anda. Untuk saat ini, mari kita buat beberapa middleware yang sangat mendasar untuk memastikan semuanya berfungsi dengan baik sebelum kita melanjutkan.

// middleware/isBot.js
module.exports = function isBot(req, res, next) {
    console.log('isBot middleware called!');
    // transmits the requests to the next middleware
    next();
}

Ubah app.js untuk menginstruksikan Express agar menggunakan middleware deteksi bot.

// app.js
const express = require('express')

// Import the bot detection middleware
const isBot = require('./middleware/isBot')

const app = express()
const port = 3000

// Tell express app to use our bot detection middleware
app.use(isBot);

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Server listening at http://localhost:${port}`))

Untuk memeriksa apakah aplikasi Express Anda menggunakan middleware, mulai ulang aplikasi Anda dan buka halaman beranda. Log aplikasi Express seharusnya mengatakan "isBot middleware telah dipanggil!". Jika tidak, pastikan aplikasi Anda menggunakan middleware (menggunakan pernyataan app.use) atau memulai ulang aplikasi Express Anda.

Tambahkan logika deteksi bot sederhana ke middleware Anda

Sekarang setelah kami memverifikasi bahwa midleware telah dipanggil, kami akan memodifikasi midleware untuk menambahkan logika deteksi bot sederhana berdasarkan agen pengguna. Ini bukan metode deteksi yang kuat karena bot sering hadir di bidang ini untuk melewati deteksi, seperti yang sudah dijelaskan di posting lain (Cara mendeteksi bot web, Cara mendeteksi chrome headless). Namun, ini adalah contoh yang bagus dan sederhana untuk menampilkan middleware deteksi bot.

// middleware/isBot.js

// Map bot name to regular expression used to detect them based
// on their user-agent
const knownBotsToPattern = new Map([
    ['Headless Chrome', /HeadlessChrome/],
    ['Wget', /[wW]get/],
    ['Python urllib', /Python\-urllib/],
    ['PHP crawl', /phpcrawl/],
    ['PhantomJS', /PhantomJS/]
]);

// Detect if an incoming request belongs to a bot using its user agent
function isKnownBotUserAgent(userAgent) {
    for (const [knownBot, pattern] of knownBotsToPattern.entries()) {
        if (userAgent.match(pattern)) {
            return {
                isBot: true,
                // In case the request comes from a bot,
                // we also returns the name of the bot
                nameBot: knownBot
            }
        }
    }

    return {
        isBot: false
    }
}

module.exports = function isBot(req, res, next) {
    // We enrich the incoming request object (req)
    // with information regarding bot detection
    req.botInfo = isKnownBotUserAgent(req.header('User-Agent'));
    next();
}

Untuk saat ini, middleware hanya meningkatkan objek permintaan dan tidak membuat keputusan apa pun seperti memblokir.

Ubah satu-satunya route (“/”) untuk menunjukkan bahwa Anda dapat mengakses informasi yang dihitung oleh middleware.

// app.js

// ...

// We modify the / route to log information regarding bot detection
app.get('/', (req, res) => {
    console.log('In /route')
    console.log(req.botInfo);
    
    res.send('Hello World!')
})

Mulai ulang aplikasi Express dan buka halaman beranda. Anda dapat melihat di log bahwa request tidak datang dari bot.

In /route
{ isBot: false }

Untuk mengizinkan middleware mendeteksi bot juga, kami menggunakan Curl untuk mengirim request ke agen pengguna yang berpura-pura dari Chrome headless. Opsi -H memungkinkan Anda untuk menentukan header HTTP (di sini agen pengguna).

curl 'http://localhost:3000/' \
  -H 'User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36'

Log aplikasi Express menunjukkan bahwa request terdeteksi dengan benar sebagai berasal dari bot Chrome headless.

In /route
{ isBot: true, nameBot: 'Headless Chrome' }

Anda mungkin berpendapat bahwa Anda tidak memerlukan middleware untuk mencapai hasil ini. Anda dapat menggunakan fungsi isKnownBotUserAgent secara langsung di fungsi panggilan balik / root. Namun, middleware dapat membantu Anda mengisolasi logika deteksi bot dengan lebih baik dari sisa kode Anda. Selain itu, middleware kami menyediakan mekanisme yang mudah untuk akses mudah ke informasi bot di semua route.

Buat route/login baru untuk menunjukkan bahwa informasi deteksi bot tersedia untuk semua route.

app.get('/login', (req, res) => {
    console.log('In /login')
    console.log(req.botInfo);

    res.send("On login page!")
})

Untuk menguji route baru ini, buka http://localhost:3000/login.

Setelah kunjungan, Anda dapat melihat bahwa request ke / login juga dianalisis oleh middleware deteksi bot.

In /login
{ isBot: false }

Informasi deteksi bot tersedia untuk semua route, sehingga Anda dapat mengubah pengendali route tergantung pada halaman yang Anda sediakan. Saya tidak peduli dengan bot di halaman beranda, tetapi saya ingin mengecualikannya dari halaman login. Kemudian ubah route login untuk memblokir request jika itu berasal dari bot.

app.get('/login', (req, res) => {
    console.log('In /login')
    console.log(req.botInfo);

    if (req.botInfo.isBot) {
        res.status(403);
        res.send("You are a bot, you can't access the login page!");
    } else {
        res.status(200);
        res.send("You seem human, you can access the login page!");
    }  
})

Saat Anda mengakses halaman / login dari browser Anda, request Anda dikabulkan. Pesan berikut ditampilkan di halaman ini: Ini seperti manusia. Anda dapat mengakses halaman login. Namun, jika saya menggunakan Curl untuk berpura-pura menjadi Chrome headless dan membuat request, saya mendapatkan kesalahan 403 dan konten di halaman konten berbeda.

curl 'http://localhost:3000/login' \
  -H 'User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36'

# It returns the following page:
# You are a bot, you can't access the login page!

Ingatlah bahwa dalam aplikasi nyata, hal terpenting untuk melindungi titik akhir login adalah route POST, bukan route GET. Faktanya, route GET hanya bertanggung jawab untuk menampilkan halaman login. Namun, request login yang berisi nama pengguna dan kata sandi dikirim sebagai request POST (jangan kirimkan sebagai request GET karena parameter login ditampilkan di URL dan tidak aman). Namun, bot tidak perlu mengakses halaman login (melalui route GET) untuk mencoba login. Cukup gunakan request POST untuk memalsukan upaya login. Contoh berikut menunjukkan cara membuat request POST yang berisi dua parameter.

Nama pengguna sama dengan "john"

Kata sandi sama dengan "kata sandi terbaik"

curl -d "username=john&param2=bestpassword" \
-X POST http://localhost:3000/login \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36'

Gagal saat ini karena tidak ada route yang terkait dengan aplikasi Express. Untuk mengujinya, buat route baru di / login untuk menangani request POST.

app.post('/login', (req, res) => {
    console.log('In /login (POST)')
    res.send("Login attempt successful");
})

Jalankan perintah curl dan Anda akan melihat bahwa bot berhasil mencoba masuk. Untuk mencegah bot memalsukan request login, ubah route POST / login untuk memblokir bot.

Setelah perubahan ini, jika Anda menggunakan Agen Pengguna Chrome Headless untuk membuat request POST, request POST akan diblokir.

curl -d "username=john&param2=bestpassword" \
-X POST http://localhost:3000/login \
-H 'User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36'

# it returns the following content
# You are a bot, stop trying to login!

Dalam posting blog ini, saya menunjukkan cara membuat middleware Express sederhana untuk mendeteksi dan memblokir bot. Logika penemuan sederhana (hanya bergantung pada agen pengguna), tetapi dapat diperluas untuk memanfaatkan fitur lain seperti ada atau tidaknya header HTTP lainnya. Demikian pula, Anda dapat menerapkan pembatasan tarif. Artinya, gunakan middleware untuk membatasi jumlah request per IP atau sesi.