pemrograman

16 Membuat Middleware Hmac Authentication Pada Golang

Pada artikel kali ini, kita akan membahas bagaimana membangun middleware HMAC (Hash-based Message Authentication Code) Authentication menggunakan Go dan library httprouter. Middleware ini berfungsi untuk mengautentikasi permintaan HTTP berdasarkan tanda tangan HMAC, yang digunakan untuk memastikan integritas data yang dikirim dan bahwa data tersebut berasal dari sumber yang sah.

Apa Itu HMAC?

HMAC adalah sebuah metode untuk memberikan autentikasi dan integritas pada pesan atau data yang dikirim melalui jaringan. Dalam konteks API, HMAC digunakan untuk memverifikasi bahwa data yang diterima oleh server belum dimodifikasi dalam perjalanan dan bahwa data tersebut benar-benar dikirim oleh pengirim yang sah.

HMAC bekerja dengan menggunakan algoritma hash (misalnya SHA-256) yang digabungkan dengan sebuah kunci rahasia. Proses ini menghasilkan sebuah tanda tangan yang dapat diverifikasi oleh penerima. Jika tanda tangan cocok, maka data dianggap valid.

Persiapan Proyek

Untuk memulai, pastikan Anda sudah memiliki lingkungan pengembangan Go yang siap. Anda juga perlu menginstal httprouter dengan perintah:

go get github.com/julienschmidt/httprouter

Setelah itu, buat folder proyek baru dan buat file main.go di dalam folder tersebut.

Langkah 1: Menyiapkan Middleware HMAC

Middleware bertanggung jawab untuk memeriksa setiap permintaan yang datang, memverifikasi tanda tangan HMAC, dan memastikan bahwa permintaan tersebut berasal dari sumber yang sah. Berikut adalah kode dasar untuk middleware HMAC:

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/julienschmidt/httprouter"
)

// Secret key untuk verifikasi HMAC
var secretKey = []byte("your-secret-key")

// Middleware HMAC
func HMACMiddleware(next httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		// Ambil tanda tangan dari header "X-Signature"
		signature := r.Header.Get("X-Signature")
		if signature == "" {
			http.Error(w, "Missing signature", http.StatusUnauthorized)
			return
		}

		// Ambil body request untuk membuat tanda tangan
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Unable to read body", http.StatusInternalServerError)
			return
		}

		// Buat HMAC dari body menggunakan kunci rahasia
		mac := hmac.New(sha256.New, secretKey)
		mac.Write(body)
		expectedSignature := hex.EncodeToString(mac.Sum(nil))

		// Verifikasi apakah tanda tangan sesuai
		if !hmac.Equal([]byte(signature), []byte(expectedSignature)) {
			http.Error(w, "Invalid signature", http.StatusUnauthorized)
			return
		}

		// Lanjutkan ke handler berikutnya
		next(w, r, ps)
	}
}

Penjelasan kode:

  1. Middleware HMACMiddleware memeriksa header X-Signature yang berisi tanda tangan HMAC dari pengirim.
  2. Kemudian, body dari permintaan dibaca untuk membangun HMAC dengan menggunakan kunci rahasia.
  3. HMAC yang dibangun dibandingkan dengan tanda tangan yang diterima di header untuk memverifikasi integritas dan autentikasi data.

Langkah 2: Menyiapkan Router dan Handler

Selanjutnya, kita perlu menyiapkan router httprouter dan handler untuk menangani permintaan yang berhasil diverifikasi tanda tangannya:

// Handler untuk halaman private yang membutuhkan otentikasi hmac
func ProtectedHMACHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	w.Write([]byte("Welcome to the session private page"))
}

func main() {
	router := httprouter.New()

	// sample for initiate hmac
	mac := hmac.New(sha256.New, middleware.SecretKey)
	mac.Write([]byte(`Hello World`))
	validSignature := hex.EncodeToString(mac.Sum(nil))
	fmt.Println("generate hmac sample: " + validSignature)

	// Menggunakan middleware HMAC untuk endpoint ini
	router.GET("/protected-hmac", middleware.HMAC(ProtectedHMACHandler))

	// Menjalankan server
	http.ListenAndServe(":8080", router)
}

Dalam contoh ini, kita menambahkan route /hello yang dilindungi oleh middleware HMAC. Hanya permintaan yang memiliki tanda tangan yang valid yang akan dapat mengaksesnya.

Langkah 3: Pengujian HMAC dengan Unit Test

Untuk memastikan bahwa middleware HMAC berfungsi dengan baik, kita akan menulis unit test. Kita akan menguji dua kasus:

  1. Permintaan yang sah dengan tanda tangan yang valid.
  2. Permintaan dengan tanda tangan yang tidak valid.

Berikut adalah kode untuk unit test:

package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/julienschmidt/httprouter"
	"github.com/stretchr/testify/assert"
)

func makeRequest(signature string, body string) *http.Request {
	req := httptest.NewRequest(http.MethodGet, "/hello", strings.NewReader(body))
	req.Header.Set("X-Signature", signature)
	return req
}

func generateSignature(body string) string {
	mac := hmac.New(sha256.New, SecretKey)
	mac.Write([]byte(body))
	return hex.EncodeToString(mac.Sum(nil))
}

func TestHMACMiddleware(t *testing.T) {
	router := httprouter.New()
	router.GET("/hello", HMAC(mockHMACHandler))

	type args struct {
		signature string
		body      string
	}
	tests := []struct {
		name             string
		args             args
		wantStatusCode   int
		wantBodyContains string
	}{
		{
			name: "valid HMAC signature",
			args: args{
				signature: generateSignature("Hello World"),
				body:      "Hello World",
			},
			wantStatusCode:   http.StatusOK,
			wantBodyContains: "Welcome to the session private page",
		},
		{
			name: "invalid HMAC signature",
			args: args{
				signature: "invalid-signature",
				body:      "Hello World",
			},
			wantStatusCode:   http.StatusUnauthorized,
			wantBodyContains: "Invalid signature",
		},
		{
			name: "signature is empty",
			args: args{
				signature: "",
				body:      "Hello World",
			},
			wantStatusCode:   http.StatusUnauthorized,
			wantBodyContains: "Missing signature",
		},
		{
			name: "body was changed (error mismatch signature)",
			args: args{
				signature: generateSignature("Original Body"),
				body:      "Tampered Body",
			},
			wantStatusCode:   http.StatusUnauthorized,
			wantBodyContains: "Invalid signature",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := makeRequest(tt.args.signature, tt.args.body)
			rr := httptest.NewRecorder()

			router.ServeHTTP(rr, req)

			if rr.Code != tt.wantStatusCode {
				t.Errorf("handler returned wrong status code: got %v, want %v", rr.Code, tt.wantStatusCode)
			}
			if !strings.Contains(rr.Body.String(), tt.wantBodyContains) {
				t.Errorf("handler returned unexpected body: got %v, want to contain %v", rr.Body.String(), tt.wantBodyContains)
			}
		})
	}
}

func mockHMACHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	w.Write([]byte("Welcome to the session private page"))
}

Penjelasan unit test:

  1. Test case 1: Kami membuat permintaan dengan tanda tangan yang valid dan memverifikasi bahwa responsnya adalah status 200 OK.
  2. Test case 2: Kami membuat permintaan dengan tanda tangan yang tidak valid dan memverifikasi bahwa server merespons dengan status 401 Unauthorized.

Untuk menjalankan unit test, gunakan perintah:

go test -v

Kesimpulan

Pada artikel ini, kita telah mempelajari bagaimana cara membangun middleware HMAC Authentication menggunakan Go dan httprouter. Middleware ini berguna untuk memastikan integritas dan autentikasi data yang dikirim melalui HTTP. Kita juga telah melihat bagaimana menulis unit test untuk memastikan middleware bekerja dengan baik.

Untuk lebih lanjut tentang Golang, Anda dapat mengunjungi Tutorial Golang. Jika Anda tertarik untuk mempelajari lebih lanjut tentang HTTP router di Go, silakan baca artikel mengenai Web HTTP Router pada Golang.

Dengan memahami konsep HMAC dan penerapannya dalam middleware, Anda bisa membangun sistem yang lebih aman dan dapat diandalkan. Semoga artikel ini bermanfaat dan mudah dipahami!

comments powered by Disqus